examples, oneshot services rework
Signed-off-by: fbt <fbt@fleshless.org>
This commit is contained in:
parent
5506a922c6
commit
e227235f46
|
@ -8,6 +8,9 @@ Services
|
||||||
A service is a script in the ssm's init.d directory.
|
A service is a script in the ssm's init.d directory.
|
||||||
By default it's /etc/ssm/services for system services and $XDG_CONFIG_HOME/ssm/services for user ones.
|
By default it's /etc/ssm/services for system services and $XDG_CONFIG_HOME/ssm/services for user ones.
|
||||||
|
|
||||||
|
Note that they are BASH scripts that get sourced by `ssm`. Same pitfalls apply.
|
||||||
|
Hopefully, you won't have to do complex logic in the services.
|
||||||
|
|
||||||
## Global settings
|
## Global settings
|
||||||
|
|
||||||
* `cgroups = 0` — Enable cgroup-related features.
|
* `cgroups = 0` — Enable cgroup-related features.
|
||||||
|
@ -41,4 +44,4 @@ Optional settings (incomplete list):
|
||||||
* `service_oneshot = no` — The service is supposed to do something and die instead of daemonizing.
|
* `service_oneshot = no` — The service is supposed to do something and die instead of daemonizing.
|
||||||
* `service_signals_passthru` — Array, empty by default. Which signals should the svc pass directly to the service mainpid. This is dangerous, use with caution.
|
* `service_signals_passthru` — Array, empty by default. Which signals should the svc pass directly to the service mainpid. This is dangerous, use with caution.
|
||||||
|
|
||||||
Quote your expansions btw. This is still BASH.
|
Mind your expansions.
|
||||||
|
|
11
examples/services/iptables
Executable file
11
examples/services/iptables
Executable file
|
@ -0,0 +1,11 @@
|
||||||
|
#!/usr/bin/env ssm
|
||||||
|
|
||||||
|
service_oneshot = true
|
||||||
|
service_command = /usr/bin/false
|
||||||
|
|
||||||
|
service::pre_start() {
|
||||||
|
cat <<- EOF
|
||||||
|
Please don't. Use ferm or any other wrapper.
|
||||||
|
You will just reinvent one in here anyway.
|
||||||
|
EOF
|
||||||
|
}
|
8
examples/services/nginx
Executable file
8
examples/services/nginx
Executable file
|
@ -0,0 +1,8 @@
|
||||||
|
#!/usr/bin/env ssm
|
||||||
|
|
||||||
|
service_respawn = on-failure
|
||||||
|
service_command = /usr/bin/nginx
|
||||||
|
service_pidfile = /run/nginx.pid
|
||||||
|
|
||||||
|
# Do not reload the service if the config test fails.
|
||||||
|
pre_reload() { "$service_command" -t "$@"; }
|
9
examples/services/rc.local
Executable file
9
examples/services/rc.local
Executable file
|
@ -0,0 +1,9 @@
|
||||||
|
#!/usr/bin/env ssm
|
||||||
|
|
||||||
|
service_type = oneshot
|
||||||
|
service_command = /etc/rc.local
|
||||||
|
|
||||||
|
pre_start() {
|
||||||
|
# Do nothing, successfully, if /etc/rc.local is not executable.
|
||||||
|
[[ -x "/etc/rc.local" ]] || die 0
|
||||||
|
}
|
10
examples/services/sshd
Executable file
10
examples/services/sshd
Executable file
|
@ -0,0 +1,10 @@
|
||||||
|
#!/usr/bin/env ssm
|
||||||
|
|
||||||
|
service_respawn = always
|
||||||
|
service_command = /usr/bin/sshd -D -f "/etc/ssh/sshd_config"
|
||||||
|
|
||||||
|
pre_start() {
|
||||||
|
if ! [[ -e "/etc/ssh/ssh_host_key" ]]; then
|
||||||
|
ssh-keygen -A
|
||||||
|
fi
|
||||||
|
}
|
7
examples/services/tinc
Executable file
7
examples/services/tinc
Executable file
|
@ -0,0 +1,7 @@
|
||||||
|
#!/usr/bin/env ssm
|
||||||
|
|
||||||
|
# This trick was neat back in 2011, I swear
|
||||||
|
var tinc_network = "${service_name##*-}"
|
||||||
|
|
||||||
|
service_respawn = on-failure
|
||||||
|
service_command = /usr/bin/tincd -D -n "$tinc_network"
|
11
examples/ssm.conf
Normal file
11
examples/ssm.conf
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
# This is actually a bash script
|
||||||
|
# Everything in ssm is a bash script.
|
||||||
|
|
||||||
|
# Enable cgroup-related functions.
|
||||||
|
# This requires quite specific setup that is undocumented as of yet, sorry.
|
||||||
|
#cgroups = 0
|
||||||
|
|
||||||
|
# You can also set service-level options here, as local defaults.
|
||||||
|
# I really do not advise doing that, but you *can*.
|
||||||
|
# For example:
|
||||||
|
#service_workdir = /
|
88
ssm
88
ssm
|
@ -189,7 +189,7 @@ svc() {
|
||||||
|
|
||||||
rm -f "$svc_pidfile" "$service_pidfile" "$service_ready_flag"
|
rm -f "$svc_pidfile" "$service_pidfile" "$service_ready_flag"
|
||||||
|
|
||||||
die 0
|
die "$job_exit"
|
||||||
}; trap 'svc::cleanup' TERM
|
}; trap 'svc::cleanup' TERM
|
||||||
|
|
||||||
svc::reload() {
|
svc::reload() {
|
||||||
|
@ -229,11 +229,7 @@ svc() {
|
||||||
|
|
||||||
# Wait for the process to exit and record the exit code
|
# Wait for the process to exit and record the exit code
|
||||||
# This depends on a few things
|
# This depends on a few things
|
||||||
if service_managed; then
|
if service_own_pidfile; then
|
||||||
printf '%s' "$job_pid" > "$service_pidfile"
|
|
||||||
|
|
||||||
wait "$job_pid"; job_exit=$?
|
|
||||||
else
|
|
||||||
# We need to wait for the service to write down its pidfile
|
# We need to wait for the service to write down its pidfile
|
||||||
until service_pidfile is file; do
|
until service_pidfile is file; do
|
||||||
(( counter >= service_pidfile_timeout*10 )) && {
|
(( counter >= service_pidfile_timeout*10 )) && {
|
||||||
|
@ -246,10 +242,16 @@ svc() {
|
||||||
|
|
||||||
read -r job_pid < "$service_pidfile"
|
read -r job_pid < "$service_pidfile"
|
||||||
|
|
||||||
# We consider any termination of an unmanaged service to be a failure
|
# We consider any termination of a service with its own pidfile
|
||||||
|
# to be a failure
|
||||||
anywait "$job_pid"; job_exit=127
|
anywait "$job_pid"; job_exit=127
|
||||||
|
else
|
||||||
|
printf '%s' "$job_pid" > "$service_pidfile"
|
||||||
|
|
||||||
|
wait "$job_pid"; job_exit=$?
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# One service failure, two service failures...
|
||||||
if service_success_exit u "$job_exit"; then
|
if service_success_exit u "$job_exit"; then
|
||||||
job_success = 1
|
job_success = 1
|
||||||
(( fail_counter )) && fail_counter--
|
(( fail_counter )) && fail_counter--
|
||||||
|
@ -258,16 +260,17 @@ svc() {
|
||||||
fail_counter++
|
fail_counter++
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Record the exit code
|
||||||
printf '%s' "$job_exit" > "$service_exit_file"
|
printf '%s' "$job_exit" > "$service_exit_file"
|
||||||
|
|
||||||
# Back off if the service exits too much AND too quickly.
|
# Back off if the service exits too much AND too quickly.
|
||||||
service_respawn_force || {
|
if ! service_respawn_force; then
|
||||||
if (( fail_counter >= 3 )); then
|
if (( fail_counter >= 3 )); then
|
||||||
printf -v date '%(%s)T'
|
printf -v date '%(%s)T'
|
||||||
|
|
||||||
(( (date - last_respawn) <= 5 )) && break
|
(( (date - last_respawn) <= 5 )) && break
|
||||||
fi
|
fi
|
||||||
}
|
fi
|
||||||
|
|
||||||
# Respawn, if necessary
|
# Respawn, if necessary
|
||||||
service_respawn_flag || break
|
service_respawn_flag || break
|
||||||
|
@ -419,25 +422,25 @@ start() {
|
||||||
depend "${service_depends[@]}" || return 7
|
depend "${service_depends[@]}" || return 7
|
||||||
depend_ready "${service_depends_ready[@]}" || return 7
|
depend_ready "${service_depends_ready[@]}" || return 7
|
||||||
|
|
||||||
mktmpfiles || return 13
|
|
||||||
|
|
||||||
rm -f "$service_stopped_flag"
|
rm -f "$service_stopped_flag"
|
||||||
|
|
||||||
|
mktmpfiles || return 13
|
||||||
|
|
||||||
|
svc "${service_command[@]}" & job=$!
|
||||||
|
|
||||||
if service_oneshot; then
|
if service_oneshot; then
|
||||||
spawn "${service_command[@]}"; res=$?
|
wait "$job"; res=$?
|
||||||
|
|
||||||
(( res )) && {
|
(( res )) && {
|
||||||
printf '%s' "$res" > "$service_exit_file"
|
printf '%s' "$res" > "$service_exit_file"
|
||||||
return "$res"
|
return 17
|
||||||
}
|
}
|
||||||
printf '1' > "$service_enabled_flag"
|
fi
|
||||||
else
|
|
||||||
svc "${service_command[@]}" &
|
|
||||||
|
|
||||||
if timer "$service_ready_timeout" run_service_action 'ready'; then
|
if timer "$service_ready_timeout" run_service_action 'ready'; then
|
||||||
set_ready
|
set_ready
|
||||||
else
|
else
|
||||||
return 5
|
return 5
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
@ -457,10 +460,7 @@ reload() {
|
||||||
stop() {
|
stop() {
|
||||||
if service_oneshot; then
|
if service_oneshot; then
|
||||||
service_enabled || return 3
|
service_enabled || return 3
|
||||||
|
return 7
|
||||||
rm -f "$service_enabled_flag"
|
|
||||||
|
|
||||||
return 0
|
|
||||||
else
|
else
|
||||||
service_running || return 3
|
service_running || return 3
|
||||||
|
|
||||||
|
@ -499,12 +499,12 @@ info() {
|
||||||
var _type = 'daemon'
|
var _type = 'daemon'
|
||||||
var _info_items
|
var _info_items
|
||||||
|
|
||||||
service_oneshot && {
|
if service_oneshot; then
|
||||||
_status_label = 'Enabled'
|
_status_label = 'Enabled'
|
||||||
_type = 'oneshot'
|
service_enabled && _status = 'yes'
|
||||||
}
|
else
|
||||||
|
service_running && _status = 'yes'
|
||||||
status && _status = 'yes'
|
fi
|
||||||
|
|
||||||
_info_items = \
|
_info_items = \
|
||||||
"Name" "$service_name" \
|
"Name" "$service_name" \
|
||||||
|
@ -584,7 +584,6 @@ var service_pid \
|
||||||
service_logfile_out \
|
service_logfile_out \
|
||||||
service_logfile_err \
|
service_logfile_err \
|
||||||
service_ready_flag \
|
service_ready_flag \
|
||||||
service_enabled_flag \
|
|
||||||
service_stopped_flag \
|
service_stopped_flag \
|
||||||
service_exit_file \
|
service_exit_file \
|
||||||
service_exit_last \
|
service_exit_last \
|
||||||
|
@ -638,7 +637,7 @@ var cgroups = 0 # Enable cgroup-related functions
|
||||||
var usrdir = '/usr/share/ssm'
|
var usrdir = '/usr/share/ssm'
|
||||||
|
|
||||||
# These are not
|
# These are not
|
||||||
var service_managed = 1
|
var service_own_pidfile = 0
|
||||||
var service_oneshot = 0
|
var service_oneshot = 0
|
||||||
var service_running = 0
|
var service_running = 0
|
||||||
var service_enabled = 0
|
var service_enabled = 0
|
||||||
|
@ -769,7 +768,6 @@ readonly service_name
|
||||||
|
|
||||||
# These depend on the service_name and make little sense to reconfigure.
|
# These depend on the service_name and make little sense to reconfigure.
|
||||||
service_ready_flag := "$rundir/$service_name.ready"
|
service_ready_flag := "$rundir/$service_name.ready"
|
||||||
service_enabled_flag := "$rundir/$service_name.enabled"
|
|
||||||
service_stopped_flag := "$rundir/$service_name.stopped"
|
service_stopped_flag := "$rundir/$service_name.stopped"
|
||||||
service_exit_file := "$rundir/$service_name.exit"
|
service_exit_file := "$rundir/$service_name.exit"
|
||||||
service_cgroup_name := "$service_name"
|
service_cgroup_name := "$service_name"
|
||||||
|
@ -801,8 +799,12 @@ if ! service_respawn == 'no'; then
|
||||||
esac
|
esac
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Unset the managed flag on services with their own pidfile
|
if service_respawn_flag && service_oneshot; then
|
||||||
service_pidfile && service_managed = 0
|
die 89 'Cowardly refusing to respawn a oneshot service'
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Catch services with their own pidfile, set the appropriate flag.
|
||||||
|
service_pidfile && service_own_pidfile = 1
|
||||||
|
|
||||||
# Semi-hardcoded stuff
|
# Semi-hardcoded stuff
|
||||||
svc_pidfile = "$rundir/$service_name.svc_pid"
|
svc_pidfile = "$rundir/$service_name.svc_pid"
|
||||||
|
@ -851,12 +853,6 @@ if svc_pidfile is file; then
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Maybe the service is enabled?
|
|
||||||
if service_enabled_flag is file; then
|
|
||||||
# Yay, it is!
|
|
||||||
service_enabled = 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Let's see if the service was deliberately stopped
|
# Let's see if the service was deliberately stopped
|
||||||
if service_stopped_flag is file; then
|
if service_stopped_flag is file; then
|
||||||
# Ooh, it was.
|
# Ooh, it was.
|
||||||
|
@ -867,7 +863,9 @@ fi
|
||||||
if service_exit_file is file; then
|
if service_exit_file is file; then
|
||||||
read -r service_exit_last < "$service_exit_file"
|
read -r service_exit_last < "$service_exit_file"
|
||||||
|
|
||||||
if ! service_success_exit u "$service_exit_last"; then
|
if service_success_exit u "$service_exit_last"; then
|
||||||
|
service_oneshot && service_enabled = 1
|
||||||
|
else
|
||||||
# :(
|
# :(
|
||||||
service_failed = 1
|
service_failed = 1
|
||||||
fi
|
fi
|
||||||
|
@ -904,7 +902,8 @@ case "$2" in
|
||||||
result "$res" \
|
result "$res" \
|
||||||
0 "Stopped $service_name" \
|
0 "Stopped $service_name" \
|
||||||
3 "$service_name is not running" \
|
3 "$service_name is not running" \
|
||||||
5 "Operation timed out"
|
5 "Operation timed out" \
|
||||||
|
7 "Can't “stop” a oneshot service. Use reset-exit if you want to “start” the service again"
|
||||||
;;
|
;;
|
||||||
|
|
||||||
start)
|
start)
|
||||||
|
@ -915,7 +914,8 @@ case "$2" in
|
||||||
7 "Failed to start dependencies for $service_name: ${failed_deps[@]}" \
|
7 "Failed to start dependencies for $service_name: ${failed_deps[@]}" \
|
||||||
9 "service_command does not exist: ${service_command[0]}" \
|
9 "service_command does not exist: ${service_command[0]}" \
|
||||||
13 "Failed to create temporary files for $service_name" \
|
13 "Failed to create temporary files for $service_name" \
|
||||||
15 "Refusing to start $service_name: the service cgroup is not empty and \$service_cgroup_exclusive is set"
|
15 "Refusing to start $service_name: the service cgroup is not empty and \$service_cgroup_exclusive is set" \
|
||||||
|
17 "$service_name failed"
|
||||||
;;
|
;;
|
||||||
|
|
||||||
reload)
|
reload)
|
||||||
|
|
49
ssm.conf
49
ssm.conf
|
@ -1,49 +0,0 @@
|
||||||
# This is actually a bash script
|
|
||||||
|
|
||||||
# Enable cgroup-related functions
|
|
||||||
#cgroups = 0
|
|
||||||
|
|
||||||
# Where to search for services, works as a PATH-like array.
|
|
||||||
#service_path = "$XDG_CONFIG_HOME/ssm/services" /etc/ssm/services "$rundir/services" /usr/share/ssm/services
|
|
||||||
|
|
||||||
# Service defaults
|
|
||||||
|
|
||||||
## Respawn the service if it exits
|
|
||||||
#service_respawn = 0
|
|
||||||
|
|
||||||
## Working directory
|
|
||||||
#service_workdir = '/'
|
|
||||||
|
|
||||||
## How long do we wait for the service to stop
|
|
||||||
#service_stop_timeout = 30
|
|
||||||
|
|
||||||
## How long do we wait for the service to get ready
|
|
||||||
#service_ready_timeout = 15
|
|
||||||
|
|
||||||
## Signals to pass through to the service under the respawner
|
|
||||||
#service_signals = 1 10 12
|
|
||||||
|
|
||||||
## The signal to send to reload the service
|
|
||||||
#service_reload_signal = 1
|
|
||||||
|
|
||||||
## The signal to send to stop the service
|
|
||||||
#service_stop_signal = 15
|
|
||||||
|
|
||||||
## These only work with cgroups = 1:
|
|
||||||
|
|
||||||
## Check if the recorded PID of the service is in the correct cgroup.
|
|
||||||
#service_cgroup_strict = 1
|
|
||||||
|
|
||||||
## Refuse to start the service if its cgroup is not empty
|
|
||||||
#service_cgroup_exclusive = 0
|
|
||||||
|
|
||||||
## Wait on all the members of the cgroup to exit when stopping the service.
|
|
||||||
#service_cgroup_wait = 0
|
|
||||||
|
|
||||||
## Send a kill signal to the members of cgroup when
|
|
||||||
## stopping the service, and the signal to send:
|
|
||||||
#service_cgroup_kill = 0
|
|
||||||
#service_cgroup_kill_signal = 15
|
|
||||||
|
|
||||||
# Clean up the cgroup when the service exits for any reason
|
|
||||||
#service_cgroup_cleanup = 0
|
|
Loading…
Reference in New Issue
Block a user