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  }