examples, oneshot services rework

Signed-off-by: fbt <fbt@fleshless.org>
This commit is contained in:
Jack L. Frost 2018-03-09 02:31:14 +03:00
parent 5506a922c6
commit e227235f46
9 changed files with 104 additions and 94 deletions

View File

@ -8,6 +8,9 @@ Services
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.
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
* `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_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
View 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
View 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
View 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
View 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
View 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
View 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
View File

@ -189,7 +189,7 @@ svc() {
rm -f "$svc_pidfile" "$service_pidfile" "$service_ready_flag"
die 0
die "$job_exit"
}; trap 'svc::cleanup' TERM
svc::reload() {
@ -229,11 +229,7 @@ svc() {
# Wait for the process to exit and record the exit code
# This depends on a few things
if service_managed; then
printf '%s' "$job_pid" > "$service_pidfile"
wait "$job_pid"; job_exit=$?
else
if service_own_pidfile; then
# We need to wait for the service to write down its pidfile
until service_pidfile is file; do
(( counter >= service_pidfile_timeout*10 )) && {
@ -246,10 +242,16 @@ svc() {
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
else
printf '%s' "$job_pid" > "$service_pidfile"
wait "$job_pid"; job_exit=$?
fi
# One service failure, two service failures...
if service_success_exit u "$job_exit"; then
job_success = 1
(( fail_counter )) && fail_counter--
@ -258,16 +260,17 @@ svc() {
fail_counter++
fi
# Record the exit code
printf '%s' "$job_exit" > "$service_exit_file"
# Back off if the service exits too much AND too quickly.
service_respawn_force || {
if ! service_respawn_force; then
if (( fail_counter >= 3 )); then
printf -v date '%(%s)T'
(( (date - last_respawn) <= 5 )) && break
fi
}
fi
# Respawn, if necessary
service_respawn_flag || break
@ -419,25 +422,25 @@ start() {
depend "${service_depends[@]}" || return 7
depend_ready "${service_depends_ready[@]}" || return 7
mktmpfiles || return 13
rm -f "$service_stopped_flag"
mktmpfiles || return 13
svc "${service_command[@]}" & job=$!
if service_oneshot; then
spawn "${service_command[@]}"; res=$?
wait "$job"; res=$?
(( res )) && {
printf '%s' "$res" > "$service_exit_file"
return "$res"
return 17
}
printf '1' > "$service_enabled_flag"
else
svc "${service_command[@]}" &
fi
if timer "$service_ready_timeout" run_service_action 'ready'; then
set_ready
else
return 5
fi
if timer "$service_ready_timeout" run_service_action 'ready'; then
set_ready
else
return 5
fi
return 0
@ -457,10 +460,7 @@ reload() {
stop() {
if service_oneshot; then
service_enabled || return 3
rm -f "$service_enabled_flag"
return 0
return 7
else
service_running || return 3
@ -499,12 +499,12 @@ info() {
var _type = 'daemon'
var _info_items
service_oneshot && {
if service_oneshot; then
_status_label = 'Enabled'
_type = 'oneshot'
}
status && _status = 'yes'
service_enabled && _status = 'yes'
else
service_running && _status = 'yes'
fi
_info_items = \
"Name" "$service_name" \
@ -584,7 +584,6 @@ var service_pid \
service_logfile_out \
service_logfile_err \
service_ready_flag \
service_enabled_flag \
service_stopped_flag \
service_exit_file \
service_exit_last \
@ -638,7 +637,7 @@ var cgroups = 0 # Enable cgroup-related functions
var usrdir = '/usr/share/ssm'
# These are not
var service_managed = 1
var service_own_pidfile = 0
var service_oneshot = 0
var service_running = 0
var service_enabled = 0
@ -769,7 +768,6 @@ readonly service_name
# These depend on the service_name and make little sense to reconfigure.
service_ready_flag := "$rundir/$service_name.ready"
service_enabled_flag := "$rundir/$service_name.enabled"
service_stopped_flag := "$rundir/$service_name.stopped"
service_exit_file := "$rundir/$service_name.exit"
service_cgroup_name := "$service_name"
@ -801,8 +799,12 @@ if ! service_respawn == 'no'; then
esac
fi
# Unset the managed flag on services with their own pidfile
service_pidfile && service_managed = 0
if service_respawn_flag && service_oneshot; then
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
svc_pidfile = "$rundir/$service_name.svc_pid"
@ -851,12 +853,6 @@ if svc_pidfile is file; then
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
if service_stopped_flag is file; then
# Ooh, it was.
@ -867,7 +863,9 @@ fi
if service_exit_file is file; then
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
fi
@ -904,7 +902,8 @@ case "$2" in
result "$res" \
0 "Stopped $service_name" \
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)
@ -915,7 +914,8 @@ case "$2" in
7 "Failed to start dependencies for $service_name: ${failed_deps[@]}" \
9 "service_command does not exist: ${service_command[0]}" \
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)

View File

@ -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