diff --git a/wrappers/services.go b/wrappers/services.go index 25e204b00ab..1e9872b9a46 100644 --- a/wrappers/services.go +++ b/wrappers/services.go @@ -170,7 +170,7 @@ func RemoveSnapServices(s *snap.Info, inter interacter) error { func genServiceFile(appInfo *snap.AppInfo) string { serviceTemplate := `[Unit] -# Auto-generated, DO NO EDIT +# Auto-generated, DO NOT EDIT Description=Service for snap application {{.App.Snap.Name}}.{{.App.Name}} Requires={{.MountUnit}} Wants={{.PrerequisiteTarget}} @@ -186,6 +186,7 @@ WorkingDirectory={{.App.Snap.DataDir}} {{if .App.PostStopCommand}}ExecStopPost={{.App.LauncherPostStopCommand}}{{end}} {{if .StopTimeout}}TimeoutStopSec={{.StopTimeout.Seconds}}{{end}} Type={{.App.Daemon}} +{{if .Remain}}RemainAfterExit={{.Remain}}{{end}} {{if .App.BusName}}BusName={{.App.BusName}}{{end}} [Install] @@ -204,6 +205,17 @@ WantedBy={{.ServicesTarget}} restartCond = systemd.RestartOnFailure.String() } + var remain string + if appInfo.Daemon == "oneshot" { + // any restart condition other than "no" is invalid for oneshot daemons + restartCond = "no" + // If StopExec is present for a oneshot service than we also need + // RemainAfterExit=yes + if appInfo.StopCommand != "" { + remain = "yes" + } + } + wrapperData := struct { App *snap.AppInfo @@ -212,6 +224,7 @@ WantedBy={{.ServicesTarget}} ServicesTarget string PrerequisiteTarget string MountUnit string + Remain string Home string EnvVars string @@ -223,6 +236,7 @@ WantedBy={{.ServicesTarget}} ServicesTarget: systemd.ServicesTarget, PrerequisiteTarget: systemd.PrerequisiteTarget, MountUnit: filepath.Base(systemd.MountUnitPath(appInfo.Snap.MountDir())), + Remain: remain, // systemd runs as PID 1 so %h will not work. Home: "/root", diff --git a/wrappers/services_gen_test.go b/wrappers/services_gen_test.go index 7b8c68224fd..57dd245a16a 100644 --- a/wrappers/services_gen_test.go +++ b/wrappers/services_gen_test.go @@ -25,6 +25,7 @@ import ( . "gopkg.in/check.v1" "github.com/snapcore/snapd/snap" + "github.com/snapcore/snapd/snap/snaptest" "github.com/snapcore/snapd/systemd" "github.com/snapcore/snapd/timeout" "github.com/snapcore/snapd/wrappers" @@ -35,7 +36,7 @@ type servicesWrapperGenSuite struct{} var _ = Suite(&servicesWrapperGenSuite{}) const expectedServiceFmt = `[Unit] -# Auto-generated, DO NO EDIT +# Auto-generated, DO NOT EDIT Description=Service for snap application snap.app Requires=snap-snap-44.mount Wants=network-online.target @@ -44,7 +45,7 @@ X-Snappy=yes [Service] ExecStart=/usr/bin/snap run snap.app -Restart=on-failure +Restart=%s WorkingDirectory=/var/snap/snap/44 ExecStop=/usr/bin/snap run --command=stop snap.app ExecReload=/usr/bin/snap run --command=reload snap.app @@ -57,13 +58,14 @@ WantedBy=multi-user.target ` var ( - expectedAppService = fmt.Sprintf(expectedServiceFmt, "simple\n") - expectedDbusService = fmt.Sprintf(expectedServiceFmt, "dbus\nBusName=foo.bar.baz") + expectedAppService = fmt.Sprintf(expectedServiceFmt, "on-failure", "simple\n\n") + expectedDbusService = fmt.Sprintf(expectedServiceFmt, "on-failure", "dbus\n\nBusName=foo.bar.baz") + expectedOneshotService = fmt.Sprintf(expectedServiceFmt, "no", "oneshot\nRemainAfterExit=yes\n") ) var ( expectedServiceWrapperFmt = `[Unit] -# Auto-generated, DO NO EDIT +# Auto-generated, DO NOT EDIT Description=Service for snap application xkcd-webserver.xkcd-webserver Requires=snap-xkcd\x2dwebserver-44.mount Wants=network-online.target @@ -81,7 +83,7 @@ TimeoutStopSec=30 Type=%s %s ` - expectedTypeForkingWrapper = fmt.Sprintf(expectedServiceWrapperFmt, "forking\n", "\n[Install]\nWantedBy=multi-user.target") + expectedTypeForkingWrapper = fmt.Sprintf(expectedServiceWrapperFmt, "forking", "\n\n\n[Install]\nWantedBy=multi-user.target") ) func (s *servicesWrapperGenSuite) TestGenerateSnapServiceFile(c *C) { @@ -201,3 +203,26 @@ apps: c.Assert(wrapperText, Equals, expectedDbusService) } + +func (s *servicesWrapperGenSuite) TestGenOneshotServiceFile(c *C) { + + info := snaptest.MockInfo(c, ` +name: snap +version: 1.0 +apps: + app: + command: bin/start + stop-command: bin/stop + reload-command: bin/reload + post-stop-command: bin/stop --post + stop-timeout: 10s + daemon: oneshot +`, &snap.SideInfo{Revision: snap.R(44)}) + + app := info.Apps["app"] + + wrapperText, err := wrappers.GenerateSnapServiceFile(app) + c.Assert(err, IsNil) + + c.Assert(wrapperText, Equals, expectedOneshotService) +}