(2025-07-14) Taming the daemons, the POSIX way ---------------------------------------------- As you might know, I am also running a fully self-hosted Nex station along with some additional services on my Orange Pi Zero. The way I'm exposing the ports of those services relies on the SSH functionality along with some config tweaks on both sides, maybe I'm gonna cover it the other time. For now though, I'd like to focus on a different aspect of that setup: service management. Of course, I could wrap all the scripts into systemd units but I don't think that's portable enough, and I'd have to move those units into a place I definitely would forget about at some point. So, up until today, I had individual start/stop scripts for each individual service on that Orange. But then, I found all of them too similar to each other and thought that maybe, just maybe, all my custom daemon management could be ruled from a single place. Thus, the DaemonTamer was born. You can download it from the "Downloads" section of this Gopherhole but here it is, in its entirety: #!/bin/sh # DaemonTamer: simple and portable local service manager in pure sh # that allows to quickly daemonize any command and manage it as a service # Depends on POSIX versions of sh, nohup, cut and grep # the unreg option also depends on mktemp (not POSIX but present everywhere) # Run sh dt help to see the parameters # Created by Luxferre in 2025, released into public domain with no warranties err() { echo $* exit 1 } find_service() { sline="$(grep -E "^$1\s+" $CONF)" [ -z "$sline" ] && err "No such service found in the config!" echo "$sline" } CONF="$HOME/.dt.conf" srv_name="$2" [ "$1" != "help" ] && [ -z "$srv_name" ] && err "Service name is required!" pidfile="/tmp/$srv_name.pid" case $1 in help) echo 'DaemonTamer v1' echo "Usage: $0 {reg|unreg|start|stop|restart} SERVICE_NAME [REG_PARAMS]" echo 'Parameters for the reg subcommand:' echo "$0 reg 'COMMAND' [WORKDIR] [STDOUT_LOG_FILE] [STDERR_LOG_FILE]" echo '(the COMMAND must be specified in single quotes and without nohup)' echo "\nCreated by Luxferre in 2025, released into public domain with no warranties" ;; reg) srv_cmd="$3" [ -z "$srv_cmd" ] && err "Service runner command is required!" srv_wdir="$4" srv_outlog="$5" srv_errlog="$6" [ -z "$srv_wdir" ] && srv_wdir='none' [ -z "$srv_outlog" ] && srv_outlog='/dev/null' [ -z "$srv_errlog" ] && srv_errlog='/dev/null' printf "%s\t%s\t%s\t%s\t%s\n" "$srv_name" "$srv_cmd" "$srv_wdir" \ "$srv_outlog" "$srv_errlog" >> $CONF echo "Service $srv_name registered" ;; unreg) srvline="$(find_service "$srv_name")" TCONF="$(mktemp)" grep -v "$srv_name" $CONF > $TCONF && mv $TCONF $CONF echo "Service $srv_name unregistered" ;; start) srvline="$(find_service "$srv_name")" srv_cmd="$(echo "$srvline" | cut -f2)" srv_wdir="$(echo "$srvline" | cut -f3)" srv_outlog="$(echo "$srvline" | cut -f4)" srv_errlog="$(echo "$srvline" | cut -f5)" echo "Starting service $srv_name" echo "- command: $srv_cmd" echo "- workdir: $srv_wdir" echo "- stdout: $srv_outlog" echo "- stderr: $srv_errlog" [ "$srv_wdir" != "none" ] && cd "$srv_wdir" nohup $srv_cmd >$srv_outlog 2>$srv_errlog & echo $! > $pidfile echo "Service $srv_name started" ;; stop) srvline="$(find_service "$srv_name")" echo "Stopping service $srv_name" kill $(cat $pidfile) rm -f $pidfile echo "Service $srv_name stopped" ;; restart) srvline="$(find_service "$srv_name")" $SHELL $0 stop $srv_name $SHELL $0 start $srv_name ;; *) echo 'Invalid operation! Pick start, stop, restart, reg, unreg or help' ;; esac Yeah, that simple. As you can see, you can register any command in a single TSV file to be run as a daemon, and you have the options to specify its working directory and the files to redirect standard output and error streams to. After registering this command, you can also unregister this daemon or start/stop/restart it. This is the bare functionality minimum that I personally need. Maybe I'll also add some pre/post-start and pre/post-stop hooks and some additional checks if the service is already running in v2, but that's really it. The TSV format was intentionally chosen as the simplest one to parse by any shell-based tool, considering the tab character naturally cannot appear in any command, service or file name, and it also is the default one for the "cut" command. Bopher-NG vibes, sort of. The only thing I'd like to ask you is to not perceive this as any kind of statement. This is a tool created for some practical purposes of portability and quick deployment. It can't, won't and doesn't aim to replace the main service manager in your OS. If you try to do that, don't come yelling at me that something doesn't work. Because it probably was never supposed to work the way you expected. In any event, stay tuned for v2, you don't want to miss it. Just kidding. --- Luxferre ---