Packages: nodogsplash 5.0.2 (on libmicrohttpd without TLS, as OpenWrt's no-ssl variant), openwrt-nodogsplash (init script, stock config, uci-defaults adding the firewall include, restart.sh detached), iptables-nft/cross (Guix's iptables-nft points at an x86 iptables in cross builds). nodogsplash-service-type: openwrt-init-script, seed with `enabled' off by default, mutable /etc/nodogsplash/htdocs, ndsctl and iptables in the profile. firewall-service-type forks fw4 outside the service context instead of waiting for it (includes may call herd). openwrt-rc idles when the init script opens no instance, instead of exiting into Shepherd's respawn limit; the init.d shims enable before start/restart. Verified: splash redirect, ndsctl auth/deauth, firewall reload with the portal running (PITFALLS #46). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
142 lines
4.4 KiB
Bash
Executable File
142 lines
4.4 KiB
Bash
Executable File
#!/bin/sh
|
|
# openwrt-rc: run an OpenWrt procd init script (USE_PROCD=1) without procd,
|
|
# for one Shepherd service. The script is sourced with procd's functions
|
|
# stubbed; the first instance it opens is the one that runs.
|
|
#
|
|
# NAME is the service's OpenWrt name (/etc/init.d/NAME), SCRIPT the file:
|
|
# openwrt-rc NAME SCRIPT exec start_service, then exec the instance's
|
|
# command (Shepherd's process)
|
|
# openwrt-rc NAME SCRIPT reload PID
|
|
# reload_service (or start_service again);
|
|
# if the instance changed -- command, env,
|
|
# contents of its `file's -- SIGTERM PID
|
|
# (Shepherd respawns it), as procd restarts
|
|
# changed instances
|
|
# openwrt-rc NAME SCRIPT print print the instance's command line
|
|
#
|
|
# Shepherd must only fork this, never wait for it (PITFALLS #43): scripts
|
|
# call ubus, udhcpc, logger...
|
|
name="$1" SCRIPT="$2" MODE="$3" PID="$4"
|
|
STATE=/var/run/openwrt-rc
|
|
initscript="$SCRIPT"
|
|
|
|
. @base@/lib/functions.sh
|
|
|
|
_rc_log() { logger -t "openwrt-rc" "$name: $*"; }
|
|
|
|
# Shell-quote one argument.
|
|
_rc_q() { printf "'%s'" "$(printf '%s' "$1" | sed "s/'/'\\\\''/g")"; }
|
|
|
|
_rc_instances=0 _rc_cur=0
|
|
_rc_instance= _rc_cmd= _rc_env= _rc_files= _rc_user=
|
|
|
|
procd_open_instance() {
|
|
_rc_instances=$((_rc_instances + 1))
|
|
_rc_cur=$_rc_instances
|
|
[ "$_rc_cur" = 1 ] && _rc_instance="${1:-instance1}"
|
|
return 0
|
|
}
|
|
|
|
_rc_param() {
|
|
local append="$1" key="$2" a
|
|
shift 2
|
|
[ "$_rc_cur" = 1 ] || return 0
|
|
case "$key" in
|
|
command)
|
|
[ "$append" = 1 ] || _rc_cmd=
|
|
for a in "$@"; do _rc_cmd="$_rc_cmd $(_rc_q "$a")"; done ;;
|
|
env)
|
|
[ "$append" = 1 ] || _rc_env=
|
|
for a in "$@"; do _rc_env="$_rc_env $(_rc_q "$a")"; done ;;
|
|
file)
|
|
[ "$append" = 1 ] || _rc_files=
|
|
_rc_files="$_rc_files $*" ;;
|
|
user)
|
|
_rc_user="$1" ;;
|
|
esac
|
|
return 0
|
|
}
|
|
procd_set_param() { _rc_param 0 "$@"; }
|
|
procd_append_param() { _rc_param 1 "$@"; }
|
|
procd_close_instance() { _rc_cur=0; }
|
|
|
|
# procd_send_signal SERVICE [INSTANCE] [SIGNAL]
|
|
procd_send_signal() {
|
|
[ -n "$PID" ] && kill -"${3:-HUP}" "$PID" 2>/dev/null
|
|
return 0
|
|
}
|
|
procd_running() { [ -n "$PID" ] && kill -0 "$PID" 2>/dev/null; }
|
|
procd_kill() { [ -n "$PID" ] && kill "$PID" 2>/dev/null; return 0; }
|
|
rc_procd() { "$@"; }
|
|
|
|
# Triggers, jails, data, validation: procd features with no counterpart
|
|
# here (reload triggers come from procd-compat, hotplug from netifd).
|
|
for f in procd_open_service procd_close_service procd_open_trigger \
|
|
procd_close_trigger procd_add_reload_trigger procd_add_config_trigger \
|
|
procd_add_interface_trigger procd_add_reload_interface_trigger \
|
|
procd_add_raw_trigger procd_add_restart_mount_trigger \
|
|
procd_add_reload_mount_trigger procd_add_action_mount_trigger \
|
|
procd_add_jail procd_add_jail_mount procd_add_jail_mount_rw \
|
|
procd_set_config_changed procd_open_data procd_close_data \
|
|
procd_open_validate procd_close_validate procd_add_validation \
|
|
procd_add_mdns procd_add_mdns_service procd_add_instance \
|
|
procd_add_service_data; do
|
|
eval "$f() { return 0; }"
|
|
done
|
|
|
|
. "$SCRIPT"
|
|
|
|
_rc_signature() {
|
|
{
|
|
echo "$_rc_cmd"; echo "$_rc_env"; echo "$_rc_user"
|
|
for f in $_rc_files; do [ -f "$f" ] && md5sum < "$f"; done
|
|
} | md5sum | cut -d' ' -f1
|
|
}
|
|
|
|
case "$MODE" in
|
|
exec|print)
|
|
start_service ;;
|
|
reload)
|
|
if type reload_service 2>/dev/null | grep -q function; then
|
|
reload_service
|
|
else
|
|
start_service
|
|
fi ;;
|
|
*)
|
|
echo "usage: $0 NAME SCRIPT exec|reload PID|print" >&2
|
|
exit 1 ;;
|
|
esac
|
|
|
|
if [ -z "$_rc_cmd" ]; then
|
|
# No instance (disabled in its UCI config, say): procd keeps such a
|
|
# service with no instances. Idle, so Shepherd doesn't respawn us into
|
|
# its respawn limit; a reload that finds an instance restarts us.
|
|
case "$MODE" in
|
|
exec)
|
|
_rc_log "no instance to run; idle until a reload finds one"
|
|
mkdir -p "$STATE"
|
|
_rc_signature > "$STATE/$name"
|
|
exec sleep 2147483647 ;;
|
|
print)
|
|
exit 1 ;;
|
|
esac
|
|
fi
|
|
[ "$_rc_instances" -gt 1 ] &&
|
|
_rc_log "$_rc_instances instances; only the first ($_rc_instance) runs"
|
|
|
|
case "$MODE" in
|
|
exec)
|
|
mkdir -p "$STATE"
|
|
_rc_signature > "$STATE/$name"
|
|
[ -n "$_rc_user" ] && [ "$_rc_user" != root ] &&
|
|
_rc_log "procd user '$_rc_user' not applied: runs as root"
|
|
eval "exec env $_rc_env $_rc_cmd" ;;
|
|
reload)
|
|
if [ "$(_rc_signature)" != "$(cat "$STATE/$name" 2>/dev/null)" ]; then
|
|
_rc_log "instance changed: restarting"
|
|
kill -TERM "$PID" 2>/dev/null
|
|
fi ;;
|
|
print)
|
|
echo "${_rc_env:+env$_rc_env}$_rc_cmd" ;;
|
|
esac
|