github.com/rigado/snapd@v2.42.5-go-mod+incompatible/cmd/snapd/main.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2015 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package main 21 22 import ( 23 "fmt" 24 "os" 25 "os/signal" 26 "syscall" 27 "time" 28 29 "github.com/snapcore/snapd/cmd" 30 "github.com/snapcore/snapd/daemon" 31 "github.com/snapcore/snapd/errtracker" 32 "github.com/snapcore/snapd/httputil" 33 "github.com/snapcore/snapd/logger" 34 "github.com/snapcore/snapd/osutil" 35 "github.com/snapcore/snapd/sanity" 36 "github.com/snapcore/snapd/systemd" 37 ) 38 39 var ( 40 sanityCheck = sanity.Check 41 ) 42 43 func init() { 44 err := logger.SimpleSetup() 45 if err != nil { 46 fmt.Fprintf(os.Stderr, "WARNING: failed to activate logging: %s\n", err) 47 } 48 // set here to avoid accidental submits in e.g. unit tests 49 errtracker.CrashDbURLBase = "https://daisy.ubuntu.com/" 50 errtracker.SnapdVersion = cmd.Version 51 } 52 53 func main() { 54 cmd.ExecInSnapdOrCoreSnap() 55 56 ch := make(chan os.Signal, 2) 57 signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM) 58 if err := run(ch); err != nil { 59 if err == daemon.ErrRestartSocket { 60 // Note that we don't prepend: "error: " here because 61 // ErrRestartSocket is not an error as such. 62 fmt.Fprintf(os.Stdout, "%v\n", err) 63 // the exit code must be in sync with 64 // data/systemd/snapd.service.in:SuccessExitStatus= 65 os.Exit(42) 66 } 67 fmt.Fprintf(os.Stderr, "cannot run daemon: %v\n", err) 68 os.Exit(1) 69 } 70 } 71 72 func runWatchdog(d *daemon.Daemon) (*time.Ticker, error) { 73 // not running under systemd 74 if os.Getenv("WATCHDOG_USEC") == "" { 75 return nil, nil 76 } 77 usec := osutil.GetenvInt64("WATCHDOG_USEC") 78 if usec == 0 { 79 return nil, fmt.Errorf("cannot parse WATCHDOG_USEC: %q", os.Getenv("WATCHDOG_USEC")) 80 } 81 dur := time.Duration(usec/2) * time.Microsecond 82 logger.Debugf("Setting up sd_notify() watchdog timer every %s", dur) 83 wt := time.NewTicker(dur) 84 85 go func() { 86 for { 87 select { 88 case <-wt.C: 89 // TODO: poke the snapd API here and 90 // only report WATCHDOG=1 if it 91 // replies with valid data 92 systemd.SdNotify("WATCHDOG=1") 93 case <-d.Dying(): 94 return 95 } 96 } 97 }() 98 99 return wt, nil 100 } 101 102 var checkRunningConditionsRetryDelay = 300 * time.Second 103 104 func run(ch chan os.Signal) error { 105 t0 := time.Now().Truncate(time.Millisecond) 106 httputil.SetUserAgentFromVersion(cmd.Version) 107 108 d, err := daemon.New() 109 if err != nil { 110 return err 111 } 112 if err := d.Init(); err != nil { 113 return err 114 } 115 116 // Run sanity check now, if anything goes wrong with the 117 // check we go into "degraded" mode where we always report 118 // the given error to any snap client. 119 var checkTicker <-chan time.Time 120 var tic *time.Ticker 121 if err := sanityCheck(); err != nil { 122 degradedErr := fmt.Errorf("system does not fully support snapd: %s", err) 123 logger.Noticef("%s", degradedErr) 124 d.SetDegradedMode(degradedErr) 125 tic = time.NewTicker(checkRunningConditionsRetryDelay) 126 checkTicker = tic.C 127 } 128 129 d.Version = cmd.Version 130 131 if err := d.Start(); err != nil { 132 return err 133 } 134 135 watchdog, err := runWatchdog(d) 136 if err != nil { 137 return fmt.Errorf("cannot run software watchdog: %v", err) 138 } 139 if watchdog != nil { 140 defer watchdog.Stop() 141 } 142 143 logger.Debugf("activation done in %v", time.Now().Truncate(time.Millisecond).Sub(t0)) 144 145 out: 146 for { 147 select { 148 case sig := <-ch: 149 logger.Noticef("Exiting on %s signal.\n", sig) 150 break out 151 case <-d.Dying(): 152 // something called Stop() 153 break out 154 case <-checkTicker: 155 if err := sanityCheck(); err == nil { 156 d.SetDegradedMode(nil) 157 tic.Stop() 158 } 159 } 160 } 161 162 return d.Stop(ch) 163 }