github.com/tompreston/snapd@v0.0.0-20210817193607-954edfcb9611/cmd/snapd/main.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2015-2020 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/daemon"
    30  	"github.com/snapcore/snapd/errtracker"
    31  	"github.com/snapcore/snapd/logger"
    32  	"github.com/snapcore/snapd/osutil"
    33  	"github.com/snapcore/snapd/sandbox"
    34  	"github.com/snapcore/snapd/sanity"
    35  	"github.com/snapcore/snapd/snapdenv"
    36  	"github.com/snapcore/snapd/snapdtool"
    37  	"github.com/snapcore/snapd/systemd"
    38  )
    39  
    40  var (
    41  	sanityCheck = sanity.Check
    42  )
    43  
    44  func init() {
    45  	err := logger.SimpleSetup()
    46  	if err != nil {
    47  		fmt.Fprintf(os.Stderr, "WARNING: failed to activate logging: %s\n", err)
    48  	}
    49  	// set here to avoid accidental submits in e.g. unit tests
    50  	errtracker.CrashDbURLBase = "https://daisy.ubuntu.com/"
    51  	errtracker.SnapdVersion = snapdtool.Version
    52  }
    53  
    54  func main() {
    55  	// When preseeding re-exec is not used
    56  	if snapdenv.Preseeding() {
    57  		logger.Noticef("running for preseeding")
    58  	} else {
    59  		snapdtool.ExecInSnapdOrCoreSnap()
    60  	}
    61  
    62  	ch := make(chan os.Signal, 2)
    63  	signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
    64  	if err := run(ch); err != nil {
    65  		if err == daemon.ErrRestartSocket {
    66  			// Note that we don't prepend: "error: " here because
    67  			// ErrRestartSocket is not an error as such.
    68  			fmt.Fprintf(os.Stdout, "%v\n", err)
    69  			// the exit code must be in sync with
    70  			// data/systemd/snapd.service.in:SuccessExitStatus=
    71  			os.Exit(42)
    72  		}
    73  		fmt.Fprintf(os.Stderr, "cannot run daemon: %v\n", err)
    74  		os.Exit(1)
    75  	}
    76  }
    77  
    78  func runWatchdog(d *daemon.Daemon) (*time.Ticker, error) {
    79  	// not running under systemd
    80  	if os.Getenv("WATCHDOG_USEC") == "" {
    81  		return nil, nil
    82  	}
    83  	usec := osutil.GetenvInt64("WATCHDOG_USEC")
    84  	if usec == 0 {
    85  		return nil, fmt.Errorf("cannot parse WATCHDOG_USEC: %q", os.Getenv("WATCHDOG_USEC"))
    86  	}
    87  	dur := time.Duration(usec/2) * time.Microsecond
    88  	logger.Debugf("Setting up sd_notify() watchdog timer every %s", dur)
    89  	wt := time.NewTicker(dur)
    90  
    91  	go func() {
    92  		for {
    93  			select {
    94  			case <-wt.C:
    95  				// TODO: poke the snapd API here and
    96  				//       only report WATCHDOG=1 if it
    97  				//       replies with valid data
    98  				systemd.SdNotify("WATCHDOG=1")
    99  			case <-d.Dying():
   100  				return
   101  			}
   102  		}
   103  	}()
   104  
   105  	return wt, nil
   106  }
   107  
   108  var checkRunningConditionsRetryDelay = 300 * time.Second
   109  
   110  func run(ch chan os.Signal) error {
   111  	t0 := time.Now().Truncate(time.Millisecond)
   112  	snapdenv.SetUserAgentFromVersion(snapdtool.Version, sandbox.ForceDevMode)
   113  
   114  	d, err := daemon.New()
   115  	if err != nil {
   116  		return err
   117  	}
   118  	if err := d.Init(); err != nil {
   119  		return err
   120  	}
   121  
   122  	// Run sanity check now, if anything goes wrong with the
   123  	// check we go into "degraded" mode where we always report
   124  	// the given error to any snap client.
   125  	var checkTicker <-chan time.Time
   126  	var tic *time.Ticker
   127  	if err := sanityCheck(); err != nil {
   128  		degradedErr := fmt.Errorf("system does not fully support snapd: %s", err)
   129  		logger.Noticef("%s", degradedErr)
   130  		d.SetDegradedMode(degradedErr)
   131  		tic = time.NewTicker(checkRunningConditionsRetryDelay)
   132  		checkTicker = tic.C
   133  	}
   134  
   135  	d.Version = snapdtool.Version
   136  
   137  	if err := d.Start(); err != nil {
   138  		return err
   139  	}
   140  
   141  	watchdog, err := runWatchdog(d)
   142  	if err != nil {
   143  		return fmt.Errorf("cannot run software watchdog: %v", err)
   144  	}
   145  	if watchdog != nil {
   146  		defer watchdog.Stop()
   147  	}
   148  
   149  	logger.Debugf("activation done in %v", time.Now().Truncate(time.Millisecond).Sub(t0))
   150  
   151  out:
   152  	for {
   153  		select {
   154  		case sig := <-ch:
   155  			logger.Noticef("Exiting on %s signal.\n", sig)
   156  			break out
   157  		case <-d.Dying():
   158  			// something called Stop()
   159  			break out
   160  		case <-checkTicker:
   161  			if err := sanityCheck(); err == nil {
   162  				d.SetDegradedMode(nil)
   163  				tic.Stop()
   164  			}
   165  		}
   166  	}
   167  
   168  	return d.Stop(ch)
   169  }