github.com/ethanhsieh/snapd@v0.0.0-20210615102523-3db9b8e4edc5/wrappers/services.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2014-2021 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 wrappers
    21  
    22  import (
    23  	"bytes"
    24  	"context"
    25  	"fmt"
    26  	"io/ioutil"
    27  	"os"
    28  	"path/filepath"
    29  	"sort"
    30  	"strconv"
    31  	"strings"
    32  	"text/template"
    33  	"time"
    34  
    35  	"github.com/snapcore/snapd/dirs"
    36  	"github.com/snapcore/snapd/interfaces"
    37  	"github.com/snapcore/snapd/logger"
    38  	"github.com/snapcore/snapd/osutil"
    39  	"github.com/snapcore/snapd/osutil/sys"
    40  	"github.com/snapcore/snapd/progress"
    41  	"github.com/snapcore/snapd/randutil"
    42  	"github.com/snapcore/snapd/snap"
    43  	"github.com/snapcore/snapd/snap/quota"
    44  	"github.com/snapcore/snapd/strutil"
    45  	"github.com/snapcore/snapd/systemd"
    46  	"github.com/snapcore/snapd/timeout"
    47  	"github.com/snapcore/snapd/timeutil"
    48  	"github.com/snapcore/snapd/timings"
    49  	"github.com/snapcore/snapd/usersession/client"
    50  )
    51  
    52  type interacter interface {
    53  	Notify(status string)
    54  }
    55  
    56  // wait this time between TERM and KILL
    57  var killWait = 5 * time.Second
    58  
    59  func serviceStopTimeout(app *snap.AppInfo) time.Duration {
    60  	tout := app.StopTimeout
    61  	if tout == 0 {
    62  		tout = timeout.DefaultTimeout
    63  	}
    64  	return time.Duration(tout)
    65  }
    66  
    67  // TODO: this should not accept AddSnapServicesOptions, it should use some other
    68  // subset of options, specifically it should not accept Preseeding as an option
    69  // here
    70  func generateSnapServiceFile(app *snap.AppInfo, opts *AddSnapServicesOptions) ([]byte, error) {
    71  	if err := snap.ValidateApp(app); err != nil {
    72  		return nil, err
    73  	}
    74  
    75  	return genServiceFile(app, opts)
    76  }
    77  
    78  // generateGroupSliceFile generates a systemd slice unit definition for the
    79  // specified quota group.
    80  func generateGroupSliceFile(grp *quota.Group) ([]byte, error) {
    81  	buf := bytes.Buffer{}
    82  
    83  	template := `[Unit]
    84  Description=Slice for snap quota group %[1]s
    85  Before=slices.target
    86  X-Snappy=yes
    87  
    88  [Slice]
    89  # Always enable memory accounting otherwise the MemoryMax setting does nothing.
    90  MemoryAccounting=true
    91  MemoryMax=%[2]d
    92  # for compatibility with older versions of systemd
    93  MemoryLimit=%[2]d
    94  
    95  # Always enable task accounting in order to be able to count the processes/
    96  # threads, etc for a slice
    97  TasksAccounting=true
    98  `
    99  
   100  	fmt.Fprintf(&buf, template, grp.Name, grp.MemoryLimit)
   101  
   102  	return buf.Bytes(), nil
   103  }
   104  
   105  func stopUserServices(cli *client.Client, inter interacter, services ...string) error {
   106  	ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout.DefaultTimeout))
   107  	defer cancel()
   108  	failures, err := cli.ServicesStop(ctx, services)
   109  	for _, f := range failures {
   110  		inter.Notify(fmt.Sprintf("Could not stop service %q for uid %d: %s", f.Service, f.Uid, f.Error))
   111  	}
   112  	return err
   113  }
   114  
   115  func startUserServices(cli *client.Client, inter interacter, services ...string) error {
   116  	ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout.DefaultTimeout))
   117  	defer cancel()
   118  	startFailures, stopFailures, err := cli.ServicesStart(ctx, services)
   119  	for _, f := range startFailures {
   120  		inter.Notify(fmt.Sprintf("Could not start service %q for uid %d: %s", f.Service, f.Uid, f.Error))
   121  	}
   122  	for _, f := range stopFailures {
   123  		inter.Notify(fmt.Sprintf("While trying to stop previously started service %q for uid %d: %s", f.Service, f.Uid, f.Error))
   124  	}
   125  	return err
   126  }
   127  
   128  func stopService(sysd systemd.Systemd, app *snap.AppInfo, inter interacter) error {
   129  	serviceName := app.ServiceName()
   130  	tout := serviceStopTimeout(app)
   131  
   132  	var extraServices []string
   133  	for _, socket := range app.Sockets {
   134  		extraServices = append(extraServices, filepath.Base(socket.File()))
   135  	}
   136  	if app.Timer != nil {
   137  		extraServices = append(extraServices, filepath.Base(app.Timer.File()))
   138  	}
   139  
   140  	switch app.DaemonScope {
   141  	case snap.SystemDaemon:
   142  		stopErrors := []error{}
   143  		for _, service := range extraServices {
   144  			if err := sysd.Stop(service, tout); err != nil {
   145  				stopErrors = append(stopErrors, err)
   146  			}
   147  		}
   148  
   149  		if err := sysd.Stop(serviceName, tout); err != nil {
   150  			if !systemd.IsTimeout(err) {
   151  				return err
   152  			}
   153  			inter.Notify(fmt.Sprintf("%s refused to stop, killing.", serviceName))
   154  			// ignore errors for kill; nothing we'd do differently at this point
   155  			sysd.Kill(serviceName, "TERM", "")
   156  			time.Sleep(killWait)
   157  			sysd.Kill(serviceName, "KILL", "")
   158  		}
   159  
   160  		if len(stopErrors) > 0 {
   161  			return stopErrors[0]
   162  		}
   163  
   164  	case snap.UserDaemon:
   165  		extraServices = append(extraServices, serviceName)
   166  		cli := client.New()
   167  		return stopUserServices(cli, inter, extraServices...)
   168  	}
   169  
   170  	return nil
   171  }
   172  
   173  // enableServices enables services specified by apps. On success the returned
   174  // disable function can be used to undo all the actions. On error all the
   175  // services get disabled automatically (disable is nil).
   176  func enableServices(apps []*snap.AppInfo, inter interacter) (disable func(), err error) {
   177  	var enabled []string
   178  	var userEnabled []string
   179  
   180  	systemSysd := systemd.New(systemd.SystemMode, inter)
   181  	userSysd := systemd.New(systemd.GlobalUserMode, inter)
   182  
   183  	disableEnabledServices := func() {
   184  		for _, srvName := range enabled {
   185  			if e := systemSysd.Disable(srvName); e != nil {
   186  				inter.Notify(fmt.Sprintf("While trying to disable previously enabled service %q: %v", srvName, e))
   187  			}
   188  		}
   189  		for _, s := range userEnabled {
   190  			if e := userSysd.Disable(s); e != nil {
   191  				inter.Notify(fmt.Sprintf("while trying to disable %s due to previous failure: %v", s, e))
   192  			}
   193  		}
   194  	}
   195  
   196  	defer func() {
   197  		if err != nil {
   198  			disableEnabledServices()
   199  		}
   200  	}()
   201  
   202  	for _, app := range apps {
   203  		var sysd systemd.Systemd
   204  		switch app.DaemonScope {
   205  		case snap.SystemDaemon:
   206  			sysd = systemSysd
   207  		case snap.UserDaemon:
   208  			sysd = userSysd
   209  		}
   210  
   211  		svcName := app.ServiceName()
   212  
   213  		switch app.DaemonScope {
   214  		case snap.SystemDaemon:
   215  			if err = sysd.Enable(svcName); err != nil {
   216  				return nil, err
   217  
   218  			}
   219  			enabled = append(enabled, svcName)
   220  		case snap.UserDaemon:
   221  			if err = userSysd.Enable(svcName); err != nil {
   222  				return nil, err
   223  			}
   224  			userEnabled = append(userEnabled, svcName)
   225  		}
   226  	}
   227  
   228  	return disableEnabledServices, nil
   229  }
   230  
   231  // StartServicesFlags carries extra flags for StartServices.
   232  type StartServicesFlags struct {
   233  	Enable bool
   234  }
   235  
   236  // StartServices starts service units for the applications from the snap which
   237  // are services. Service units will be started in the order provided by the
   238  // caller.
   239  func StartServices(apps []*snap.AppInfo, disabledSvcs []string, flags *StartServicesFlags, inter interacter, tm timings.Measurer) (err error) {
   240  	if flags == nil {
   241  		flags = &StartServicesFlags{}
   242  	}
   243  
   244  	systemSysd := systemd.New(systemd.SystemMode, inter)
   245  	userSysd := systemd.New(systemd.GlobalUserMode, inter)
   246  	cli := client.New()
   247  
   248  	var disableEnabledServices func()
   249  
   250  	defer func() {
   251  		if err == nil {
   252  			return
   253  		}
   254  		if disableEnabledServices != nil {
   255  			disableEnabledServices()
   256  		}
   257  	}()
   258  
   259  	var toEnable []*snap.AppInfo
   260  	systemServices := make([]string, 0, len(apps))
   261  	userServices := make([]string, 0, len(apps))
   262  
   263  	// gather all non-sockets, non-timers, and non-dbus activated
   264  	// services to enable first
   265  	for _, app := range apps {
   266  		// they're *supposed* to be all services, but checking doesn't hurt
   267  		if !app.IsService() {
   268  			continue
   269  		}
   270  		// sockets and timers are enabled and started separately (and unconditionally) further down.
   271  		// dbus activatable services are started on first use.
   272  		if len(app.Sockets) == 0 && app.Timer == nil && len(app.ActivatesOn) == 0 {
   273  			if strutil.ListContains(disabledSvcs, app.Name) {
   274  				continue
   275  			}
   276  			svcName := app.ServiceName()
   277  			switch app.DaemonScope {
   278  			case snap.SystemDaemon:
   279  				systemServices = append(systemServices, svcName)
   280  			case snap.UserDaemon:
   281  				userServices = append(userServices, svcName)
   282  			}
   283  			if flags.Enable {
   284  				toEnable = append(toEnable, app)
   285  			}
   286  		}
   287  	}
   288  
   289  	disableEnabledServices, err = enableServices(toEnable, inter)
   290  	if err != nil {
   291  		return err
   292  	}
   293  
   294  	// handle sockets and timers
   295  	for _, app := range apps {
   296  		// they're *supposed* to be all services, but checking doesn't hurt
   297  		if !app.IsService() {
   298  			continue
   299  		}
   300  
   301  		var sysd systemd.Systemd
   302  		switch app.DaemonScope {
   303  		case snap.SystemDaemon:
   304  			sysd = systemSysd
   305  		case snap.UserDaemon:
   306  			sysd = userSysd
   307  		}
   308  
   309  		defer func(app *snap.AppInfo) {
   310  			if err == nil {
   311  				return
   312  			}
   313  
   314  			if e := stopService(sysd, app, inter); e != nil {
   315  				inter.Notify(fmt.Sprintf("While trying to stop previously started service %q: %v", app.ServiceName(), e))
   316  			}
   317  			for _, socket := range app.Sockets {
   318  				socketService := filepath.Base(socket.File())
   319  				if e := sysd.Disable(socketService); e != nil {
   320  					inter.Notify(fmt.Sprintf("While trying to disable previously enabled socket service %q: %v", socketService, e))
   321  				}
   322  			}
   323  			if app.Timer != nil {
   324  				timerService := filepath.Base(app.Timer.File())
   325  				if e := sysd.Disable(timerService); e != nil {
   326  					inter.Notify(fmt.Sprintf("While trying to disable previously enabled timer service %q: %v", timerService, e))
   327  				}
   328  			}
   329  		}(app)
   330  
   331  		for _, socket := range app.Sockets {
   332  			socketService := filepath.Base(socket.File())
   333  			// enable the socket
   334  			if err = sysd.Enable(socketService); err != nil {
   335  				return err
   336  			}
   337  
   338  			switch app.DaemonScope {
   339  			case snap.SystemDaemon:
   340  				timings.Run(tm, "start-system-socket-service", fmt.Sprintf("start system socket service %q", socketService), func(nested timings.Measurer) {
   341  					err = sysd.Start(socketService)
   342  				})
   343  			case snap.UserDaemon:
   344  				timings.Run(tm, "start-user-socket-service", fmt.Sprintf("start user socket service %q", socketService), func(nested timings.Measurer) {
   345  					err = startUserServices(cli, inter, socketService)
   346  				})
   347  			}
   348  			if err != nil {
   349  				return err
   350  			}
   351  		}
   352  
   353  		if app.Timer != nil {
   354  			timerService := filepath.Base(app.Timer.File())
   355  			// enable the timer
   356  			if err = sysd.Enable(timerService); err != nil {
   357  				return err
   358  			}
   359  
   360  			switch app.DaemonScope {
   361  			case snap.SystemDaemon:
   362  				timings.Run(tm, "start-system-timer-service", fmt.Sprintf("start system timer service %q", timerService), func(nested timings.Measurer) {
   363  					err = sysd.Start(timerService)
   364  				})
   365  			case snap.UserDaemon:
   366  				timings.Run(tm, "start-user-timer-service", fmt.Sprintf("start user timer service %q", timerService), func(nested timings.Measurer) {
   367  					err = startUserServices(cli, inter, timerService)
   368  				})
   369  			}
   370  			if err != nil {
   371  				return err
   372  			}
   373  		}
   374  	}
   375  
   376  	for _, srv := range systemServices {
   377  		// starting all services at once does not create a single
   378  		// transaction, but instead spawns multiple jobs, make sure the
   379  		// services started in the original order by bring them up one
   380  		// by one, see:
   381  		// https://github.com/systemd/systemd/issues/8102
   382  		// https://lists.freedesktop.org/archives/systemd-devel/2018-January/040152.html
   383  		timings.Run(tm, "start-service", fmt.Sprintf("start service %q", srv), func(nested timings.Measurer) {
   384  			err = systemSysd.Start(srv)
   385  		})
   386  		if err != nil {
   387  			// cleanup was set up by iterating over apps
   388  			return err
   389  		}
   390  	}
   391  
   392  	if len(userServices) != 0 {
   393  		timings.Run(tm, "start-user-services", "start user services", func(nested timings.Measurer) {
   394  			err = startUserServices(cli, inter, userServices...)
   395  		})
   396  		if err != nil {
   397  			return err
   398  		}
   399  	}
   400  
   401  	return nil
   402  }
   403  
   404  func userDaemonReload() error {
   405  	cli := client.New()
   406  	ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout.DefaultTimeout))
   407  	defer cancel()
   408  	return cli.ServicesDaemonReload(ctx)
   409  }
   410  
   411  func tryFileUpdate(path string, desiredContent []byte) (old *osutil.MemoryFileState, modified bool, err error) {
   412  	newFileState := osutil.MemoryFileState{
   413  		Content: desiredContent,
   414  		Mode:    os.FileMode(0644),
   415  	}
   416  
   417  	// get the existing content (if any) of the file to have something to
   418  	// rollback to if we have any errors
   419  
   420  	// note we can't use FileReference here since we may be modifying
   421  	// the file, and the FileReference.State() wouldn't be evaluated
   422  	// until _after_ we attempted modification
   423  	oldFileState := osutil.MemoryFileState{}
   424  
   425  	if st, err := os.Stat(path); err == nil {
   426  		b, err := ioutil.ReadFile(path)
   427  		if err != nil {
   428  			return nil, false, err
   429  		}
   430  		oldFileState.Content = b
   431  		oldFileState.Mode = st.Mode()
   432  		newFileState.Mode = st.Mode()
   433  
   434  		// save the old state of the file
   435  		old = &oldFileState
   436  	}
   437  
   438  	if mkdirErr := os.MkdirAll(filepath.Dir(path), 0755); mkdirErr != nil {
   439  		return nil, false, mkdirErr
   440  	}
   441  	ensureErr := osutil.EnsureFileState(path, &newFileState)
   442  	switch ensureErr {
   443  	case osutil.ErrSameState:
   444  		// didn't change the file
   445  		return old, false, nil
   446  	case nil:
   447  		// we successfully modified the file
   448  		return old, true, nil
   449  	default:
   450  		// some other fatal error trying to write the file
   451  		return nil, false, ensureErr
   452  	}
   453  }
   454  
   455  type SnapServiceOptions struct {
   456  	// VitalityRank is the rank of all services in the specified snap used by
   457  	// the OOM killer when OOM conditions are reached.
   458  	VitalityRank int
   459  
   460  	// QuotaGroup is the quota group for all services in the specified snap.
   461  	QuotaGroup *quota.Group
   462  }
   463  
   464  // ObserveChangeCallback can be invoked by EnsureSnapServices to observe
   465  // the previous content of a unit and the new on a change.
   466  // unitType can be "service", "socket", "timer". name is empty for a timer.
   467  type ObserveChangeCallback func(app *snap.AppInfo, grp *quota.Group, unitType string, name, old, new string)
   468  
   469  // EnsureSnapServicesOptions is the set of options applying to the
   470  // EnsureSnapServices operation. It does not include per-snap specific options
   471  // such as VitalityRank or RequireMountedSnapdSnap from AddSnapServiceOptions,
   472  // since those are expected to be provided in the snaps argument.
   473  type EnsureSnapServicesOptions struct {
   474  	// Preseeding is whether the system is currently being preseeded, in which
   475  	// case there is not a running systemd for EnsureSnapServicesOptions to
   476  	// issue commands like systemctl daemon-reload to.
   477  	Preseeding bool
   478  
   479  	// RequireMountedSnapdSnap is whether the generated units should depend on
   480  	// the snapd snap being mounted, this is specific to systems like UC18 and
   481  	// UC20 which have the snapd snap and need to have units generated
   482  	RequireMountedSnapdSnap bool
   483  }
   484  
   485  // EnsureSnapServices will ensure that the specified snap services' file states
   486  // are up to date with the specified options and infos. It will add new services
   487  // if those units don't already exist, but it does not delete existing service
   488  // units that are not present in the snap's Info structures.
   489  // There are two sets of options; there are global options which apply to the
   490  // entire transaction and to every snap service that is ensured, and options
   491  // which are per-snap service and specified in the map argument.
   492  // If any errors are encountered trying to update systemd units, then all
   493  // changes performed up to that point are rolled back, meaning newly written
   494  // units are deleted and modified units are attempted to be restored to their
   495  // previous state.
   496  // To observe which units were added or modified a
   497  // ObserveChangeCallback calllback can be provided. The callback is
   498  // invoked while processing the changes. Because of that it should not
   499  // produce immediate side-effects, as the changes are in effect only
   500  // if the function did not return an error.
   501  // This function is idempotent.
   502  func EnsureSnapServices(snaps map[*snap.Info]*SnapServiceOptions, opts *EnsureSnapServicesOptions, observeChange ObserveChangeCallback, inter interacter) (err error) {
   503  	// note, sysd is not used when preseeding
   504  	sysd := systemd.New(systemd.SystemMode, inter)
   505  
   506  	if opts == nil {
   507  		opts = &EnsureSnapServicesOptions{}
   508  	}
   509  
   510  	// we only consider the global EnsureSnapServicesOptions to decide if we
   511  	// are preseeding or not to reduce confusion about which set of options
   512  	// determines whether we are preseeding or not during the ensure operation
   513  	preseeding := opts.Preseeding
   514  
   515  	// modifiedUnitsPreviousState is the set of units that were modified and the previous
   516  	// state of the unit before modification that we can roll back to if there
   517  	// are any issues.
   518  	// note that the rollback is best effort, if we are rebooted in the middle,
   519  	// there is no guarantee about the state of files, some may have been
   520  	// updated and some may have been rolled back, higher level tasks/changes
   521  	// should have do/undo handlers to properly handle the case where this
   522  	// function is interrupted midway
   523  	modifiedUnitsPreviousState := make(map[string]*osutil.MemoryFileState)
   524  	var modifiedSystem, modifiedUser bool
   525  
   526  	defer func() {
   527  		if err == nil {
   528  			return
   529  		}
   530  		for file, state := range modifiedUnitsPreviousState {
   531  			if state == nil {
   532  				// we don't have anything to rollback to, so just remove the
   533  				// file
   534  				if e := os.Remove(file); e != nil {
   535  					inter.Notify(fmt.Sprintf("while trying to remove %s due to previous failure: %v", file, e))
   536  				}
   537  			} else {
   538  				// rollback the file to the previous state
   539  				if e := osutil.EnsureFileState(file, state); e != nil {
   540  					inter.Notify(fmt.Sprintf("while trying to rollback %s due to previous failure: %v", file, e))
   541  				}
   542  			}
   543  		}
   544  		if modifiedSystem && !preseeding {
   545  			if e := sysd.DaemonReload(); e != nil {
   546  				inter.Notify(fmt.Sprintf("while trying to perform systemd daemon-reload due to previous failure: %v", e))
   547  			}
   548  		}
   549  		if modifiedUser && !preseeding {
   550  			if e := userDaemonReload(); e != nil {
   551  				inter.Notify(fmt.Sprintf("while trying to perform user systemd daemon-reload due to previous failure: %v", e))
   552  			}
   553  		}
   554  	}()
   555  
   556  	handleFileModification := func(app *snap.AppInfo, unitType string, name, path string, content []byte) error {
   557  		old, modifiedFile, err := tryFileUpdate(path, content)
   558  		if err != nil {
   559  			return err
   560  		}
   561  
   562  		if modifiedFile {
   563  			if observeChange != nil {
   564  				var oldContent []byte
   565  				if old != nil {
   566  					oldContent = old.Content
   567  				}
   568  				observeChange(app, nil, unitType, name, string(oldContent), string(content))
   569  			}
   570  			modifiedUnitsPreviousState[path] = old
   571  
   572  			// also mark that we need to reload either the system or
   573  			// user instance of systemd
   574  			switch app.DaemonScope {
   575  			case snap.SystemDaemon:
   576  				modifiedSystem = true
   577  			case snap.UserDaemon:
   578  				modifiedUser = true
   579  			}
   580  		}
   581  
   582  		return nil
   583  	}
   584  
   585  	neededQuotaGrps := &quota.QuotaGroupSet{}
   586  
   587  	for s, snapSvcOpts := range snaps {
   588  		if s.Type() == snap.TypeSnapd {
   589  			return fmt.Errorf("internal error: adding explicit services for snapd snap is unexpected")
   590  		}
   591  
   592  		// always use RequireMountedSnapdSnap options from the global options
   593  		genServiceOpts := &AddSnapServicesOptions{
   594  			RequireMountedSnapdSnap: opts.RequireMountedSnapdSnap,
   595  		}
   596  		if snapSvcOpts != nil {
   597  			// and if there are per-snap options specified, use that for
   598  			// VitalityRank
   599  			genServiceOpts.VitalityRank = snapSvcOpts.VitalityRank
   600  			genServiceOpts.QuotaGroup = snapSvcOpts.QuotaGroup
   601  
   602  			if snapSvcOpts.QuotaGroup != nil {
   603  				if err := neededQuotaGrps.AddAllNecessaryGroups(snapSvcOpts.QuotaGroup); err != nil {
   604  					// this error can basically only be a circular reference
   605  					// in the quota group tree
   606  					return err
   607  				}
   608  			}
   609  		}
   610  		// note that the Preseeding option is not used here at all
   611  
   612  		for _, app := range s.Apps {
   613  			if !app.IsService() {
   614  				continue
   615  			}
   616  
   617  			// create services first; this doesn't trigger systemd
   618  
   619  			// Generate new service file state
   620  			path := app.ServiceFile()
   621  			content, err := generateSnapServiceFile(app, genServiceOpts)
   622  			if err != nil {
   623  				return err
   624  			}
   625  
   626  			if err := handleFileModification(app, "service", app.Name, path, content); err != nil {
   627  				return err
   628  			}
   629  
   630  			// Generate systemd .socket files if needed
   631  			socketFiles, err := generateSnapSocketFiles(app)
   632  			if err != nil {
   633  				return err
   634  			}
   635  			for name, content := range socketFiles {
   636  				path := app.Sockets[name].File()
   637  				if err := handleFileModification(app, "socket", name, path, content); err != nil {
   638  					return err
   639  				}
   640  			}
   641  
   642  			if app.Timer != nil {
   643  				content, err := generateSnapTimerFile(app)
   644  				if err != nil {
   645  					return err
   646  				}
   647  				path := app.Timer.File()
   648  				if err := handleFileModification(app, "timer", "", path, content); err != nil {
   649  					return err
   650  				}
   651  			}
   652  		}
   653  	}
   654  
   655  	handleSliceModification := func(grp *quota.Group, path string, content []byte) error {
   656  		old, modifiedFile, err := tryFileUpdate(path, content)
   657  		if err != nil {
   658  			return err
   659  		}
   660  
   661  		if modifiedFile {
   662  			if observeChange != nil {
   663  				var oldContent []byte
   664  				if old != nil {
   665  					oldContent = old.Content
   666  				}
   667  				observeChange(nil, grp, "slice", grp.Name, string(oldContent), string(content))
   668  			}
   669  
   670  			modifiedUnitsPreviousState[path] = old
   671  
   672  			// also mark that we need to reload the system instance of systemd
   673  			// TODO: also handle reloading the user instance of systemd when
   674  			// needed
   675  			modifiedSystem = true
   676  		}
   677  
   678  		return nil
   679  	}
   680  
   681  	// now make sure that all of the slice units exist
   682  	for _, grp := range neededQuotaGrps.AllQuotaGroups() {
   683  		content, err := generateGroupSliceFile(grp)
   684  		if err != nil {
   685  			return err
   686  		}
   687  
   688  		sliceFileName := grp.SliceFileName()
   689  		path := filepath.Join(dirs.SnapServicesDir, sliceFileName)
   690  		if err := handleSliceModification(grp, path, content); err != nil {
   691  			return err
   692  		}
   693  	}
   694  
   695  	if !preseeding {
   696  		if modifiedSystem {
   697  			if err = sysd.DaemonReload(); err != nil {
   698  				return err
   699  			}
   700  		}
   701  		if modifiedUser {
   702  			if err = userDaemonReload(); err != nil {
   703  				return err
   704  			}
   705  		}
   706  	}
   707  
   708  	return nil
   709  }
   710  
   711  // AddSnapServicesOptions is a struct for controlling the generated service
   712  // definition for a snap service.
   713  type AddSnapServicesOptions struct {
   714  	// VitalityRank is the rank of all services in the specified snap used by
   715  	// the OOM killer when OOM conditions are reached.
   716  	VitalityRank int
   717  
   718  	// QuotaGroup is the quota group for all services in the specified snap.
   719  	QuotaGroup *quota.Group
   720  
   721  	// RequireMountedSnapdSnap is whether the generated units should depend on
   722  	// the snapd snap being mounted, this is specific to systems like UC18 and
   723  	// UC20 which have the snapd snap and need to have units generated
   724  	RequireMountedSnapdSnap bool
   725  
   726  	// Preseeding is whether the system is currently being preseeded, in which
   727  	// case there is not a running systemd for EnsureSnapServicesOptions to
   728  	// issue commands like systemctl daemon-reload to.
   729  	Preseeding bool
   730  }
   731  
   732  // AddSnapServices adds service units for the applications from the snap which
   733  // are services. The services do not get enabled or started.
   734  func AddSnapServices(s *snap.Info, opts *AddSnapServicesOptions, inter interacter) error {
   735  	m := map[*snap.Info]*SnapServiceOptions{
   736  		s: {},
   737  	}
   738  	ensureOpts := &EnsureSnapServicesOptions{}
   739  	if opts != nil {
   740  		// set the per-snap service options
   741  		m[s].VitalityRank = opts.VitalityRank
   742  		m[s].QuotaGroup = opts.QuotaGroup
   743  
   744  		// copy the globally applicable opts from AddSnapServicesOptions to
   745  		// EnsureSnapServicesOptions, since those options override the per-snap opts
   746  		// we put in the map argument
   747  		ensureOpts.Preseeding = opts.Preseeding
   748  		ensureOpts.RequireMountedSnapdSnap = opts.RequireMountedSnapdSnap
   749  	}
   750  
   751  	return EnsureSnapServices(m, ensureOpts, nil, inter)
   752  }
   753  
   754  // StopServicesFlags carries extra flags for StopServices.
   755  type StopServicesFlags struct {
   756  	Disable bool
   757  }
   758  
   759  // StopServices stops and optionally disables service units for the applications
   760  // from the snap which are services.
   761  func StopServices(apps []*snap.AppInfo, flags *StopServicesFlags, reason snap.ServiceStopReason, inter interacter, tm timings.Measurer) error {
   762  	sysd := systemd.New(systemd.SystemMode, inter)
   763  	if flags == nil {
   764  		flags = &StopServicesFlags{}
   765  	}
   766  
   767  	if reason != snap.StopReasonOther {
   768  		logger.Debugf("StopServices called for %q, reason: %v", apps, reason)
   769  	} else {
   770  		logger.Debugf("StopServices called for %q", apps)
   771  	}
   772  	for _, app := range apps {
   773  		// Handle the case where service file doesn't exist and don't try to stop it as it will fail.
   774  		// This can happen with snap try when snap.yaml is modified on the fly and a daemon line is added.
   775  		if !app.IsService() || !osutil.FileExists(app.ServiceFile()) {
   776  			continue
   777  		}
   778  		// Skip stop on refresh when refresh mode is set to something
   779  		// other than "restart" (or "" which is the same)
   780  		if reason == snap.StopReasonRefresh {
   781  			logger.Debugf(" %s refresh-mode: %v", app.Name, app.StopMode)
   782  			switch app.RefreshMode {
   783  			case "endure":
   784  				// skip this service
   785  				continue
   786  			}
   787  		}
   788  
   789  		var err error
   790  		timings.Run(tm, "stop-service", fmt.Sprintf("stop service %q", app.ServiceName()), func(nested timings.Measurer) {
   791  			err = stopService(sysd, app, inter)
   792  			if err == nil && flags.Disable {
   793  				err = sysd.Disable(app.ServiceName())
   794  			}
   795  		})
   796  		if err != nil {
   797  			return err
   798  		}
   799  
   800  		// ensure the service is really stopped on remove regardless
   801  		// of stop-mode
   802  		if reason == snap.StopReasonRemove && !app.StopMode.KillAll() && app.DaemonScope == snap.SystemDaemon {
   803  			// FIXME: make this smarter and avoid the killWait
   804  			//        delay if not needed (i.e. if all processes
   805  			//        have died)
   806  			sysd.Kill(app.ServiceName(), "TERM", "all")
   807  			time.Sleep(killWait)
   808  			sysd.Kill(app.ServiceName(), "KILL", "")
   809  		}
   810  	}
   811  	return nil
   812  }
   813  
   814  // ServicesEnableState returns a map of service names from the given snap,
   815  // together with their enable/disable status.
   816  func ServicesEnableState(s *snap.Info, inter interacter) (map[string]bool, error) {
   817  	sysd := systemd.New(systemd.SystemMode, inter)
   818  
   819  	// loop over all services in the snap, querying systemd for the current
   820  	// systemd state of the snaps
   821  	snapSvcsState := make(map[string]bool, len(s.Apps))
   822  	for name, app := range s.Apps {
   823  		if !app.IsService() {
   824  			continue
   825  		}
   826  		// FIXME: handle user daemons
   827  		if app.DaemonScope != snap.SystemDaemon {
   828  			continue
   829  		}
   830  		state, err := sysd.IsEnabled(app.ServiceName())
   831  		if err != nil {
   832  			return nil, err
   833  		}
   834  		snapSvcsState[name] = state
   835  	}
   836  	return snapSvcsState, nil
   837  }
   838  
   839  // RemoveQuotaGroup ensures that the slice file for a quota group is removed. It
   840  // assumes that the slice corresponding to the group is not in use anymore by
   841  // any services or sub-groups of the group when it is invoked. To remove a group
   842  // with sub-groups, one must remove all the sub-groups first.
   843  // This function is idempotent, if the slice file doesn't exist no error is
   844  // returned.
   845  func RemoveQuotaGroup(grp *quota.Group, inter interacter) error {
   846  	// TODO: it only works on leaf sub-groups currently
   847  	if len(grp.SubGroups) != 0 {
   848  		return fmt.Errorf("internal error: cannot remove quota group with sub-groups")
   849  	}
   850  
   851  	systemSysd := systemd.New(systemd.SystemMode, inter)
   852  
   853  	// remove the slice file
   854  	err := os.Remove(filepath.Join(dirs.SnapServicesDir, grp.SliceFileName()))
   855  	if err != nil && !os.IsNotExist(err) {
   856  		return err
   857  	}
   858  
   859  	if err == nil {
   860  		// we deleted the slice unit, so we need to daemon-reload
   861  		if err := systemSysd.DaemonReload(); err != nil {
   862  			return err
   863  		}
   864  	}
   865  	return nil
   866  }
   867  
   868  // RemoveSnapServices disables and removes service units for the applications
   869  // from the snap which are services. The optional flag indicates whether
   870  // services are removed as part of undoing of first install of a given snap.
   871  func RemoveSnapServices(s *snap.Info, inter interacter) error {
   872  	if s.Type() == snap.TypeSnapd {
   873  		return fmt.Errorf("internal error: removing explicit services for snapd snap is unexpected")
   874  	}
   875  	systemSysd := systemd.New(systemd.SystemMode, inter)
   876  	userSysd := systemd.New(systemd.GlobalUserMode, inter)
   877  	var removedSystem, removedUser bool
   878  
   879  	for _, app := range s.Apps {
   880  		if !app.IsService() || !osutil.FileExists(app.ServiceFile()) {
   881  			continue
   882  		}
   883  
   884  		var sysd systemd.Systemd
   885  		switch app.DaemonScope {
   886  		case snap.SystemDaemon:
   887  			sysd = systemSysd
   888  			removedSystem = true
   889  		case snap.UserDaemon:
   890  			sysd = userSysd
   891  			removedUser = true
   892  		}
   893  		serviceName := filepath.Base(app.ServiceFile())
   894  
   895  		for _, socket := range app.Sockets {
   896  			path := socket.File()
   897  			socketServiceName := filepath.Base(path)
   898  			if err := sysd.Disable(socketServiceName); err != nil {
   899  				return err
   900  			}
   901  
   902  			if err := os.Remove(path); err != nil && !os.IsNotExist(err) {
   903  				logger.Noticef("Failed to remove socket file %q for %q: %v", path, serviceName, err)
   904  			}
   905  		}
   906  
   907  		if app.Timer != nil {
   908  			path := app.Timer.File()
   909  
   910  			timerName := filepath.Base(path)
   911  			if err := sysd.Disable(timerName); err != nil {
   912  				return err
   913  			}
   914  
   915  			if err := os.Remove(path); err != nil && !os.IsNotExist(err) {
   916  				logger.Noticef("Failed to remove timer file %q for %q: %v", path, serviceName, err)
   917  			}
   918  		}
   919  
   920  		if err := sysd.Disable(serviceName); err != nil {
   921  			return err
   922  		}
   923  
   924  		if err := os.Remove(app.ServiceFile()); err != nil && !os.IsNotExist(err) {
   925  			logger.Noticef("Failed to remove service file for %q: %v", serviceName, err)
   926  		}
   927  
   928  	}
   929  
   930  	// only reload if we actually had services
   931  	if removedSystem {
   932  		if err := systemSysd.DaemonReload(); err != nil {
   933  			return err
   934  		}
   935  	}
   936  	if removedUser {
   937  		if err := userDaemonReload(); err != nil {
   938  			return err
   939  		}
   940  	}
   941  
   942  	return nil
   943  }
   944  
   945  func genServiceNames(snap *snap.Info, appNames []string) []string {
   946  	names := make([]string, 0, len(appNames))
   947  
   948  	for _, name := range appNames {
   949  		if app := snap.Apps[name]; app != nil {
   950  			names = append(names, app.ServiceName())
   951  		}
   952  	}
   953  	return names
   954  }
   955  
   956  // TODO: this should not accept AddSnapServicesOptions, it should use some other
   957  // subset of options, specifically it should not accept Preseeding as an option
   958  // here
   959  func genServiceFile(appInfo *snap.AppInfo, opts *AddSnapServicesOptions) ([]byte, error) {
   960  	if opts == nil {
   961  		opts = &AddSnapServicesOptions{}
   962  	}
   963  
   964  	// assemble all of the service directive snippets for all interfaces that
   965  	// this service needs to include in the generated systemd file
   966  
   967  	// use an ordered set to ensure we don't duplicate any keys from interfaces
   968  	// that specify the same snippet
   969  
   970  	// TODO: maybe we should error if multiple interfaces specify different
   971  	// values for the same directive, otherwise one of them will overwrite the
   972  	// other? What happens right now is that the snippet from the plug that
   973  	// comes last will win in the case of directives that can have only one
   974  	// value, but for some directives, systemd combines their values into a
   975  	// list.
   976  	ifaceServiceSnippets := &strutil.OrderedSet{}
   977  
   978  	for _, plug := range appInfo.Plugs {
   979  		iface, err := interfaces.ByName(plug.Interface)
   980  		if err != nil {
   981  			return nil, fmt.Errorf("error processing plugs while generating service unit for %v: %v", appInfo.SecurityTag(), err)
   982  		}
   983  		snips, err := interfaces.PermanentPlugServiceSnippets(iface, plug)
   984  		if err != nil {
   985  			return nil, fmt.Errorf("error processing plugs while generating service unit for %v: %v", appInfo.SecurityTag(), err)
   986  		}
   987  		for _, snip := range snips {
   988  			ifaceServiceSnippets.Put(snip)
   989  		}
   990  	}
   991  
   992  	// join the service snippets into one string to be included in the
   993  	// template
   994  	ifaceSpecifiedServiceSnippet := strings.Join(ifaceServiceSnippets.Items(), "\n")
   995  
   996  	serviceTemplate := `[Unit]
   997  # Auto-generated, DO NOT EDIT
   998  Description=Service for snap application {{.App.Snap.InstanceName}}.{{.App.Name}}
   999  {{- if .MountUnit }}
  1000  Requires={{.MountUnit}}
  1001  {{- end }}
  1002  {{- if .PrerequisiteTarget}}
  1003  Wants={{.PrerequisiteTarget}}
  1004  {{- end}}
  1005  {{- if .After}}
  1006  After={{ stringsJoin .After " " }}
  1007  {{- end}}
  1008  {{- if .Before}}
  1009  Before={{ stringsJoin .Before " "}}
  1010  {{- end}}
  1011  {{- if .CoreMountedSnapdSnapDep}}
  1012  Wants={{ stringsJoin .CoreMountedSnapdSnapDep " "}}
  1013  After={{ stringsJoin .CoreMountedSnapdSnapDep " "}}
  1014  {{- end}}
  1015  X-Snappy=yes
  1016  
  1017  [Service]
  1018  EnvironmentFile=-/etc/environment
  1019  ExecStart={{.App.LauncherCommand}}
  1020  SyslogIdentifier={{.App.Snap.InstanceName}}.{{.App.Name}}
  1021  Restart={{.Restart}}
  1022  {{- if .App.RestartDelay}}
  1023  RestartSec={{.App.RestartDelay.Seconds}}
  1024  {{- end}}
  1025  WorkingDirectory={{.WorkingDir}}
  1026  {{- if .App.StopCommand}}
  1027  ExecStop={{.App.LauncherStopCommand}}
  1028  {{- end}}
  1029  {{- if .App.ReloadCommand}}
  1030  ExecReload={{.App.LauncherReloadCommand}}
  1031  {{- end}}
  1032  {{- if .App.PostStopCommand}}
  1033  ExecStopPost={{.App.LauncherPostStopCommand}}
  1034  {{- end}}
  1035  {{- if .StopTimeout}}
  1036  TimeoutStopSec={{.StopTimeout.Seconds}}
  1037  {{- end}}
  1038  {{- if .StartTimeout}}
  1039  TimeoutStartSec={{.StartTimeout.Seconds}}
  1040  {{- end}}
  1041  Type={{.App.Daemon}}
  1042  {{- if .Remain}}
  1043  RemainAfterExit={{.Remain}}
  1044  {{- end}}
  1045  {{- if .BusName}}
  1046  BusName={{.BusName}}
  1047  {{- end}}
  1048  {{- if .App.WatchdogTimeout}}
  1049  WatchdogSec={{.App.WatchdogTimeout.Seconds}}
  1050  {{- end}}
  1051  {{- if .KillMode}}
  1052  KillMode={{.KillMode}}
  1053  {{- end}}
  1054  {{- if .KillSignal}}
  1055  KillSignal={{.KillSignal}}
  1056  {{- end}}
  1057  {{- if .OOMAdjustScore }}
  1058  OOMScoreAdjust={{.OOMAdjustScore}}
  1059  {{- end}}
  1060  {{- if .InterfaceServiceSnippets}}
  1061  {{.InterfaceServiceSnippets}}
  1062  {{- end}}
  1063  {{- if .SliceUnit}}
  1064  Slice={{.SliceUnit}}
  1065  {{- end}}
  1066  {{- if not (or .App.Sockets .App.Timer .App.ActivatesOn) }}
  1067  
  1068  [Install]
  1069  WantedBy={{.ServicesTarget}}
  1070  {{- end}}
  1071  `
  1072  	var templateOut bytes.Buffer
  1073  	tmpl := template.New("service-wrapper")
  1074  	tmpl.Funcs(template.FuncMap{
  1075  		"stringsJoin": strings.Join,
  1076  	})
  1077  	t := template.Must(tmpl.Parse(serviceTemplate))
  1078  
  1079  	restartCond := appInfo.RestartCond.String()
  1080  	if restartCond == "" {
  1081  		restartCond = snap.RestartOnFailure.String()
  1082  	}
  1083  
  1084  	// use score -900+vitalityRank, where vitalityRank starts at 1
  1085  	// and considering snapd itself has OOMScoreAdjust=-900
  1086  	const baseOOMAdjustScore = -900
  1087  	var oomAdjustScore int
  1088  	if opts.VitalityRank > 0 {
  1089  		oomAdjustScore = baseOOMAdjustScore + opts.VitalityRank
  1090  	}
  1091  
  1092  	var remain string
  1093  	if appInfo.Daemon == "oneshot" {
  1094  		// any restart condition other than "no" is invalid for oneshot daemons
  1095  		restartCond = "no"
  1096  		// If StopExec is present for a oneshot service than we also need
  1097  		// RemainAfterExit=yes
  1098  		if appInfo.StopCommand != "" {
  1099  			remain = "yes"
  1100  		}
  1101  	}
  1102  	var killMode string
  1103  	if !appInfo.StopMode.KillAll() {
  1104  		killMode = "process"
  1105  	}
  1106  
  1107  	var busName string
  1108  	if appInfo.Daemon == "dbus" {
  1109  		busName = appInfo.BusName
  1110  		if busName == "" && len(appInfo.ActivatesOn) != 0 {
  1111  			slot := appInfo.ActivatesOn[len(appInfo.ActivatesOn)-1]
  1112  			if err := slot.Attr("name", &busName); err != nil {
  1113  				// This should be impossible for a valid AppInfo
  1114  				logger.Noticef("Cannot get 'name' attribute of dbus slot %q: %v", slot.Name, err)
  1115  			}
  1116  		}
  1117  	}
  1118  
  1119  	wrapperData := struct {
  1120  		App *snap.AppInfo
  1121  
  1122  		Restart                  string
  1123  		WorkingDir               string
  1124  		StopTimeout              time.Duration
  1125  		StartTimeout             time.Duration
  1126  		ServicesTarget           string
  1127  		PrerequisiteTarget       string
  1128  		MountUnit                string
  1129  		Remain                   string
  1130  		KillMode                 string
  1131  		KillSignal               string
  1132  		OOMAdjustScore           int
  1133  		BusName                  string
  1134  		Before                   []string
  1135  		After                    []string
  1136  		InterfaceServiceSnippets string
  1137  		SliceUnit                string
  1138  
  1139  		Home    string
  1140  		EnvVars string
  1141  
  1142  		CoreMountedSnapdSnapDep []string
  1143  	}{
  1144  		App: appInfo,
  1145  
  1146  		InterfaceServiceSnippets: ifaceSpecifiedServiceSnippet,
  1147  
  1148  		Restart:        restartCond,
  1149  		StopTimeout:    serviceStopTimeout(appInfo),
  1150  		StartTimeout:   time.Duration(appInfo.StartTimeout),
  1151  		Remain:         remain,
  1152  		KillMode:       killMode,
  1153  		KillSignal:     appInfo.StopMode.KillSignal(),
  1154  		OOMAdjustScore: oomAdjustScore,
  1155  		BusName:        busName,
  1156  
  1157  		Before: genServiceNames(appInfo.Snap, appInfo.Before),
  1158  		After:  genServiceNames(appInfo.Snap, appInfo.After),
  1159  
  1160  		// systemd runs as PID 1 so %h will not work.
  1161  		Home: "/root",
  1162  	}
  1163  	switch appInfo.DaemonScope {
  1164  	case snap.SystemDaemon:
  1165  		wrapperData.ServicesTarget = systemd.ServicesTarget
  1166  		wrapperData.PrerequisiteTarget = systemd.PrerequisiteTarget
  1167  		wrapperData.MountUnit = filepath.Base(systemd.MountUnitPath(appInfo.Snap.MountDir()))
  1168  		wrapperData.WorkingDir = appInfo.Snap.DataDir()
  1169  		wrapperData.After = append(wrapperData.After, "snapd.apparmor.service")
  1170  	case snap.UserDaemon:
  1171  		wrapperData.ServicesTarget = systemd.UserServicesTarget
  1172  		// FIXME: ideally use UserDataDir("%h"), but then the
  1173  		// unit fails if the directory doesn't exist.
  1174  		wrapperData.WorkingDir = appInfo.Snap.DataDir()
  1175  	default:
  1176  		panic("unknown snap.DaemonScope")
  1177  	}
  1178  
  1179  	// check the quota group slice
  1180  	if opts.QuotaGroup != nil {
  1181  		wrapperData.SliceUnit = opts.QuotaGroup.SliceFileName()
  1182  	}
  1183  
  1184  	// Add extra "After" targets
  1185  	if wrapperData.PrerequisiteTarget != "" {
  1186  		wrapperData.After = append([]string{wrapperData.PrerequisiteTarget}, wrapperData.After...)
  1187  	}
  1188  	if wrapperData.MountUnit != "" {
  1189  		wrapperData.After = append([]string{wrapperData.MountUnit}, wrapperData.After...)
  1190  	}
  1191  
  1192  	if opts.RequireMountedSnapdSnap {
  1193  		// on core 18+ systems, the snapd tooling is exported
  1194  		// into the host system via a special mount unit, which
  1195  		// also adds an implicit dependency on the snapd snap
  1196  		// mount thus /usr/bin/snap points
  1197  		wrapperData.CoreMountedSnapdSnapDep = []string{SnapdToolingMountUnit}
  1198  	}
  1199  
  1200  	if err := t.Execute(&templateOut, wrapperData); err != nil {
  1201  		// this can never happen, except we forget a variable
  1202  		logger.Panicf("Unable to execute template: %v", err)
  1203  	}
  1204  
  1205  	return templateOut.Bytes(), nil
  1206  }
  1207  
  1208  func genServiceSocketFile(appInfo *snap.AppInfo, socketName string) []byte {
  1209  	socketTemplate := `[Unit]
  1210  # Auto-generated, DO NOT EDIT
  1211  Description=Socket {{.SocketName}} for snap application {{.App.Snap.InstanceName}}.{{.App.Name}}
  1212  {{- if .MountUnit}}
  1213  Requires={{.MountUnit}}
  1214  After={{.MountUnit}}
  1215  {{- end}}
  1216  X-Snappy=yes
  1217  
  1218  [Socket]
  1219  Service={{.ServiceFileName}}
  1220  FileDescriptorName={{.SocketInfo.Name}}
  1221  ListenStream={{.ListenStream}}
  1222  {{- if .SocketInfo.SocketMode}}
  1223  SocketMode={{.SocketInfo.SocketMode | printf "%04o"}}
  1224  {{- end}}
  1225  
  1226  [Install]
  1227  WantedBy={{.SocketsTarget}}
  1228  `
  1229  	var templateOut bytes.Buffer
  1230  	t := template.Must(template.New("socket-wrapper").Parse(socketTemplate))
  1231  
  1232  	socket := appInfo.Sockets[socketName]
  1233  	listenStream := renderListenStream(socket)
  1234  	wrapperData := struct {
  1235  		App             *snap.AppInfo
  1236  		ServiceFileName string
  1237  		SocketsTarget   string
  1238  		MountUnit       string
  1239  		SocketName      string
  1240  		SocketInfo      *snap.SocketInfo
  1241  		ListenStream    string
  1242  	}{
  1243  		App:             appInfo,
  1244  		ServiceFileName: filepath.Base(appInfo.ServiceFile()),
  1245  		SocketsTarget:   systemd.SocketsTarget,
  1246  		SocketName:      socketName,
  1247  		SocketInfo:      socket,
  1248  		ListenStream:    listenStream,
  1249  	}
  1250  	switch appInfo.DaemonScope {
  1251  	case snap.SystemDaemon:
  1252  		wrapperData.MountUnit = filepath.Base(systemd.MountUnitPath(appInfo.Snap.MountDir()))
  1253  	case snap.UserDaemon:
  1254  		// nothing
  1255  	default:
  1256  		panic("unknown snap.DaemonScope")
  1257  	}
  1258  
  1259  	if err := t.Execute(&templateOut, wrapperData); err != nil {
  1260  		// this can never happen, except we forget a variable
  1261  		logger.Panicf("Unable to execute template: %v", err)
  1262  	}
  1263  
  1264  	return templateOut.Bytes()
  1265  }
  1266  
  1267  func generateSnapSocketFiles(app *snap.AppInfo) (map[string][]byte, error) {
  1268  	if err := snap.ValidateApp(app); err != nil {
  1269  		return nil, err
  1270  	}
  1271  
  1272  	socketFiles := make(map[string][]byte)
  1273  	for name := range app.Sockets {
  1274  		socketFiles[name] = genServiceSocketFile(app, name)
  1275  	}
  1276  	return socketFiles, nil
  1277  }
  1278  
  1279  func renderListenStream(socket *snap.SocketInfo) string {
  1280  	s := socket.App.Snap
  1281  	listenStream := socket.ListenStream
  1282  	switch socket.App.DaemonScope {
  1283  	case snap.SystemDaemon:
  1284  		listenStream = strings.Replace(listenStream, "$SNAP_DATA", s.DataDir(), -1)
  1285  		// TODO: when we support User/Group in the generated
  1286  		// systemd unit, adjust this accordingly
  1287  		serviceUserUid := sys.UserID(0)
  1288  		runtimeDir := s.UserXdgRuntimeDir(serviceUserUid)
  1289  		listenStream = strings.Replace(listenStream, "$XDG_RUNTIME_DIR", runtimeDir, -1)
  1290  		listenStream = strings.Replace(listenStream, "$SNAP_COMMON", s.CommonDataDir(), -1)
  1291  	case snap.UserDaemon:
  1292  		listenStream = strings.Replace(listenStream, "$SNAP_USER_DATA", s.UserDataDir("%h"), -1)
  1293  		listenStream = strings.Replace(listenStream, "$SNAP_USER_COMMON", s.UserCommonDataDir("%h"), -1)
  1294  		// FIXME: find some way to share code with snap.UserXdgRuntimeDir()
  1295  		listenStream = strings.Replace(listenStream, "$XDG_RUNTIME_DIR", fmt.Sprintf("%%t/snap.%s", s.InstanceName()), -1)
  1296  	default:
  1297  		panic("unknown snap.DaemonScope")
  1298  	}
  1299  	return listenStream
  1300  }
  1301  
  1302  func generateSnapTimerFile(app *snap.AppInfo) ([]byte, error) {
  1303  	timerTemplate := `[Unit]
  1304  # Auto-generated, DO NOT EDIT
  1305  Description=Timer {{.TimerName}} for snap application {{.App.Snap.InstanceName}}.{{.App.Name}}
  1306  {{- if .MountUnit}}
  1307  Requires={{.MountUnit}}
  1308  After={{.MountUnit}}
  1309  {{- end}}
  1310  X-Snappy=yes
  1311  
  1312  [Timer]
  1313  Unit={{.ServiceFileName}}
  1314  {{ range .Schedules }}OnCalendar={{ . }}
  1315  {{ end }}
  1316  [Install]
  1317  WantedBy={{.TimersTarget}}
  1318  `
  1319  	var templateOut bytes.Buffer
  1320  	t := template.Must(template.New("timer-wrapper").Parse(timerTemplate))
  1321  
  1322  	timerSchedule, err := timeutil.ParseSchedule(app.Timer.Timer)
  1323  	if err != nil {
  1324  		return nil, err
  1325  	}
  1326  
  1327  	schedules := generateOnCalendarSchedules(timerSchedule)
  1328  
  1329  	wrapperData := struct {
  1330  		App             *snap.AppInfo
  1331  		ServiceFileName string
  1332  		TimersTarget    string
  1333  		TimerName       string
  1334  		MountUnit       string
  1335  		Schedules       []string
  1336  	}{
  1337  		App:             app,
  1338  		ServiceFileName: filepath.Base(app.ServiceFile()),
  1339  		TimersTarget:    systemd.TimersTarget,
  1340  		TimerName:       app.Name,
  1341  		Schedules:       schedules,
  1342  	}
  1343  	switch app.DaemonScope {
  1344  	case snap.SystemDaemon:
  1345  		wrapperData.MountUnit = filepath.Base(systemd.MountUnitPath(app.Snap.MountDir()))
  1346  	case snap.UserDaemon:
  1347  		// nothing
  1348  	default:
  1349  		panic("unknown snap.DaemonScope")
  1350  	}
  1351  
  1352  	if err := t.Execute(&templateOut, wrapperData); err != nil {
  1353  		// this can never happen, except we forget a variable
  1354  		logger.Panicf("Unable to execute template: %v", err)
  1355  	}
  1356  
  1357  	return templateOut.Bytes(), nil
  1358  }
  1359  
  1360  func makeAbbrevWeekdays(start time.Weekday, end time.Weekday) []string {
  1361  	out := make([]string, 0, 7)
  1362  	for w := start; w%7 != (end + 1); w++ {
  1363  		out = append(out, time.Weekday(w % 7).String()[0:3])
  1364  	}
  1365  	return out
  1366  }
  1367  
  1368  // daysRange generates a string representing a continuous range between given
  1369  // day numbers, which due to compatiblilty with old systemd version uses a
  1370  // verbose syntax of x,y,z instead of x..z
  1371  func daysRange(start, end uint) string {
  1372  	var buf bytes.Buffer
  1373  	for i := start; i <= end; i++ {
  1374  		buf.WriteString(strconv.FormatInt(int64(i), 10))
  1375  		if i < end {
  1376  			buf.WriteRune(',')
  1377  		}
  1378  	}
  1379  	return buf.String()
  1380  }
  1381  
  1382  // generateOnCalendarSchedules converts a schedule into OnCalendar schedules
  1383  // suitable for use in systemd *.timer units using systemd.time(7)
  1384  // https://www.freedesktop.org/software/systemd/man/systemd.time.html
  1385  // XXX: old systemd versions do not support x..y ranges
  1386  func generateOnCalendarSchedules(schedule []*timeutil.Schedule) []string {
  1387  	calendarEvents := make([]string, 0, len(schedule))
  1388  	for _, sched := range schedule {
  1389  		days := make([]string, 0, len(sched.WeekSpans))
  1390  		for _, week := range sched.WeekSpans {
  1391  			abbrev := strings.Join(makeAbbrevWeekdays(week.Start.Weekday, week.End.Weekday), ",")
  1392  
  1393  			if week.Start.Pos == timeutil.EveryWeek && week.End.Pos == timeutil.EveryWeek {
  1394  				// eg: mon, mon-fri, fri-mon
  1395  				days = append(days, fmt.Sprintf("%s *-*-*", abbrev))
  1396  				continue
  1397  			}
  1398  			// examples:
  1399  			// mon1 - Mon *-*-1..7 (Monday during the first 7 days)
  1400  			// fri1 - Fri *-*-1..7 (Friday during the first 7 days)
  1401  
  1402  			// entries below will make systemd timer expire more
  1403  			// frequently than the schedule suggests, however snap
  1404  			// runner evaluates current time and gates the actual
  1405  			// action
  1406  			//
  1407  			// mon1-tue - *-*-1..7 *-*-8 (anchored at first
  1408  			// Monday; Monday happens during the 7 days,
  1409  			// Tuesday can possibly happen on the 8th day if
  1410  			// the month started on Tuesday)
  1411  			//
  1412  			// mon-tue1 - *-*~1 *-*-1..7 (anchored at first
  1413  			// Tuesday; matching Monday can happen on the
  1414  			// last day of previous month if Tuesday is the
  1415  			// 1st)
  1416  			//
  1417  			// mon5-tue - *-*~1..7 *-*-1 (anchored at last
  1418  			// Monday, the matching Tuesday can still happen
  1419  			// within the last 7 days, or on the 1st of the
  1420  			// next month)
  1421  			//
  1422  			// fri4-mon - *-*-22-31 *-*-1..7 (anchored at 4th
  1423  			// Friday, can span onto the next month, extreme case in
  1424  			// February when 28th is Friday)
  1425  			//
  1426  			// XXX: since old versions of systemd, eg. 229 available
  1427  			// in 16.04 does not support x..y ranges, days need to
  1428  			// be enumerated like so:
  1429  			// Mon *-*-1..7 -> Mon *-*-1,2,3,4,5,6,7
  1430  			//
  1431  			// XXX: old systemd versions do not support the last n
  1432  			// days syntax eg, *-*~1, thus the range needs to be
  1433  			// generated in more verbose way like so:
  1434  			// Mon *-*~1..7 -> Mon *-*-22,23,24,25,26,27,28,29,30,31
  1435  			// (22-28 is the last week, but the month can have
  1436  			// anywhere from 28 to 31 days)
  1437  			//
  1438  			startPos := week.Start.Pos
  1439  			endPos := startPos
  1440  			if !week.AnchoredAtStart() {
  1441  				startPos = week.End.Pos
  1442  				endPos = startPos
  1443  			}
  1444  			startDay := (startPos-1)*7 + 1
  1445  			endDay := (endPos) * 7
  1446  
  1447  			if week.IsSingleDay() {
  1448  				// single day, can use the 'weekday' filter
  1449  				if startPos == timeutil.LastWeek {
  1450  					// last week of a month, which can be
  1451  					// 22-28 in case of February, while
  1452  					// month can have between 28 and 31 days
  1453  					days = append(days,
  1454  						fmt.Sprintf("%s *-*-%s", abbrev, daysRange(22, 31)))
  1455  				} else {
  1456  					days = append(days,
  1457  						fmt.Sprintf("%s *-*-%s", abbrev, daysRange(startDay, endDay)))
  1458  				}
  1459  				continue
  1460  			}
  1461  
  1462  			if week.AnchoredAtStart() {
  1463  				// explore the edge cases first
  1464  				switch startPos {
  1465  				case timeutil.LastWeek:
  1466  					// starts in the last week of the month and
  1467  					// possibly spans into the first week of the
  1468  					// next month;
  1469  					// month can have between 28 and 31
  1470  					// days
  1471  					days = append(days,
  1472  						// trailing 29-31 that are not part of a full week
  1473  						fmt.Sprintf("*-*-%s", daysRange(29, 31)),
  1474  						fmt.Sprintf("*-*-%s", daysRange(1, 7)))
  1475  				case 4:
  1476  					// a range in the 4th week can span onto
  1477  					// the next week, which is either 28-31
  1478  					// or in extreme case (eg. February with
  1479  					// 28 days) 1-7 of the next month
  1480  					days = append(days,
  1481  						// trailing 29-31 that are not part of a full week
  1482  						fmt.Sprintf("*-*-%s", daysRange(29, 31)),
  1483  						fmt.Sprintf("*-*-%s", daysRange(1, 7)))
  1484  				default:
  1485  					// can possibly spill into the next week
  1486  					days = append(days,
  1487  						fmt.Sprintf("*-*-%s", daysRange(startDay+7, endDay+7)))
  1488  				}
  1489  
  1490  				if startDay < 28 {
  1491  					days = append(days,
  1492  						fmt.Sprintf("*-*-%s", daysRange(startDay, endDay)))
  1493  				} else {
  1494  					// from the end of the month
  1495  					days = append(days,
  1496  						fmt.Sprintf("*-*-%s", daysRange(startDay-7, endDay-7)))
  1497  				}
  1498  			} else {
  1499  				switch endPos {
  1500  				case timeutil.LastWeek:
  1501  					// month can have between 28 and 31
  1502  					// days, add trailing 29-31 that are not
  1503  					// part of a full week
  1504  					days = append(days, fmt.Sprintf("*-*-%s", daysRange(29, 31)))
  1505  				case 1:
  1506  					// possibly spans from the last week of the
  1507  					// previous month and ends in the first week of
  1508  					// current month
  1509  					days = append(days, fmt.Sprintf("*-*-%s", daysRange(22, 31)))
  1510  				default:
  1511  					// can possibly spill into the previous week
  1512  					days = append(days,
  1513  						fmt.Sprintf("*-*-%s", daysRange(startDay-7, endDay-7)))
  1514  				}
  1515  				if endDay < 28 {
  1516  					days = append(days,
  1517  						fmt.Sprintf("*-*-%s", daysRange(startDay, endDay)))
  1518  				} else {
  1519  					days = append(days,
  1520  						fmt.Sprintf("*-*-%s", daysRange(startDay-7, endDay-7)))
  1521  				}
  1522  			}
  1523  		}
  1524  
  1525  		if len(days) == 0 {
  1526  			// no weekday spec, meaning the timer runs every day
  1527  			days = []string{"*-*-*"}
  1528  		}
  1529  
  1530  		startTimes := make([]string, 0, len(sched.ClockSpans))
  1531  		for _, clocks := range sched.ClockSpans {
  1532  			// use expanded clock spans
  1533  			for _, span := range clocks.ClockSpans() {
  1534  				when := span.Start
  1535  				if span.Spread {
  1536  					length := span.End.Sub(span.Start)
  1537  					if length < 0 {
  1538  						// span Start wraps around, so we have '00:00.Sub(23:45)'
  1539  						length = -length
  1540  					}
  1541  					if length > 5*time.Minute {
  1542  						// replicate what timeutil.Next() does
  1543  						// and cut some time at the end of the
  1544  						// window so that events do not happen
  1545  						// directly one after another
  1546  						length -= 5 * time.Minute
  1547  					}
  1548  					when = when.Add(randutil.RandomDuration(length))
  1549  				}
  1550  				if when.Hour == 24 {
  1551  					// 24:00 for us means the other end of
  1552  					// the day, for systemd we need to
  1553  					// adjust it to the 0-23 hour range
  1554  					when.Hour -= 24
  1555  				}
  1556  
  1557  				startTimes = append(startTimes, when.String())
  1558  			}
  1559  		}
  1560  
  1561  		for _, day := range days {
  1562  			if len(startTimes) == 0 {
  1563  				// current schedule is days only
  1564  				calendarEvents = append(calendarEvents, day)
  1565  				continue
  1566  			}
  1567  
  1568  			for _, startTime := range startTimes {
  1569  				calendarEvents = append(calendarEvents, fmt.Sprintf("%s %s", day, startTime))
  1570  			}
  1571  		}
  1572  	}
  1573  	return calendarEvents
  1574  }
  1575  
  1576  type RestartServicesFlags struct {
  1577  	Reload bool
  1578  }
  1579  
  1580  // Restart or reload services; if reload flag is set then "systemctl reload-or-restart" is attempted.
  1581  func RestartServices(svcs []*snap.AppInfo, flags *RestartServicesFlags, inter interacter, tm timings.Measurer) error {
  1582  	sysd := systemd.New(systemd.SystemMode, inter)
  1583  
  1584  	for _, srv := range svcs {
  1585  		// they're *supposed* to be all services, but checking doesn't hurt
  1586  		if !srv.IsService() {
  1587  			continue
  1588  		}
  1589  
  1590  		var err error
  1591  		timings.Run(tm, "restart-service", fmt.Sprintf("restart service %q", srv), func(nested timings.Measurer) {
  1592  			if flags != nil && flags.Reload {
  1593  				err = sysd.ReloadOrRestart(srv.ServiceName())
  1594  			} else {
  1595  				// note: stop followed by start, not just 'restart'
  1596  				err = sysd.Restart(srv.ServiceName(), 5*time.Second)
  1597  			}
  1598  		})
  1599  		if err != nil {
  1600  			// there is nothing we can do about failed service
  1601  			return err
  1602  		}
  1603  	}
  1604  	return nil
  1605  }
  1606  
  1607  // QueryDisabledServices returns a list of all currently disabled snap services
  1608  // in the snap.
  1609  func QueryDisabledServices(info *snap.Info, pb progress.Meter) ([]string, error) {
  1610  	// save the list of services that are in the disabled state before unlinking
  1611  	// and thus removing the snap services
  1612  	snapSvcStates, err := ServicesEnableState(info, pb)
  1613  	if err != nil {
  1614  		return nil, err
  1615  	}
  1616  
  1617  	disabledSnapSvcs := []string{}
  1618  	// add all disabled services to the list
  1619  	for svc, isEnabled := range snapSvcStates {
  1620  		if !isEnabled {
  1621  			disabledSnapSvcs = append(disabledSnapSvcs, svc)
  1622  		}
  1623  	}
  1624  
  1625  	// sort for easier testing
  1626  	sort.Strings(disabledSnapSvcs)
  1627  
  1628  	return disabledSnapSvcs, nil
  1629  }