summaryrefslogtreecommitdiff
diff options
authorMichael Vogt <michael.vogt@gmail.com>2018-04-16 11:41:09 +0200
committerGitHub <noreply@github.com>2018-04-16 11:41:09 +0200
commit75dfe705e2b1eed9c608dcca39d71773946b954e (patch)
tree4b4dcfb6e24f628fd1deb1d5a46483c380d3660d
parentf3d16a317616b1aa37a95c229159cc58f2b09a1f (diff)
parent1ca0f37350dc77665d8de502ea778ac059589656 (diff)
Merge pull request #5050 from mvo5/snap-refresh-mode-fixes-2.32
many: add "stop-mode: sig{term,hup,usr[12]}{,-all}" instead of conflating that with refresh-mode #5043
-rw-r--r--snap/info.go28
-rw-r--r--snap/info_snap_yaml.go2
-rw-r--r--snap/info_test.go38
-rw-r--r--snap/validate.go11
-rw-r--r--snap/validate_test.go37
-rwxr-xr-xtests/lib/snaps/test-snapd-service/bin/reload2
-rwxr-xr-xtests/lib/snaps/test-snapd-service/bin/start-refresh-mode11
-rwxr-xr-xtests/lib/snaps/test-snapd-service/bin/start-stop-mode11
-rwxr-xr-xtests/lib/snaps/test-snapd-service/bin/start-stop-mode-sigterm8
-rwxr-xr-xtests/lib/snaps/test-snapd-service/bin/stop-stop-mode (renamed from tests/lib/snaps/test-snapd-service/bin/stop-refresh-mode)0
-rw-r--r--tests/lib/snaps/test-snapd-service/meta/snap.yaml64
-rw-r--r--tests/main/snap-service-refresh-mode/task.yaml37
-rw-r--r--tests/main/snap-service-stop-mode-sigkill/task.yaml37
-rw-r--r--tests/main/snap-service-stop-mode/task.yaml54
-rw-r--r--wrappers/services.go60
-rw-r--r--wrappers/services_gen_test.go21
-rw-r--r--wrappers/services_test.go28
17 files changed, 333 insertions, 116 deletions
diff --git a/snap/info.go b/snap/info.go
index 63ba061506..c179347fc1 100644
--- a/snap/info.go
+++ b/snap/info.go
@@ -537,6 +537,33 @@ type TimerInfo struct {
Timer string
}
+// StopModeType is the type for the "stop-mode:" of a snap app
+type StopModeType string
+
+// KillAll returns if the stop-mode means all processes should be killed
+// when the service is stopped or just the main process.
+func (st StopModeType) KillAll() bool {
+ return string(st) == "" || strings.HasSuffix(string(st), "-all")
+}
+
+// KillSignal returns the signal that should be used to kill the process
+// (or an empty string if no signal is needed)
+func (st StopModeType) KillSignal() string {
+ if st == "" {
+ return ""
+ }
+ return strings.ToUpper(strings.TrimSuffix(string(st), "-all"))
+}
+
+func (st StopModeType) Valid() error {
+ switch st {
+ case "", "sigterm", "sigterm-all", "sighup", "sighup-all", "sigusr1", "sigusr1-all", "sigusr2", "sigusr2-all":
+ // valid
+ return nil
+ }
+ return fmt.Errorf(`"stop-mode" field contains invalid value %q`, st)
+}
+
// AppInfo provides information about a app.
type AppInfo struct {
Snap *Info
@@ -553,6 +580,7 @@ type AppInfo struct {
RestartCond RestartCondition
Completer string
RefreshMode string
+ StopMode StopModeType
// TODO: this should go away once we have more plumbing and can change
// things vs refactor
diff --git a/snap/info_snap_yaml.go b/snap/info_snap_yaml.go
index 3f074a7d65..112f9af079 100644
--- a/snap/info_snap_yaml.go
+++ b/snap/info_snap_yaml.go
@@ -68,6 +68,7 @@ type appYaml struct {
StopTimeout timeout.Timeout `yaml:"stop-timeout,omitempty"`
Completer string `yaml:"completer,omitempty"`
RefreshMode string `yaml:"refresh-mode,omitempty"`
+ StopMode StopModeType `yaml:"stop-mode,omitempty"`
RestartCond RestartCondition `yaml:"restart-condition,omitempty"`
SlotNames []string `yaml:"slots,omitempty"`
@@ -300,6 +301,7 @@ func setAppsFromSnapYaml(y snapYaml, snap *Info) error {
Environment: yApp.Environment,
Completer: yApp.Completer,
RefreshMode: yApp.RefreshMode,
+ StopMode: yApp.StopMode,
Before: yApp.Before,
After: yApp.After,
Autostart: yApp.Autostart,
diff --git a/snap/info_test.go b/snap/info_test.go
index 51849c4c40..52e4e0c531 100644
--- a/snap/info_test.go
+++ b/snap/info_test.go
@@ -927,3 +927,41 @@ func (s *infoSuite) TestExpandSnapVariables(c *C) {
c.Assert(info.ExpandSnapVariables("$SNAP_COMMON/stuff"), Equals, "/var/snap/foo/common/stuff")
c.Assert(info.ExpandSnapVariables("$GARBAGE/rocks"), Equals, "/rocks")
}
+
+func (s *infoSuite) TestStopModeTypeKillMode(c *C) {
+ for _, t := range []struct {
+ stopMode string
+ killall bool
+ }{
+ {"", true},
+ {"sigterm", false},
+ {"sigterm-all", true},
+ {"sighup", false},
+ {"sighup-all", true},
+ {"sigusr1", false},
+ {"sigusr1-all", true},
+ {"sigusr2", false},
+ {"sigusr2-all", true},
+ } {
+ c.Check(snap.StopModeType(t.stopMode).KillAll(), Equals, t.killall, Commentf("wrong KillAll for %v", t.stopMode))
+ }
+}
+
+func (s *infoSuite) TestStopModeTypeKillSignal(c *C) {
+ for _, t := range []struct {
+ stopMode string
+ killSig string
+ }{
+ {"", ""},
+ {"sigterm", "SIGTERM"},
+ {"sigterm-all", "SIGTERM"},
+ {"sighup", "SIGHUP"},
+ {"sighup-all", "SIGHUP"},
+ {"sigusr1", "SIGUSR1"},
+ {"sigusr1-all", "SIGUSR1"},
+ {"sigusr2", "SIGUSR2"},
+ {"sigusr2-all", "SIGUSR2"},
+ } {
+ c.Check(snap.StopModeType(t.stopMode).KillSignal(), Equals, t.killSig)
+ }
+}
diff --git a/snap/validate.go b/snap/validate.go
index bff6ee205a..2909432288 100644
--- a/snap/validate.go
+++ b/snap/validate.go
@@ -534,13 +534,20 @@ func ValidateApp(app *AppInfo) error {
return err
}
- // validate refresh-mode
+ // validate stop-mode
+ if err := app.StopMode.Valid(); err != nil {
+ return err
+ }
+ // validate stop-mode
switch app.RefreshMode {
- case "", "endure", "restart", "sigterm", "sigterm-all", "sighup", "sighup-all", "sigusr1", "sigusr1-all", "sigusr2", "sigusr2-all":
+ case "", "endure", "restart":
// valid
default:
return fmt.Errorf(`"refresh-mode" field contains invalid value %q`, app.RefreshMode)
}
+ if app.StopMode != "" && app.Daemon == "" {
+ return fmt.Errorf(`"stop-mode" cannot be used for %q, only for services`, app.Name)
+ }
if app.RefreshMode != "" && app.Daemon == "" {
return fmt.Errorf(`"refresh-mode" cannot be used for %q, only for services`, app.Name)
}
diff --git a/snap/validate_test.go b/snap/validate_test.go
index 36e975c8d3..384ed2fe5c 100644
--- a/snap/validate_test.go
+++ b/snap/validate_test.go
@@ -415,16 +415,14 @@ func (s *ValidateSuite) TestAppDaemonValue(c *C) {
}
}
-func (s *ValidateSuite) TestAppRefreshMode(c *C) {
+func (s *ValidateSuite) TestAppStopMode(c *C) {
// check services
for _, t := range []struct {
- refresh string
- ok bool
+ stopMode string
+ ok bool
}{
// good
{"", true},
- {"endure", true},
- {"restart", true},
{"sigterm", true},
{"sigterm-all", true},
{"sighup", true},
@@ -437,9 +435,34 @@ func (s *ValidateSuite) TestAppRefreshMode(c *C) {
{"invalid-thing", false},
} {
if t.ok {
- c.Check(ValidateApp(&AppInfo{Name: "foo", Daemon: "simple", RefreshMode: t.refresh}), IsNil)
+ c.Check(ValidateApp(&AppInfo{Name: "foo", Daemon: "simple", StopMode: StopModeType(t.stopMode)}), IsNil)
+ } else {
+ c.Check(ValidateApp(&AppInfo{Name: "foo", Daemon: "simple", StopMode: StopModeType(t.stopMode)}), ErrorMatches, fmt.Sprintf(`"stop-mode" field contains invalid value %q`, t.stopMode))
+ }
+ }
+
+ // non-services cannot have a stop-mode
+ err := ValidateApp(&AppInfo{Name: "foo", Daemon: "", StopMode: "sigterm"})
+ c.Check(err, ErrorMatches, `"stop-mode" cannot be used for "foo", only for services`)
+}
+
+func (s *ValidateSuite) TestAppRefreshMode(c *C) {
+ // check services
+ for _, t := range []struct {
+ refreshMode string
+ ok bool
+ }{
+ // good
+ {"", true},
+ {"endure", true},
+ {"restart", true},
+ // bad
+ {"invalid-thing", false},
+ } {
+ if t.ok {
+ c.Check(ValidateApp(&AppInfo{Name: "foo", Daemon: "simple", RefreshMode: t.refreshMode}), IsNil)
} else {
- c.Check(ValidateApp(&AppInfo{Name: "foo", Daemon: "simple", RefreshMode: t.refresh}), ErrorMatches, fmt.Sprintf(`"refresh-mode" field contains invalid value %q`, t.refresh))
+ c.Check(ValidateApp(&AppInfo{Name: "foo", Daemon: "simple", RefreshMode: t.refreshMode}), ErrorMatches, fmt.Sprintf(`"refresh-mode" field contains invalid value %q`, t.refreshMode))
}
}
diff --git a/tests/lib/snaps/test-snapd-service/bin/reload b/tests/lib/snaps/test-snapd-service/bin/reload
index e04f606e14..4e14f5aa90 100755
--- a/tests/lib/snaps/test-snapd-service/bin/reload
+++ b/tests/lib/snaps/test-snapd-service/bin/reload
@@ -5,4 +5,4 @@ if [ -z "$MAINPID" ]; then
exit 1
fi
echo "reloading main PID: $MAINPID"
-kill -HUP $MAINPID
+kill -HUP "$MAINPID"
diff --git a/tests/lib/snaps/test-snapd-service/bin/start-refresh-mode b/tests/lib/snaps/test-snapd-service/bin/start-refresh-mode
deleted file mode 100755
index dd14f4bd97..0000000000
--- a/tests/lib/snaps/test-snapd-service/bin/start-refresh-mode
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/bin/bash
-
-trap "echo got sigusr1" USR1
-trap "echo got sigusr2" USR2
-trap "echo got sighup" HUP
-
-while true; do
- echo "running $1 process"
- sleep 1
-done
-
diff --git a/tests/lib/snaps/test-snapd-service/bin/start-stop-mode b/tests/lib/snaps/test-snapd-service/bin/start-stop-mode
new file mode 100755
index 0000000000..57c2c67504
--- /dev/null
+++ b/tests/lib/snaps/test-snapd-service/bin/start-stop-mode
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+SIG=0
+trap "echo got sigusr1; SIG=1" USR1
+trap "echo got sigusr2; SIG=1" USR2
+trap "echo got sighup; SIG=1" HUP
+
+while [ "$SIG" = "0" ]; do
+ echo "running $1 process"
+ sleep 1
+done
diff --git a/tests/lib/snaps/test-snapd-service/bin/start-stop-mode-sigterm b/tests/lib/snaps/test-snapd-service/bin/start-stop-mode-sigterm
new file mode 100755
index 0000000000..e1465aa7ac
--- /dev/null
+++ b/tests/lib/snaps/test-snapd-service/bin/start-stop-mode-sigterm
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+set -e
+
+echo "start-refresh-mode-sigkill"
+
+echo "running a process"
+sleep 3133731337
diff --git a/tests/lib/snaps/test-snapd-service/bin/stop-refresh-mode b/tests/lib/snaps/test-snapd-service/bin/stop-stop-mode
index 16f1c0ccf0..16f1c0ccf0 100755
--- a/tests/lib/snaps/test-snapd-service/bin/stop-refresh-mode
+++ b/tests/lib/snaps/test-snapd-service/bin/stop-stop-mode
diff --git a/tests/lib/snaps/test-snapd-service/meta/snap.yaml b/tests/lib/snaps/test-snapd-service/meta/snap.yaml
index 77501a5d4d..f457127fbe 100644
--- a/tests/lib/snaps/test-snapd-service/meta/snap.yaml
+++ b/tests/lib/snaps/test-snapd-service/meta/snap.yaml
@@ -9,48 +9,46 @@ apps:
test-snapd-other-service:
command: bin/start-other
daemon: simple
- test-snapd-endure-service:
- command: bin/start-refresh-mode endure
- stop-command: bin/stop-refresh-mode endure
- daemon: simple
- refresh-mode: endure
- test-snapd-sigterm-service:
- command: bin/start-refresh-mode sigterm
- stop-command: bin/stop-refresh-mode sigterm
- daemon: simple
- refresh-mode: sigterm
- test-snapd-sigterm-all-service:
- command: bin/start-refresh-mode sigterm-all
- stop-command: bin/stop-refresh-mode sigterm-all
- daemon: simple
- refresh-mode: sigterm
test-snapd-sighup-service:
- command: bin/start-refresh-mode sighup
- stop-command: bin/stop-refresh-mode sighup
+ command: bin/start-stop-mode sighup
+ stop-command: bin/stop-stop-mode sighup
daemon: simple
- refresh-mode: sighup
+ stop-mode: sighup
test-snapd-sighup-all-service:
- command: bin/start-refresh-mode sighup-all
- stop-command: bin/stop-refresh-mode sighup-all
+ command: bin/start-stop-mode sighup-all
+ stop-command: bin/stop-stop-mode sighup-all
daemon: simple
- refresh-mode: sighup-all
+ stop-mode: sighup-all
test-snapd-sigusr1-service:
- command: bin/start-refresh-mode sigusr1
- stop-command: bin/stop-refresh-mode sigusr1
+ command: bin/start-stop-mode sigusr1
+ stop-command: bin/stop-stop-mode sigusr1
daemon: simple
- refresh-mode: sigusr1
+ stop-mode: sigusr1
test-snapd-sigusr1-all-service:
- command: bin/start-refresh-mode sigusr1-all
- stop-command: bin/stop-refresh-mode sigusr1-all
+ command: bin/start-stop-mode sigusr1-all
+ stop-command: bin/stop-stop-mode sigusr1-all
daemon: simple
- refresh-mode: sigusr1-all
+ stop-mode: sigusr1-all
test-snapd-sigusr2-service:
- command: bin/start-refresh-mode sigusr2
- stop-command: bin/stop-refresh-mode sigusr2
+ command: bin/start-stop-mode sigusr2
+ stop-command: bin/stop-stop-mode sigusr2
daemon: simple
- refresh-mode: sigusr2
+ stop-mode: sigusr2
test-snapd-sigusr2-all-service:
- command: bin/start-refresh-mode sigusr2-all
- stop-command: bin/stop-refresh-mode sigusr2-all
+ command: bin/start-stop-mode sigusr2-all
+ stop-command: bin/stop-stop-mode sigusr2-all
daemon: simple
- refresh-mode: sigusr2-all
+ stop-mode: sigusr2-all
+ test-snapd-sigterm-service:
+ command: bin/start-stop-mode-sigterm
+ daemon: simple
+ stop-mode: sigterm
+ test-snapd-sigterm-all-service:
+ command: bin/start-stop-mode-sigterm
+ daemon: simple
+ stop-mode: sigterm-all
+ test-snapd-endure-service:
+ command: bin/start-stop-mode endure
+ stop-command: bin/stop-stop-mode endure
+ daemon: simple
+ refresh-mode: endure
diff --git a/tests/main/snap-service-refresh-mode/task.yaml b/tests/main/snap-service-refresh-mode/task.yaml
index b6dfa43afc..f71d1e6543 100644
--- a/tests/main/snap-service-refresh-mode/task.yaml
+++ b/tests/main/snap-service-refresh-mode/task.yaml
@@ -3,7 +3,8 @@ summary: "Check that refresh-modes works"
kill-timeout: 3m
debug: |
- journalctl -u snap.test-snapd-service.test-snapd-endure-service
+ grep -n '' *.pid || true
+ systemctl status snap.test-snapd-service.test-snapd-endure-service || true
execute: |
echo "When the service snap is installed"
@@ -12,35 +13,19 @@ execute: |
echo "We can see it running"
systemctl status snap.test-snapd-service.test-snapd-endure-service|MATCH "running"
+ systemctl show -p MainPID snap.test-snapd-service.test-snapd-endure-service > old-main.pid
echo "When it is re-installed"
install_local test-snapd-service
- # note that we do not spread test sigterm{,-all} because catching this
- # signal in the service means the stop will timeout with a 90s delay
- refresh_modes="endure sighup sighup-all sigusr1 sigusr1-all sigusr2 sigusr2-all"
- for s in $refresh_modes; do
- echo "We can still see it running"
- systemctl status snap.test-snapd-service.test-snapd-${s}-service|MATCH "running"
-
- echo "And it is not re-started"
- if journalctl -u snap.test-snapd-service.test-snapd-${s}-service | grep "stop ${s}"; then
- echo "reinstall did stop a service that shouldn't"
- exit 1
- fi
-
- if [[ "$s" == sig* ]]; then
- echo "checking that the right signal was sent"
- sleep 1
- journalctl -u snap.test-snapd-service.test-snapd-${s}-service | MATCH "got ${s%%-all}"
- fi
- done
-
- echo "But regular services are restarted"
- journalctl -u snap.test-snapd-service.test-snapd-service | MATCH "stop service"
+ echo "We can still see it running with the same PID"
+ systemctl show -p MainPID snap.test-snapd-service.test-snapd-endure-service > new-main.pid
+
+ test "$(cat new-endure.pid)" = "$(cat old-endure.pid)"
echo "Once the snap is removed, the service is stopped"
snap remove test-snapd-service
- for s in $refresh_modes; do
- journalctl | MATCH "stop ${s}"
- done
+ journalctl | MATCH "stop endure"
+
+restore:
+ rm -f *.pid || true
diff --git a/tests/main/snap-service-stop-mode-sigkill/task.yaml b/tests/main/snap-service-stop-mode-sigkill/task.yaml
new file mode 100644
index 0000000000..0d0bd4619c
--- /dev/null
+++ b/tests/main/snap-service-stop-mode-sigkill/task.yaml
@@ -0,0 +1,37 @@
+summary: "Check that refresh-modes sigkill works"
+
+kill-timeout: 3m
+
+restore: |
+ # remove to ensure all services are stopped
+ snap remove test-snapd-service || true
+ killall sleep || true
+
+debug: |
+ ps afx
+
+execute: |
+ echo "Ensure no stray sleep processes are around"
+ killall sleep || true
+
+ echo "When the service snap is installed"
+ . $TESTSLIB/snaps.sh
+ install_local test-snapd-service
+
+ refresh_modes="sigterm sigterm-all"
+ for s in $refresh_modes; do
+ systemctl show -p ActiveState snap.test-snapd-service.test-snapd-${s}-service | MATCH "ActiveState=active"
+ done
+
+ echo "we expect two sleep processes (children) from the two sigterm services"
+ n=$(ps afx | grep [3]133731337 | grep -v grep | wc -l)
+ [ "$n" = "2" ]
+
+ echo "When it is re-installed one process uses sigterm, the other sigterm-all"
+ install_local test-snapd-service
+
+ echo "After reinstall the sigterm-all service and all children got killed"
+ echo "but the sigterm service only got a kill for the main process "
+ echo "and one sleep is still alive"
+ n=$(ps afx | grep [3]133731337 | wc -l)
+ [ "$n" = "3" ]
diff --git a/tests/main/snap-service-stop-mode/task.yaml b/tests/main/snap-service-stop-mode/task.yaml
new file mode 100644
index 0000000000..2a8f3ccdd5
--- /dev/null
+++ b/tests/main/snap-service-stop-mode/task.yaml
@@ -0,0 +1,54 @@
+summary: "Check that stop-modes works"
+
+kill-timeout: 3m
+
+# journald in ubuntu-14.04 not reliable
+systems: [-ubuntu-14.04-*]
+
+debug: |
+ stop_modes="sighup sighup-all sigusr1 sigusr1-all sigusr2 sigusr2-all"
+ for s in $stop_modes; do
+ systemctl status snap.test-snapd-service.test-snapd-${s}-service || true
+ done
+
+execute: |
+ echo "When the service snap is installed"
+ . $TESTSLIB/snaps.sh
+ install_local test-snapd-service
+
+ echo "We can see it running"
+ systemctl status snap.test-snapd-service.test-snapd-service|MATCH "running"
+
+ stop_modes="sighup sighup-all sigusr1 sigusr1-all sigusr2 sigusr2-all"
+ for s in $stop_modes; do
+ systemctl show -p ActiveState snap.test-snapd-service.test-snapd-${s}-service | MATCH "ActiveState=active"
+ done
+
+ echo "When it is re-installed"
+ install_local test-snapd-service
+
+ # note that sigterm{,-all} is tested separately
+ for s in $stop_modes; do
+ echo "We can see it is running"
+ systemctl show -p ActiveState snap.test-snapd-service.test-snapd-${s}-service | MATCH "ActiveState=active"
+
+ echo "and it got the right signal"
+ echo "checking that the right signal was sent"
+ sleep 1
+ journalctl -u snap.test-snapd-service.test-snapd-${s}-service | MATCH "got ${s%%-all}"
+ done
+
+ echo "Regular services are restarted normally"
+ journalctl -u snap.test-snapd-service.test-snapd-service | MATCH "stop service"
+
+ echo "Once the snap is removed, all services are stopped"
+ snap remove test-snapd-service
+ for s in $stop_modes; do
+ journalctl | MATCH "stop ${s}"
+ done
+
+restore: |
+ rm -f *.pid || true
+ # remove to ensure all services are stopped
+ snap remove test-snapd-service || true
+ killall sleep || true
diff --git a/wrappers/services.go b/wrappers/services.go
index 90aa09f32b..bdcbb679fe 100644
--- a/wrappers/services.go
+++ b/wrappers/services.go
@@ -230,45 +230,30 @@ func StopServices(apps []*snap.AppInfo, reason snap.ServiceStopReason, inter int
if !app.IsService() || !osutil.FileExists(app.ServiceFile()) {
continue
}
- // Skip stop on refresh when refresh mode is set to something
+ // Skip stop on refresh when stop mode is set to something
// other than "restart" (or "" which is the same)
if reason == snap.StopReasonRefresh {
- logger.Debugf(" %s refresh-mode: %v", app.Name, app.RefreshMode)
+ logger.Debugf(" %s stop-mode: %v", app.Name, app.StopMode)
switch app.RefreshMode {
case "endure":
// skip this service
continue
- case "sigterm":
- sysd.Kill(app.ServiceName(), "TERM", "main")
- continue
- case "sigterm-all":
- sysd.Kill(app.ServiceName(), "TERM", "all")
- continue
- case "sighup":
- sysd.Kill(app.ServiceName(), "HUP", "main")
- continue
- case "sighup-all":
- sysd.Kill(app.ServiceName(), "HUP", "all")
- continue
- case "sigusr1":
- sysd.Kill(app.ServiceName(), "USR1", "main")
- continue
- case "sigusr1-all":
- sysd.Kill(app.ServiceName(), "USR1", "all")
- continue
- case "sigusr2":
- sysd.Kill(app.ServiceName(), "USR2", "main")
- continue
- case "sigusr2-all":
- sysd.Kill(app.ServiceName(), "USR2", "all")
- continue
- case "", "restart":
- // do nothing here, the default below to stop
}
}
if err := stopService(sysd, app, inter); err != nil {
return err
}
+
+ // ensure the service is really stopped on remove regardless
+ // of stop-mode
+ if reason == snap.StopReasonRemove && !app.StopMode.KillAll() {
+ // FIXME: make this smarter and avoid the killWait
+ // delay if not needed (i.e. if all processes
+ // have died)
+ sysd.Kill(app.ServiceName(), "TERM", "all")
+ time.Sleep(killWait)
+ sysd.Kill(app.ServiceName(), "KILL", "")
+ }
}
return nil
@@ -367,6 +352,12 @@ RemainAfterExit={{.Remain}}
{{- if .App.BusName}}
BusName={{.App.BusName}}
{{- end}}
+{{- if .KillMode}}
+KillMode={{.KillMode}}
+{{- end}}
+{{- if .KillSignal}}
+KillSignal={{.KillSignal}}
+{{- end}}
{{- if not .App.Sockets}}
[Install]
@@ -391,6 +382,10 @@ WantedBy={{.ServicesTarget}}
remain = "yes"
}
}
+ var killMode string
+ if !appInfo.StopMode.KillAll() {
+ killMode = "process"
+ }
wrapperData := struct {
App *snap.AppInfo
@@ -401,6 +396,8 @@ WantedBy={{.ServicesTarget}}
PrerequisiteTarget string
MountUnit string
Remain string
+ KillMode string
+ KillSignal string
Before []string
After []string
@@ -415,8 +412,11 @@ WantedBy={{.ServicesTarget}}
PrerequisiteTarget: systemd.PrerequisiteTarget,
MountUnit: filepath.Base(systemd.MountUnitPath(appInfo.Snap.MountDir())),
Remain: remain,
- Before: genServiceNames(appInfo.Snap, appInfo.Before),
- After: genServiceNames(appInfo.Snap, appInfo.After),
+ KillMode: killMode,
+ KillSignal: appInfo.StopMode.KillSignal(),
+
+ Before: genServiceNames(appInfo.Snap, appInfo.Before),
+ After: genServiceNames(appInfo.Snap, appInfo.After),
// 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 9d24e9e8e2..21edd7a089 100644
--- a/wrappers/services_gen_test.go
+++ b/wrappers/services_gen_test.go
@@ -333,3 +333,24 @@ WantedBy=multi-user.target
c.Logf("service: \n%v\n", string(generatedWrapper))
c.Assert(string(generatedWrapper), Equals, expectedService)
}
+
+func (s *servicesWrapperGenSuite) TestKillModeSig(c *C) {
+ for _, rm := range []string{"sigterm", "sighup", "sigusr1", "sigusr2"} {
+ service := &snap.AppInfo{
+ Snap: &snap.Info{
+ SuggestedName: "snap",
+ Version: "0.3.4",
+ SideInfo: snap.SideInfo{Revision: snap.R(44)},
+ },
+ Name: "app",
+ Command: "bin/foo start",
+ Daemon: "simple",
+ StopMode: snap.StopModeType(rm),
+ }
+
+ generatedWrapper, err := wrappers.GenerateSnapServiceFile(service)
+ c.Assert(err, IsNil)
+
+ c.Assert(string(generatedWrapper), testutil.Contains, "\nKillMode=process\n")
+ }
+}
diff --git a/wrappers/services_test.go b/wrappers/services_test.go
index b33216fa40..1c010925fa 100644
--- a/wrappers/services_test.go
+++ b/wrappers/services_test.go
@@ -652,6 +652,9 @@ func (s *servicesTestSuite) TestStopServiceSigs(c *C) {
})
defer r()
+ r = wrappers.MockKillWait(1 * time.Millisecond)
+ defer r()
+
survivorFile := filepath.Join(s.tempdir, "/etc/systemd/system/snap.survive-snap.srv.service")
for _, t := range []struct {
mode string
@@ -672,7 +675,7 @@ version: 1.0
apps:
srv:
command: bin/survivor
- refresh-mode: %s
+ stop-mode: %s
daemon: simple
`, t.mode)
info := snaptest.MockSnap(c, surviveYaml, &snap.SideInfo{Revision: snap.R(1)})
@@ -689,16 +692,29 @@ apps:
err = wrappers.StopServices(info.Services(), snap.StopReasonRefresh, progress.Null)
c.Assert(err, IsNil)
c.Check(sysdLog, DeepEquals, [][]string{
- {"kill", filepath.Base(survivorFile), "-s", t.expectedSig, "--kill-who=" + t.expectedWho},
+ {"stop", filepath.Base(survivorFile)},
+ {"show", "--property=ActiveState", "snap.survive-snap.srv.service"},
}, Commentf("failure in %s", t.mode))
sysdLog = nil
err = wrappers.StopServices(info.Services(), snap.StopReasonRemove, progress.Null)
c.Assert(err, IsNil)
- c.Check(sysdLog, DeepEquals, [][]string{
- {"stop", filepath.Base(survivorFile)},
- {"show", "--property=ActiveState", "snap.survive-snap.srv.service"},
- })
+ switch t.expectedWho {
+ case "all":
+ c.Check(sysdLog, DeepEquals, [][]string{
+ {"stop", filepath.Base(survivorFile)},
+ {"show", "--property=ActiveState", "snap.survive-snap.srv.service"},
+ })
+ case "main":
+ c.Check(sysdLog, DeepEquals, [][]string{
+ {"stop", filepath.Base(survivorFile)},
+ {"show", "--property=ActiveState", "snap.survive-snap.srv.service"},
+ {"kill", filepath.Base(survivorFile), "-s", "TERM", "--kill-who=all"},
+ {"kill", filepath.Base(survivorFile), "-s", "KILL", "--kill-who=all"},
+ })
+ default:
+ panic("not reached")
+ }
}
}