github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/wrappers/services.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2014-2016 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  	"os"
    27  	"path/filepath"
    28  	"sort"
    29  	"strconv"
    30  	"strings"
    31  	"text/template"
    32  	"time"
    33  
    34  	"github.com/snapcore/snapd/dirs"
    35  	"github.com/snapcore/snapd/logger"
    36  	"github.com/snapcore/snapd/osutil"
    37  	"github.com/snapcore/snapd/osutil/sys"
    38  	"github.com/snapcore/snapd/progress"
    39  	"github.com/snapcore/snapd/randutil"
    40  	"github.com/snapcore/snapd/snap"
    41  	"github.com/snapcore/snapd/strutil"
    42  	"github.com/snapcore/snapd/systemd"
    43  	"github.com/snapcore/snapd/timeout"
    44  	"github.com/snapcore/snapd/timeutil"
    45  	"github.com/snapcore/snapd/timings"
    46  	"github.com/snapcore/snapd/usersession/client"
    47  )
    48  
    49  type interacter interface {
    50  	Notify(status string)
    51  }
    52  
    53  // wait this time between TERM and KILL
    54  var killWait = 5 * time.Second
    55  
    56  func serviceStopTimeout(app *snap.AppInfo) time.Duration {
    57  	tout := app.StopTimeout
    58  	if tout == 0 {
    59  		tout = timeout.DefaultTimeout
    60  	}
    61  	return time.Duration(tout)
    62  }
    63  
    64  func generateSnapServiceFile(app *snap.AppInfo, opts *AddSnapServicesOptions) ([]byte, error) {
    65  	if err := snap.ValidateApp(app); err != nil {
    66  		return nil, err
    67  	}
    68  
    69  	return genServiceFile(app, opts), nil
    70  }
    71  
    72  func stopUserServices(cli *client.Client, inter interacter, services ...string) error {
    73  	ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout.DefaultTimeout))
    74  	defer cancel()
    75  	failures, err := cli.ServicesStop(ctx, services)
    76  	for _, f := range failures {
    77  		inter.Notify(fmt.Sprintf("Could not stop service %q for uid %d: %s", f.Service, f.Uid, f.Error))
    78  	}
    79  	return err
    80  }
    81  
    82  func startUserServices(cli *client.Client, inter interacter, services ...string) error {
    83  	ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout.DefaultTimeout))
    84  	defer cancel()
    85  	startFailures, stopFailures, err := cli.ServicesStart(ctx, services)
    86  	for _, f := range startFailures {
    87  		inter.Notify(fmt.Sprintf("Could not start service %q for uid %d: %s", f.Service, f.Uid, f.Error))
    88  	}
    89  	for _, f := range stopFailures {
    90  		inter.Notify(fmt.Sprintf("While trying to stop previously started service %q for uid %d: %s", f.Service, f.Uid, f.Error))
    91  	}
    92  	return err
    93  }
    94  
    95  func stopService(sysd systemd.Systemd, app *snap.AppInfo, inter interacter) error {
    96  	serviceName := app.ServiceName()
    97  	tout := serviceStopTimeout(app)
    98  
    99  	var extraServices []string
   100  	for _, socket := range app.Sockets {
   101  		extraServices = append(extraServices, filepath.Base(socket.File()))
   102  	}
   103  	if app.Timer != nil {
   104  		extraServices = append(extraServices, filepath.Base(app.Timer.File()))
   105  	}
   106  
   107  	switch app.DaemonScope {
   108  	case snap.SystemDaemon:
   109  		stopErrors := []error{}
   110  		for _, service := range extraServices {
   111  			if err := sysd.Stop(service, tout); err != nil {
   112  				stopErrors = append(stopErrors, err)
   113  			}
   114  		}
   115  
   116  		if err := sysd.Stop(serviceName, tout); err != nil {
   117  			if !systemd.IsTimeout(err) {
   118  				return err
   119  			}
   120  			inter.Notify(fmt.Sprintf("%s refused to stop, killing.", serviceName))
   121  			// ignore errors for kill; nothing we'd do differently at this point
   122  			sysd.Kill(serviceName, "TERM", "")
   123  			time.Sleep(killWait)
   124  			sysd.Kill(serviceName, "KILL", "")
   125  		}
   126  
   127  		if len(stopErrors) > 0 {
   128  			return stopErrors[0]
   129  		}
   130  
   131  	case snap.UserDaemon:
   132  		extraServices = append(extraServices, serviceName)
   133  		cli := client.New()
   134  		return stopUserServices(cli, inter, extraServices...)
   135  	}
   136  
   137  	return nil
   138  }
   139  
   140  // enableServices enables services specified by apps. On success the returned
   141  // disable function can be used to undo all the actions. On error all the
   142  // services get disabled automatically (disable is nil).
   143  func enableServices(apps []*snap.AppInfo, inter interacter) (disable func(), err error) {
   144  	var enabled []string
   145  	var userEnabled []string
   146  
   147  	systemSysd := systemd.New(dirs.GlobalRootDir, systemd.SystemMode, inter)
   148  	userSysd := systemd.New(dirs.GlobalRootDir, systemd.GlobalUserMode, inter)
   149  
   150  	disableEnabledServices := func() {
   151  		for _, srvName := range enabled {
   152  			if e := systemSysd.Disable(srvName); e != nil {
   153  				inter.Notify(fmt.Sprintf("While trying to disable previously enabled service %q: %v", srvName, e))
   154  			}
   155  		}
   156  		for _, s := range userEnabled {
   157  			if e := userSysd.Disable(s); e != nil {
   158  				inter.Notify(fmt.Sprintf("while trying to disable %s due to previous failure: %v", s, e))
   159  			}
   160  		}
   161  	}
   162  
   163  	defer func() {
   164  		if err != nil {
   165  			disableEnabledServices()
   166  		}
   167  	}()
   168  
   169  	for _, app := range apps {
   170  		var sysd systemd.Systemd
   171  		switch app.DaemonScope {
   172  		case snap.SystemDaemon:
   173  			sysd = systemSysd
   174  		case snap.UserDaemon:
   175  			sysd = userSysd
   176  		}
   177  
   178  		svcName := app.ServiceName()
   179  
   180  		switch app.DaemonScope {
   181  		case snap.SystemDaemon:
   182  			if err = sysd.Enable(svcName); err != nil {
   183  				return nil, err
   184  
   185  			}
   186  			enabled = append(enabled, svcName)
   187  		case snap.UserDaemon:
   188  			if err = userSysd.Enable(svcName); err != nil {
   189  				return nil, err
   190  			}
   191  			userEnabled = append(userEnabled, svcName)
   192  		}
   193  	}
   194  
   195  	return disableEnabledServices, nil
   196  }
   197  
   198  // StartServicesFlags carries extra flags for StartServices.
   199  type StartServicesFlags struct {
   200  	Enable bool
   201  }
   202  
   203  // StartServices starts service units for the applications from the snap which
   204  // are services. Service units will be started in the order provided by the
   205  // caller.
   206  func StartServices(apps []*snap.AppInfo, disabledSvcs []string, flags *StartServicesFlags, inter interacter, tm timings.Measurer) (err error) {
   207  	systemSysd := systemd.New(dirs.GlobalRootDir, systemd.SystemMode, inter)
   208  	userSysd := systemd.New(dirs.GlobalRootDir, systemd.GlobalUserMode, inter)
   209  	cli := client.New()
   210  
   211  	systemServices := make([]string, 0, len(apps))
   212  	userServices := make([]string, 0, len(apps))
   213  	for _, app := range apps {
   214  		// they're *supposed* to be all services, but checking doesn't hurt
   215  		if !app.IsService() {
   216  			continue
   217  		}
   218  
   219  		var sysd systemd.Systemd
   220  		switch app.DaemonScope {
   221  		case snap.SystemDaemon:
   222  			sysd = systemSysd
   223  		case snap.UserDaemon:
   224  			sysd = userSysd
   225  		}
   226  
   227  		defer func(app *snap.AppInfo) {
   228  			if err == nil {
   229  				return
   230  			}
   231  			if e := stopService(sysd, app, inter); e != nil {
   232  				inter.Notify(fmt.Sprintf("While trying to stop previously started service %q: %v", app.ServiceName(), e))
   233  			}
   234  			for _, socket := range app.Sockets {
   235  				socketService := filepath.Base(socket.File())
   236  				if e := sysd.Disable(socketService); e != nil {
   237  					inter.Notify(fmt.Sprintf("While trying to disable previously enabled socket service %q: %v", socketService, e))
   238  				}
   239  			}
   240  			if app.Timer != nil {
   241  				timerService := filepath.Base(app.Timer.File())
   242  				if e := sysd.Disable(timerService); e != nil {
   243  					inter.Notify(fmt.Sprintf("While trying to disable previously enabled timer service %q: %v", timerService, e))
   244  				}
   245  			}
   246  		}(app)
   247  
   248  		if len(app.Sockets) == 0 && app.Timer == nil {
   249  			// check if the service is disabled, if so don't start it up
   250  			// this could happen for example if the service was disabled in
   251  			// the install hook by snapctl or if the service was disabled in
   252  			// the previous installation
   253  			isEnabled, err := sysd.IsEnabled(app.ServiceName())
   254  			if err != nil {
   255  				return err
   256  			}
   257  			if isEnabled {
   258  				switch app.DaemonScope {
   259  				case snap.SystemDaemon:
   260  					systemServices = append(systemServices, app.ServiceName())
   261  				case snap.UserDaemon:
   262  					userServices = append(userServices, app.ServiceName())
   263  				}
   264  			}
   265  		}
   266  
   267  		for _, socket := range app.Sockets {
   268  			socketService := filepath.Base(socket.File())
   269  			// enable the socket
   270  			if err := sysd.Enable(socketService); err != nil {
   271  				return err
   272  			}
   273  
   274  			switch app.DaemonScope {
   275  			case snap.SystemDaemon:
   276  				timings.Run(tm, "start-system-socket-service", fmt.Sprintf("start system socket service %q", socketService), func(nested timings.Measurer) {
   277  					err = sysd.Start(socketService)
   278  				})
   279  			case snap.UserDaemon:
   280  				timings.Run(tm, "start-user-socket-service", fmt.Sprintf("start user socket service %q", socketService), func(nested timings.Measurer) {
   281  					err = startUserServices(cli, inter, socketService)
   282  				})
   283  			}
   284  			if err != nil {
   285  				return err
   286  			}
   287  		}
   288  
   289  		if app.Timer != nil {
   290  			timerService := filepath.Base(app.Timer.File())
   291  			// enable the timer
   292  			if err := sysd.Enable(timerService); err != nil {
   293  				return err
   294  			}
   295  
   296  			switch app.DaemonScope {
   297  			case snap.SystemDaemon:
   298  				timings.Run(tm, "start-system-timer-service", fmt.Sprintf("start system timer service %q", timerService), func(nested timings.Measurer) {
   299  					err = sysd.Start(timerService)
   300  				})
   301  			case snap.UserDaemon:
   302  				timings.Run(tm, "start-user-timer-service", fmt.Sprintf("start user timer service %q", timerService), func(nested timings.Measurer) {
   303  					err = startUserServices(cli, inter, timerService)
   304  				})
   305  			}
   306  			if err != nil {
   307  				return err
   308  			}
   309  		}
   310  	}
   311  
   312  	for _, srv := range systemServices {
   313  		// starting all services at once does not create a single
   314  		// transaction, but instead spawns multiple jobs, make sure the
   315  		// services started in the original order by bring them up one
   316  		// by one, see:
   317  		// https://github.com/systemd/systemd/issues/8102
   318  		// https://lists.freedesktop.org/archives/systemd-devel/2018-January/040152.html
   319  		timings.Run(tm, "start-service", fmt.Sprintf("start service %q", srv), func(nested timings.Measurer) {
   320  			err = systemSysd.Start(srv)
   321  		})
   322  		if err != nil {
   323  			// cleanup was set up by iterating over apps
   324  			return err
   325  		}
   326  	}
   327  	if len(userServices) != 0 {
   328  		timings.Run(tm, "start-user-services", "start user services", func(nested timings.Measurer) {
   329  			err = startUserServices(cli, inter, userServices...)
   330  		})
   331  		if err != nil {
   332  			return err
   333  		}
   334  	}
   335  
   336  	return nil
   337  }
   338  
   339  func userDaemonReload() error {
   340  	cli := client.New()
   341  	ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout.DefaultTimeout))
   342  	defer cancel()
   343  	return cli.ServicesDaemonReload(ctx)
   344  }
   345  
   346  type AddSnapServicesOptions struct {
   347  	Preseeding   bool
   348  	VitalityRank int
   349  }
   350  
   351  // AddSnapServices adds service units for the applications from the snap which are services.
   352  func AddSnapServices(s *snap.Info, disabledSvcs []string, opts *AddSnapServicesOptions, inter interacter) (err error) {
   353  	if s.Type() == snap.TypeSnapd {
   354  		return fmt.Errorf("internal error: adding explicit services for snapd snap is unexpected")
   355  	}
   356  
   357  	if opts == nil {
   358  		opts = &AddSnapServicesOptions{}
   359  	}
   360  
   361  	// check if any previously disabled services are now no longer services and
   362  	// log messages about that
   363  	for _, svc := range disabledSvcs {
   364  		app, ok := s.Apps[svc]
   365  		if !ok {
   366  			logger.Noticef("previously disabled service %s no longer exists", svc)
   367  		} else if !app.IsService() {
   368  			logger.Noticef("previously disabled service %s is now an app and not a service", svc)
   369  		}
   370  	}
   371  
   372  	// TODO: remove once services get enabled on start and not when created.
   373  	preseeding := opts.Preseeding
   374  
   375  	sysd := systemd.New(dirs.GlobalRootDir, systemd.SystemMode, inter)
   376  	var written []string
   377  	var writtenSystem, writtenUser bool
   378  	var disableEnabledServices func()
   379  
   380  	defer func() {
   381  		if err == nil {
   382  			return
   383  		}
   384  		if disableEnabledServices != nil {
   385  			disableEnabledServices()
   386  		}
   387  		for _, s := range written {
   388  			if e := os.Remove(s); e != nil {
   389  				inter.Notify(fmt.Sprintf("while trying to remove %s due to previous failure: %v", s, e))
   390  			}
   391  		}
   392  		if writtenSystem && !preseeding {
   393  			if e := sysd.DaemonReload(); e != nil {
   394  				inter.Notify(fmt.Sprintf("while trying to perform systemd daemon-reload due to previous failure: %v", e))
   395  			}
   396  		}
   397  		if writtenUser && !preseeding {
   398  			if e := userDaemonReload(); e != nil {
   399  				inter.Notify(fmt.Sprintf("while trying to perform user systemd daemon-reload due to previous failure: %v", e))
   400  			}
   401  		}
   402  	}()
   403  
   404  	var toEnable []*snap.AppInfo
   405  
   406  	// create services first; this doesn't trigger systemd
   407  	for _, app := range s.Apps {
   408  		if !app.IsService() {
   409  			continue
   410  		}
   411  		// Generate service file
   412  		content, err := generateSnapServiceFile(app, opts)
   413  		if err != nil {
   414  			return err
   415  		}
   416  		svcFilePath := app.ServiceFile()
   417  		os.MkdirAll(filepath.Dir(svcFilePath), 0755)
   418  		if err := osutil.AtomicWriteFile(svcFilePath, content, 0644, 0); err != nil {
   419  			return err
   420  		}
   421  		written = append(written, svcFilePath)
   422  		switch app.DaemonScope {
   423  		case snap.SystemDaemon:
   424  			writtenSystem = true
   425  		case snap.UserDaemon:
   426  			writtenUser = true
   427  		}
   428  
   429  		// Generate systemd .socket files if needed
   430  		socketFiles, err := generateSnapSocketFiles(app)
   431  		if err != nil {
   432  			return err
   433  		}
   434  		for path, content := range *socketFiles {
   435  			os.MkdirAll(filepath.Dir(path), 0755)
   436  			if err := osutil.AtomicWriteFile(path, content, 0644, 0); err != nil {
   437  				return err
   438  			}
   439  			written = append(written, path)
   440  		}
   441  
   442  		if app.Timer != nil {
   443  			content, err := generateSnapTimerFile(app)
   444  			if err != nil {
   445  				return err
   446  			}
   447  			path := app.Timer.File()
   448  			os.MkdirAll(filepath.Dir(path), 0755)
   449  			if err := osutil.AtomicWriteFile(path, content, 0644, 0); err != nil {
   450  				return err
   451  			}
   452  			written = append(written, path)
   453  		}
   454  
   455  		if app.Timer != nil || len(app.Sockets) != 0 {
   456  			// service is socket or timer activated, not during the
   457  			// boot
   458  			continue
   459  		}
   460  		// XXX: this may become quadratic, optimize.
   461  		// When preseeding services get enabled in doMarkPresseeded instead at
   462  		// the moment.
   463  		if strutil.ListContains(disabledSvcs, app.Name) || preseeding {
   464  			continue
   465  		}
   466  
   467  		toEnable = append(toEnable, app)
   468  	}
   469  
   470  	disableEnabledServices, err = enableServices(toEnable, inter)
   471  	if err != nil {
   472  		return err
   473  	}
   474  
   475  	if !preseeding {
   476  		if writtenSystem {
   477  			if err := sysd.DaemonReload(); err != nil {
   478  				return err
   479  			}
   480  		}
   481  		if writtenUser {
   482  			if err := userDaemonReload(); err != nil {
   483  				return err
   484  			}
   485  		}
   486  	}
   487  
   488  	return nil
   489  }
   490  
   491  // EnableSnapServices enables all services of the snap; the main use case for this is
   492  // the first boot of a pre-seeded image with service files already in place but not enabled.
   493  // XXX: it should go away once services are fixed and enabled on start.
   494  func EnableSnapServices(s *snap.Info, inter interacter) (err error) {
   495  	sysd := systemd.New(dirs.GlobalRootDir, systemd.SystemMode, inter)
   496  	for _, app := range s.Apps {
   497  		if app.IsService() {
   498  			svcName := app.ServiceName()
   499  			if err := sysd.Enable(svcName); err != nil {
   500  				return err
   501  			}
   502  		}
   503  	}
   504  	return nil
   505  }
   506  
   507  // StopServicesFlags carries extra flags for StopServices.
   508  type StopServicesFlags struct {
   509  	Disable bool
   510  }
   511  
   512  // StopServices stops and optionally disables service units for the applications
   513  // from the snap which are services.
   514  func StopServices(apps []*snap.AppInfo, flags *StopServicesFlags, reason snap.ServiceStopReason, inter interacter, tm timings.Measurer) error {
   515  	sysd := systemd.New(dirs.GlobalRootDir, systemd.SystemMode, inter)
   516  	if flags == nil {
   517  		flags = &StopServicesFlags{}
   518  	}
   519  
   520  	if reason != snap.StopReasonOther {
   521  		logger.Debugf("StopServices called for %q, reason: %v", apps, reason)
   522  	} else {
   523  		logger.Debugf("StopServices called for %q", apps)
   524  	}
   525  	for _, app := range apps {
   526  		// Handle the case where service file doesn't exist and don't try to stop it as it will fail.
   527  		// This can happen with snap try when snap.yaml is modified on the fly and a daemon line is added.
   528  		if !app.IsService() || !osutil.FileExists(app.ServiceFile()) {
   529  			continue
   530  		}
   531  		// Skip stop on refresh when refresh mode is set to something
   532  		// other than "restart" (or "" which is the same)
   533  		if reason == snap.StopReasonRefresh {
   534  			logger.Debugf(" %s refresh-mode: %v", app.Name, app.StopMode)
   535  			switch app.RefreshMode {
   536  			case "endure":
   537  				// skip this service
   538  				continue
   539  			}
   540  		}
   541  
   542  		var err error
   543  		timings.Run(tm, "stop-service", fmt.Sprintf("stop service %q", app.ServiceName()), func(nested timings.Measurer) {
   544  			err = stopService(sysd, app, inter)
   545  			if err == nil && flags.Disable {
   546  				err = sysd.Disable(app.ServiceName())
   547  			}
   548  		})
   549  		if err != nil {
   550  			return err
   551  		}
   552  
   553  		// ensure the service is really stopped on remove regardless
   554  		// of stop-mode
   555  		if reason == snap.StopReasonRemove && !app.StopMode.KillAll() && app.DaemonScope == snap.SystemDaemon {
   556  			// FIXME: make this smarter and avoid the killWait
   557  			//        delay if not needed (i.e. if all processes
   558  			//        have died)
   559  			sysd.Kill(app.ServiceName(), "TERM", "all")
   560  			time.Sleep(killWait)
   561  			sysd.Kill(app.ServiceName(), "KILL", "")
   562  		}
   563  	}
   564  	return nil
   565  }
   566  
   567  // ServicesEnableState returns a map of service names from the given snap,
   568  // together with their enable/disable status.
   569  func ServicesEnableState(s *snap.Info, inter interacter) (map[string]bool, error) {
   570  	sysd := systemd.New(dirs.GlobalRootDir, systemd.SystemMode, inter)
   571  
   572  	// loop over all services in the snap, querying systemd for the current
   573  	// systemd state of the snaps
   574  	snapSvcsState := make(map[string]bool, len(s.Apps))
   575  	for name, app := range s.Apps {
   576  		if !app.IsService() {
   577  			continue
   578  		}
   579  		// FIXME: handle user daemons
   580  		if app.DaemonScope != snap.SystemDaemon {
   581  			continue
   582  		}
   583  		state, err := sysd.IsEnabled(app.ServiceName())
   584  		if err != nil {
   585  			return nil, err
   586  		}
   587  		snapSvcsState[name] = state
   588  	}
   589  	return snapSvcsState, nil
   590  }
   591  
   592  // RemoveSnapServices disables and removes service units for the applications
   593  // from the snap which are services. The optional flag indicates whether
   594  // services are removed as part of undoing of first install of a given snap.
   595  func RemoveSnapServices(s *snap.Info, inter interacter) error {
   596  	if s.Type() == snap.TypeSnapd {
   597  		return fmt.Errorf("internal error: removing explicit services for snapd snap is unexpected")
   598  	}
   599  	systemSysd := systemd.New(dirs.GlobalRootDir, systemd.SystemMode, inter)
   600  	userSysd := systemd.New(dirs.GlobalRootDir, systemd.GlobalUserMode, inter)
   601  	var removedSystem, removedUser bool
   602  
   603  	for _, app := range s.Apps {
   604  		if !app.IsService() || !osutil.FileExists(app.ServiceFile()) {
   605  			continue
   606  		}
   607  
   608  		var sysd systemd.Systemd
   609  		switch app.DaemonScope {
   610  		case snap.SystemDaemon:
   611  			sysd = systemSysd
   612  			removedSystem = true
   613  		case snap.UserDaemon:
   614  			sysd = userSysd
   615  			removedUser = true
   616  		}
   617  		serviceName := filepath.Base(app.ServiceFile())
   618  
   619  		for _, socket := range app.Sockets {
   620  			path := socket.File()
   621  			socketServiceName := filepath.Base(path)
   622  			if err := sysd.Disable(socketServiceName); err != nil {
   623  				return err
   624  			}
   625  
   626  			if err := os.Remove(path); err != nil && !os.IsNotExist(err) {
   627  				logger.Noticef("Failed to remove socket file %q for %q: %v", path, serviceName, err)
   628  			}
   629  		}
   630  
   631  		if app.Timer != nil {
   632  			path := app.Timer.File()
   633  
   634  			timerName := filepath.Base(path)
   635  			if err := sysd.Disable(timerName); err != nil {
   636  				return err
   637  			}
   638  
   639  			if err := os.Remove(path); err != nil && !os.IsNotExist(err) {
   640  				logger.Noticef("Failed to remove timer file %q for %q: %v", path, serviceName, err)
   641  			}
   642  		}
   643  
   644  		if err := sysd.Disable(serviceName); err != nil {
   645  			return err
   646  		}
   647  
   648  		if err := os.Remove(app.ServiceFile()); err != nil && !os.IsNotExist(err) {
   649  			logger.Noticef("Failed to remove service file for %q: %v", serviceName, err)
   650  		}
   651  
   652  	}
   653  
   654  	// only reload if we actually had services
   655  	if removedSystem {
   656  		if err := systemSysd.DaemonReload(); err != nil {
   657  			return err
   658  		}
   659  	}
   660  	if removedUser {
   661  		if err := userDaemonReload(); err != nil {
   662  			return err
   663  		}
   664  	}
   665  
   666  	return nil
   667  }
   668  
   669  func genServiceNames(snap *snap.Info, appNames []string) []string {
   670  	names := make([]string, 0, len(appNames))
   671  
   672  	for _, name := range appNames {
   673  		if app := snap.Apps[name]; app != nil {
   674  			names = append(names, app.ServiceName())
   675  		}
   676  	}
   677  	return names
   678  }
   679  
   680  func genServiceFile(appInfo *snap.AppInfo, opts *AddSnapServicesOptions) []byte {
   681  	if opts == nil {
   682  		opts = &AddSnapServicesOptions{}
   683  	}
   684  
   685  	serviceTemplate := `[Unit]
   686  # Auto-generated, DO NOT EDIT
   687  Description=Service for snap application {{.App.Snap.InstanceName}}.{{.App.Name}}
   688  {{- if .MountUnit }}
   689  Requires={{.MountUnit}}
   690  {{- end }}
   691  {{- if .PrerequisiteTarget}}
   692  Wants={{.PrerequisiteTarget}}
   693  {{- end}}
   694  {{- if .After}}
   695  After={{ stringsJoin .After " " }}
   696  {{- end}}
   697  {{- if .Before}}
   698  Before={{ stringsJoin .Before " "}}
   699  {{- end}}
   700  X-Snappy=yes
   701  
   702  [Service]
   703  EnvironmentFile=-/etc/environment
   704  ExecStart={{.App.LauncherCommand}}
   705  SyslogIdentifier={{.App.Snap.InstanceName}}.{{.App.Name}}
   706  Restart={{.Restart}}
   707  {{- if .App.RestartDelay}}
   708  RestartSec={{.App.RestartDelay.Seconds}}
   709  {{- end}}
   710  WorkingDirectory={{.WorkingDir}}
   711  {{- if .App.StopCommand}}
   712  ExecStop={{.App.LauncherStopCommand}}
   713  {{- end}}
   714  {{- if .App.ReloadCommand}}
   715  ExecReload={{.App.LauncherReloadCommand}}
   716  {{- end}}
   717  {{- if .App.PostStopCommand}}
   718  ExecStopPost={{.App.LauncherPostStopCommand}}
   719  {{- end}}
   720  {{- if .StopTimeout}}
   721  TimeoutStopSec={{.StopTimeout.Seconds}}
   722  {{- end}}
   723  {{- if .StartTimeout}}
   724  TimeoutStartSec={{.StartTimeout.Seconds}}
   725  {{- end}}
   726  Type={{.App.Daemon}}
   727  {{- if .Remain}}
   728  RemainAfterExit={{.Remain}}
   729  {{- end}}
   730  {{- if .App.BusName}}
   731  BusName={{.App.BusName}}
   732  {{- end}}
   733  {{- if .App.WatchdogTimeout}}
   734  WatchdogSec={{.App.WatchdogTimeout.Seconds}}
   735  {{- end}}
   736  {{- if .KillMode}}
   737  KillMode={{.KillMode}}
   738  {{- end}}
   739  {{- if .KillSignal}}
   740  KillSignal={{.KillSignal}}
   741  {{- end}}
   742  {{- if .OOMAdjustScore }}
   743  OOMScoreAdjust={{.OOMAdjustScore}}
   744  {{- end}}
   745  {{- if not .App.Sockets}}
   746  
   747  [Install]
   748  WantedBy={{.ServicesTarget}}
   749  {{- end}}
   750  `
   751  	var templateOut bytes.Buffer
   752  	tmpl := template.New("service-wrapper")
   753  	tmpl.Funcs(template.FuncMap{
   754  		"stringsJoin": strings.Join,
   755  	})
   756  	t := template.Must(tmpl.Parse(serviceTemplate))
   757  
   758  	restartCond := appInfo.RestartCond.String()
   759  	if restartCond == "" {
   760  		restartCond = snap.RestartOnFailure.String()
   761  	}
   762  
   763  	// use score -900+vitalityRank, where vitalityRank starts at 1
   764  	// and considering snapd itself has OOMScoreAdjust=-900
   765  	const baseOOMAdjustScore = -900
   766  	var oomAdjustScore int
   767  	if opts.VitalityRank > 0 {
   768  		oomAdjustScore = baseOOMAdjustScore + opts.VitalityRank
   769  	}
   770  
   771  	var remain string
   772  	if appInfo.Daemon == "oneshot" {
   773  		// any restart condition other than "no" is invalid for oneshot daemons
   774  		restartCond = "no"
   775  		// If StopExec is present for a oneshot service than we also need
   776  		// RemainAfterExit=yes
   777  		if appInfo.StopCommand != "" {
   778  			remain = "yes"
   779  		}
   780  	}
   781  	var killMode string
   782  	if !appInfo.StopMode.KillAll() {
   783  		killMode = "process"
   784  	}
   785  
   786  	wrapperData := struct {
   787  		App *snap.AppInfo
   788  
   789  		Restart            string
   790  		WorkingDir         string
   791  		StopTimeout        time.Duration
   792  		StartTimeout       time.Duration
   793  		ServicesTarget     string
   794  		PrerequisiteTarget string
   795  		MountUnit          string
   796  		Remain             string
   797  		KillMode           string
   798  		KillSignal         string
   799  		OOMAdjustScore     int
   800  		Before             []string
   801  		After              []string
   802  
   803  		Home    string
   804  		EnvVars string
   805  	}{
   806  		App: appInfo,
   807  
   808  		Restart:        restartCond,
   809  		StopTimeout:    serviceStopTimeout(appInfo),
   810  		StartTimeout:   time.Duration(appInfo.StartTimeout),
   811  		Remain:         remain,
   812  		KillMode:       killMode,
   813  		KillSignal:     appInfo.StopMode.KillSignal(),
   814  		OOMAdjustScore: oomAdjustScore,
   815  
   816  		Before: genServiceNames(appInfo.Snap, appInfo.Before),
   817  		After:  genServiceNames(appInfo.Snap, appInfo.After),
   818  
   819  		// systemd runs as PID 1 so %h will not work.
   820  		Home: "/root",
   821  	}
   822  	switch appInfo.DaemonScope {
   823  	case snap.SystemDaemon:
   824  		wrapperData.ServicesTarget = systemd.ServicesTarget
   825  		wrapperData.PrerequisiteTarget = systemd.PrerequisiteTarget
   826  		wrapperData.MountUnit = filepath.Base(systemd.MountUnitPath(appInfo.Snap.MountDir()))
   827  		wrapperData.WorkingDir = appInfo.Snap.DataDir()
   828  		wrapperData.After = append(wrapperData.After, "snapd.apparmor.service")
   829  	case snap.UserDaemon:
   830  		wrapperData.ServicesTarget = systemd.UserServicesTarget
   831  		// FIXME: ideally use UserDataDir("%h"), but then the
   832  		// unit fails if the directory doesn't exist.
   833  		wrapperData.WorkingDir = appInfo.Snap.DataDir()
   834  	default:
   835  		panic("unknown snap.DaemonScope")
   836  	}
   837  
   838  	// Add extra "After" targets
   839  	if wrapperData.PrerequisiteTarget != "" {
   840  		wrapperData.After = append([]string{wrapperData.PrerequisiteTarget}, wrapperData.After...)
   841  	}
   842  	if wrapperData.MountUnit != "" {
   843  		wrapperData.After = append([]string{wrapperData.MountUnit}, wrapperData.After...)
   844  	}
   845  
   846  	if err := t.Execute(&templateOut, wrapperData); err != nil {
   847  		// this can never happen, except we forget a variable
   848  		logger.Panicf("Unable to execute template: %v", err)
   849  	}
   850  
   851  	return templateOut.Bytes()
   852  }
   853  
   854  func genServiceSocketFile(appInfo *snap.AppInfo, socketName string) []byte {
   855  	socketTemplate := `[Unit]
   856  # Auto-generated, DO NOT EDIT
   857  Description=Socket {{.SocketName}} for snap application {{.App.Snap.InstanceName}}.{{.App.Name}}
   858  {{- if .MountUnit}}
   859  Requires={{.MountUnit}}
   860  After={{.MountUnit}}
   861  {{- end}}
   862  X-Snappy=yes
   863  
   864  [Socket]
   865  Service={{.ServiceFileName}}
   866  FileDescriptorName={{.SocketInfo.Name}}
   867  ListenStream={{.ListenStream}}
   868  {{- if .SocketInfo.SocketMode}}
   869  SocketMode={{.SocketInfo.SocketMode | printf "%04o"}}
   870  {{- end}}
   871  
   872  [Install]
   873  WantedBy={{.SocketsTarget}}
   874  `
   875  	var templateOut bytes.Buffer
   876  	t := template.Must(template.New("socket-wrapper").Parse(socketTemplate))
   877  
   878  	socket := appInfo.Sockets[socketName]
   879  	listenStream := renderListenStream(socket)
   880  	wrapperData := struct {
   881  		App             *snap.AppInfo
   882  		ServiceFileName string
   883  		SocketsTarget   string
   884  		MountUnit       string
   885  		SocketName      string
   886  		SocketInfo      *snap.SocketInfo
   887  		ListenStream    string
   888  	}{
   889  		App:             appInfo,
   890  		ServiceFileName: filepath.Base(appInfo.ServiceFile()),
   891  		SocketsTarget:   systemd.SocketsTarget,
   892  		SocketName:      socketName,
   893  		SocketInfo:      socket,
   894  		ListenStream:    listenStream,
   895  	}
   896  	switch appInfo.DaemonScope {
   897  	case snap.SystemDaemon:
   898  		wrapperData.MountUnit = filepath.Base(systemd.MountUnitPath(appInfo.Snap.MountDir()))
   899  	case snap.UserDaemon:
   900  		// nothing
   901  	default:
   902  		panic("unknown snap.DaemonScope")
   903  	}
   904  
   905  	if err := t.Execute(&templateOut, wrapperData); err != nil {
   906  		// this can never happen, except we forget a variable
   907  		logger.Panicf("Unable to execute template: %v", err)
   908  	}
   909  
   910  	return templateOut.Bytes()
   911  }
   912  
   913  func generateSnapSocketFiles(app *snap.AppInfo) (*map[string][]byte, error) {
   914  	if err := snap.ValidateApp(app); err != nil {
   915  		return nil, err
   916  	}
   917  
   918  	socketFiles := make(map[string][]byte)
   919  	for name, socket := range app.Sockets {
   920  		socketFiles[socket.File()] = genServiceSocketFile(app, name)
   921  	}
   922  	return &socketFiles, nil
   923  }
   924  
   925  func renderListenStream(socket *snap.SocketInfo) string {
   926  	s := socket.App.Snap
   927  	listenStream := socket.ListenStream
   928  	switch socket.App.DaemonScope {
   929  	case snap.SystemDaemon:
   930  		listenStream = strings.Replace(listenStream, "$SNAP_DATA", s.DataDir(), -1)
   931  		// TODO: when we support User/Group in the generated
   932  		// systemd unit, adjust this accordingly
   933  		serviceUserUid := sys.UserID(0)
   934  		runtimeDir := s.UserXdgRuntimeDir(serviceUserUid)
   935  		listenStream = strings.Replace(listenStream, "$XDG_RUNTIME_DIR", runtimeDir, -1)
   936  		listenStream = strings.Replace(listenStream, "$SNAP_COMMON", s.CommonDataDir(), -1)
   937  	case snap.UserDaemon:
   938  		listenStream = strings.Replace(listenStream, "$SNAP_USER_DATA", s.UserDataDir("%h"), -1)
   939  		listenStream = strings.Replace(listenStream, "$SNAP_USER_COMMON", s.UserCommonDataDir("%h"), -1)
   940  		// FIXME: find some way to share code with snap.UserXdgRuntimeDir()
   941  		listenStream = strings.Replace(listenStream, "$XDG_RUNTIME_DIR", fmt.Sprintf("%%t/snap.%s", s.InstanceName()), -1)
   942  	default:
   943  		panic("unknown snap.DaemonScope")
   944  	}
   945  	return listenStream
   946  }
   947  
   948  func generateSnapTimerFile(app *snap.AppInfo) ([]byte, error) {
   949  	timerTemplate := `[Unit]
   950  # Auto-generated, DO NOT EDIT
   951  Description=Timer {{.TimerName}} for snap application {{.App.Snap.InstanceName}}.{{.App.Name}}
   952  {{- if .MountUnit}}
   953  Requires={{.MountUnit}}
   954  After={{.MountUnit}}
   955  {{- end}}
   956  X-Snappy=yes
   957  
   958  [Timer]
   959  Unit={{.ServiceFileName}}
   960  {{ range .Schedules }}OnCalendar={{ . }}
   961  {{ end }}
   962  [Install]
   963  WantedBy={{.TimersTarget}}
   964  `
   965  	var templateOut bytes.Buffer
   966  	t := template.Must(template.New("timer-wrapper").Parse(timerTemplate))
   967  
   968  	timerSchedule, err := timeutil.ParseSchedule(app.Timer.Timer)
   969  	if err != nil {
   970  		return nil, err
   971  	}
   972  
   973  	schedules := generateOnCalendarSchedules(timerSchedule)
   974  
   975  	wrapperData := struct {
   976  		App             *snap.AppInfo
   977  		ServiceFileName string
   978  		TimersTarget    string
   979  		TimerName       string
   980  		MountUnit       string
   981  		Schedules       []string
   982  	}{
   983  		App:             app,
   984  		ServiceFileName: filepath.Base(app.ServiceFile()),
   985  		TimersTarget:    systemd.TimersTarget,
   986  		TimerName:       app.Name,
   987  		Schedules:       schedules,
   988  	}
   989  	switch app.DaemonScope {
   990  	case snap.SystemDaemon:
   991  		wrapperData.MountUnit = filepath.Base(systemd.MountUnitPath(app.Snap.MountDir()))
   992  	case snap.UserDaemon:
   993  		// nothing
   994  	default:
   995  		panic("unknown snap.DaemonScope")
   996  	}
   997  
   998  	if err := t.Execute(&templateOut, wrapperData); err != nil {
   999  		// this can never happen, except we forget a variable
  1000  		logger.Panicf("Unable to execute template: %v", err)
  1001  	}
  1002  
  1003  	return templateOut.Bytes(), nil
  1004  }
  1005  
  1006  func makeAbbrevWeekdays(start time.Weekday, end time.Weekday) []string {
  1007  	out := make([]string, 0, 7)
  1008  	for w := start; w%7 != (end + 1); w++ {
  1009  		out = append(out, time.Weekday(w % 7).String()[0:3])
  1010  	}
  1011  	return out
  1012  }
  1013  
  1014  // daysRange generates a string representing a continuous range between given
  1015  // day numbers, which due to compatiblilty with old systemd version uses a
  1016  // verbose syntax of x,y,z instead of x..z
  1017  func daysRange(start, end uint) string {
  1018  	var buf bytes.Buffer
  1019  	for i := start; i <= end; i++ {
  1020  		buf.WriteString(strconv.FormatInt(int64(i), 10))
  1021  		if i < end {
  1022  			buf.WriteRune(',')
  1023  		}
  1024  	}
  1025  	return buf.String()
  1026  }
  1027  
  1028  // generateOnCalendarSchedules converts a schedule into OnCalendar schedules
  1029  // suitable for use in systemd *.timer units using systemd.time(7)
  1030  // https://www.freedesktop.org/software/systemd/man/systemd.time.html
  1031  // XXX: old systemd versions do not support x..y ranges
  1032  func generateOnCalendarSchedules(schedule []*timeutil.Schedule) []string {
  1033  	calendarEvents := make([]string, 0, len(schedule))
  1034  	for _, sched := range schedule {
  1035  		days := make([]string, 0, len(sched.WeekSpans))
  1036  		for _, week := range sched.WeekSpans {
  1037  			abbrev := strings.Join(makeAbbrevWeekdays(week.Start.Weekday, week.End.Weekday), ",")
  1038  
  1039  			if week.Start.Pos == timeutil.EveryWeek && week.End.Pos == timeutil.EveryWeek {
  1040  				// eg: mon, mon-fri, fri-mon
  1041  				days = append(days, fmt.Sprintf("%s *-*-*", abbrev))
  1042  				continue
  1043  			}
  1044  			// examples:
  1045  			// mon1 - Mon *-*-1..7 (Monday during the first 7 days)
  1046  			// fri1 - Fri *-*-1..7 (Friday during the first 7 days)
  1047  
  1048  			// entries below will make systemd timer expire more
  1049  			// frequently than the schedule suggests, however snap
  1050  			// runner evaluates current time and gates the actual
  1051  			// action
  1052  			//
  1053  			// mon1-tue - *-*-1..7 *-*-8 (anchored at first
  1054  			// Monday; Monday happens during the 7 days,
  1055  			// Tuesday can possibly happen on the 8th day if
  1056  			// the month started on Tuesday)
  1057  			//
  1058  			// mon-tue1 - *-*~1 *-*-1..7 (anchored at first
  1059  			// Tuesday; matching Monday can happen on the
  1060  			// last day of previous month if Tuesday is the
  1061  			// 1st)
  1062  			//
  1063  			// mon5-tue - *-*~1..7 *-*-1 (anchored at last
  1064  			// Monday, the matching Tuesday can still happen
  1065  			// within the last 7 days, or on the 1st of the
  1066  			// next month)
  1067  			//
  1068  			// fri4-mon - *-*-22-31 *-*-1..7 (anchored at 4th
  1069  			// Friday, can span onto the next month, extreme case in
  1070  			// February when 28th is Friday)
  1071  			//
  1072  			// XXX: since old versions of systemd, eg. 229 available
  1073  			// in 16.04 does not support x..y ranges, days need to
  1074  			// be enumerated like so:
  1075  			// Mon *-*-1..7 -> Mon *-*-1,2,3,4,5,6,7
  1076  			//
  1077  			// XXX: old systemd versions do not support the last n
  1078  			// days syntax eg, *-*~1, thus the range needs to be
  1079  			// generated in more verbose way like so:
  1080  			// Mon *-*~1..7 -> Mon *-*-22,23,24,25,26,27,28,29,30,31
  1081  			// (22-28 is the last week, but the month can have
  1082  			// anywhere from 28 to 31 days)
  1083  			//
  1084  			startPos := week.Start.Pos
  1085  			endPos := startPos
  1086  			if !week.AnchoredAtStart() {
  1087  				startPos = week.End.Pos
  1088  				endPos = startPos
  1089  			}
  1090  			startDay := (startPos-1)*7 + 1
  1091  			endDay := (endPos) * 7
  1092  
  1093  			if week.IsSingleDay() {
  1094  				// single day, can use the 'weekday' filter
  1095  				if startPos == timeutil.LastWeek {
  1096  					// last week of a month, which can be
  1097  					// 22-28 in case of February, while
  1098  					// month can have between 28 and 31 days
  1099  					days = append(days,
  1100  						fmt.Sprintf("%s *-*-%s", abbrev, daysRange(22, 31)))
  1101  				} else {
  1102  					days = append(days,
  1103  						fmt.Sprintf("%s *-*-%s", abbrev, daysRange(startDay, endDay)))
  1104  				}
  1105  				continue
  1106  			}
  1107  
  1108  			if week.AnchoredAtStart() {
  1109  				// explore the edge cases first
  1110  				switch startPos {
  1111  				case timeutil.LastWeek:
  1112  					// starts in the last week of the month and
  1113  					// possibly spans into the first week of the
  1114  					// next month;
  1115  					// month can have between 28 and 31
  1116  					// days
  1117  					days = append(days,
  1118  						// trailing 29-31 that are not part of a full week
  1119  						fmt.Sprintf("*-*-%s", daysRange(29, 31)),
  1120  						fmt.Sprintf("*-*-%s", daysRange(1, 7)))
  1121  				case 4:
  1122  					// a range in the 4th week can span onto
  1123  					// the next week, which is either 28-31
  1124  					// or in extreme case (eg. February with
  1125  					// 28 days) 1-7 of the next month
  1126  					days = append(days,
  1127  						// trailing 29-31 that are not part of a full week
  1128  						fmt.Sprintf("*-*-%s", daysRange(29, 31)),
  1129  						fmt.Sprintf("*-*-%s", daysRange(1, 7)))
  1130  				default:
  1131  					// can possibly spill into the next week
  1132  					days = append(days,
  1133  						fmt.Sprintf("*-*-%s", daysRange(startDay+7, endDay+7)))
  1134  				}
  1135  
  1136  				if startDay < 28 {
  1137  					days = append(days,
  1138  						fmt.Sprintf("*-*-%s", daysRange(startDay, endDay)))
  1139  				} else {
  1140  					// from the end of the month
  1141  					days = append(days,
  1142  						fmt.Sprintf("*-*-%s", daysRange(startDay-7, endDay-7)))
  1143  				}
  1144  			} else {
  1145  				switch endPos {
  1146  				case timeutil.LastWeek:
  1147  					// month can have between 28 and 31
  1148  					// days, add trailing 29-31 that are not
  1149  					// part of a full week
  1150  					days = append(days, fmt.Sprintf("*-*-%s", daysRange(29, 31)))
  1151  				case 1:
  1152  					// possibly spans from the last week of the
  1153  					// previous month and ends in the first week of
  1154  					// current month
  1155  					days = append(days, fmt.Sprintf("*-*-%s", daysRange(22, 31)))
  1156  				default:
  1157  					// can possibly spill into the previous week
  1158  					days = append(days,
  1159  						fmt.Sprintf("*-*-%s", daysRange(startDay-7, endDay-7)))
  1160  				}
  1161  				if endDay < 28 {
  1162  					days = append(days,
  1163  						fmt.Sprintf("*-*-%s", daysRange(startDay, endDay)))
  1164  				} else {
  1165  					days = append(days,
  1166  						fmt.Sprintf("*-*-%s", daysRange(startDay-7, endDay-7)))
  1167  				}
  1168  			}
  1169  		}
  1170  
  1171  		if len(days) == 0 {
  1172  			// no weekday spec, meaning the timer runs every day
  1173  			days = []string{"*-*-*"}
  1174  		}
  1175  
  1176  		startTimes := make([]string, 0, len(sched.ClockSpans))
  1177  		for _, clocks := range sched.ClockSpans {
  1178  			// use expanded clock spans
  1179  			for _, span := range clocks.ClockSpans() {
  1180  				when := span.Start
  1181  				if span.Spread {
  1182  					length := span.End.Sub(span.Start)
  1183  					if length < 0 {
  1184  						// span Start wraps around, so we have '00:00.Sub(23:45)'
  1185  						length = -length
  1186  					}
  1187  					if length > 5*time.Minute {
  1188  						// replicate what timeutil.Next() does
  1189  						// and cut some time at the end of the
  1190  						// window so that events do not happen
  1191  						// directly one after another
  1192  						length -= 5 * time.Minute
  1193  					}
  1194  					when = when.Add(randutil.RandomDuration(length))
  1195  				}
  1196  				if when.Hour == 24 {
  1197  					// 24:00 for us means the other end of
  1198  					// the day, for systemd we need to
  1199  					// adjust it to the 0-23 hour range
  1200  					when.Hour -= 24
  1201  				}
  1202  
  1203  				startTimes = append(startTimes, when.String())
  1204  			}
  1205  		}
  1206  
  1207  		for _, day := range days {
  1208  			if len(startTimes) == 0 {
  1209  				// current schedule is days only
  1210  				calendarEvents = append(calendarEvents, day)
  1211  				continue
  1212  			}
  1213  
  1214  			for _, startTime := range startTimes {
  1215  				calendarEvents = append(calendarEvents, fmt.Sprintf("%s %s", day, startTime))
  1216  			}
  1217  		}
  1218  	}
  1219  	return calendarEvents
  1220  }
  1221  
  1222  type RestartServicesFlags struct {
  1223  	Reload bool
  1224  }
  1225  
  1226  // Restart or reload services; if reload flag is set then "systemctl reload-or-restart" is attempted.
  1227  func RestartServices(svcs []*snap.AppInfo, flags *RestartServicesFlags, inter interacter, tm timings.Measurer) error {
  1228  	sysd := systemd.New(dirs.GlobalRootDir, systemd.SystemMode, inter)
  1229  
  1230  	for _, srv := range svcs {
  1231  		// they're *supposed* to be all services, but checking doesn't hurt
  1232  		if !srv.IsService() {
  1233  			continue
  1234  		}
  1235  
  1236  		var err error
  1237  		timings.Run(tm, "restart-service", fmt.Sprintf("restart service %q", srv), func(nested timings.Measurer) {
  1238  			if flags != nil && flags.Reload {
  1239  				err = sysd.ReloadOrRestart(srv.ServiceName())
  1240  			} else {
  1241  				// note: stop followed by start, not just 'restart'
  1242  				err = sysd.Restart(srv.ServiceName(), 5*time.Second)
  1243  			}
  1244  		})
  1245  		if err != nil {
  1246  			// there is nothing we can do about failed service
  1247  			return err
  1248  		}
  1249  	}
  1250  	return nil
  1251  }
  1252  
  1253  // QueryDisabledServices returns a list of all currently disabled snap services
  1254  // in the snap.
  1255  func QueryDisabledServices(info *snap.Info, pb progress.Meter) ([]string, error) {
  1256  	// save the list of services that are in the disabled state before unlinking
  1257  	// and thus removing the snap services
  1258  	snapSvcStates, err := ServicesEnableState(info, pb)
  1259  	if err != nil {
  1260  		return nil, err
  1261  	}
  1262  
  1263  	disabledSnapSvcs := []string{}
  1264  	// add all disabled services to the list
  1265  	for svc, isEnabled := range snapSvcStates {
  1266  		if !isEnabled {
  1267  			disabledSnapSvcs = append(disabledSnapSvcs, svc)
  1268  		}
  1269  	}
  1270  
  1271  	// sort for easier testing
  1272  	sort.Strings(disabledSnapSvcs)
  1273  
  1274  	return disabledSnapSvcs, nil
  1275  }