#!/bin/zsh MAX_LOG=4 CONFIG_DIR="$HOME/.local/share/dewit" PIDS_DIR="/tmp/dewit.$USER" mkdir -pm 700 $CONFIG_DIR mkdir -pm 700 $PIDS_DIR dew_help() { >&2 << 'EOF' dewit ~ User-Level Services A relatively simple script that manages long-running processes for independent users, rather than creating system services that later su to the right user. dewit itself isn't a long-running process. It creates processes with `nohup &` and caches the PID to kill later if needed. Logs are managed on service start, featuring automated compression and overwriting. A "service" is just a shell script, similar to runit's design. If the service script is just a wrapper over another long-running executable, make sure to `exec` it, otherwise it may not get killed properly. It's intended to put "@reboot dewit all start" in your account's crontab. EOF dew_usage >&2 << 'EOF' Configured services: EOF dew_list <<< "" } dew_usage() { >&2 << 'EOF' Usage: dewit [action] Actions: (nothing) Same as just `status`. status [args] Get the service's status, prints its PID to stdout, and runs `tail $args` on its current log. * log [n] `less` the entire current log (n=0) or the nth previous one. archive Archive old logs now instead of at start start [args] Start the service with `args` if it isn't already running. stop Stop the service if it is running. restart [args] Stop the service if it's running, then start it with `args`. * cmd Edit the service with $EDITOR or `vi`. rm Stops & deletes the service. * Commands marked won't work great with "all" services. EOF } dew_list() { for _ in $CONFIG_DIR(NF); do for sv in $CONFIG_DIR/*; do <<< ${sv:t} done 2> /dev/null return 0 done <<< "- No services -" >&2 return 1 } dew_check() { pid=$( < $PIDF ) 2> /dev/null || return 1 ps -p $pid > /dev/null || rm $PIDF || return 1 <<< $pid } dew_start() { dew_log_gz <<< "Starting $NAME $( date )" tee "$CONF/log.0" > /dev/stderr nohup "$CONF/cmd" $@ &>> "$CONF/log.0" & pid=$! <<< $pid > $PIDF sleep 0.1 ps -p $pid > /dev/null } dew_stop() { pid=$( < $PIDF ) 2> /dev/null || return 0 <<< "Stopping $NAME $( date )" tee -a "$CONF/log.0" > /dev/stderr kill $pid sleep 0.1 ! ps -p $pid > /dev/null && rm $PIDF } dew_log_gz() { [[ $MAX_LOG = 0 ]] && return [[ -f "$CONF/log.0" ]] && gzip -fk "$CONF/log.0" && <<< "" > "$CONF/log.0" &> /dev/null for new in {$MAX_LOG..1}; do old=$(( $new - 1 )) mv "$CONF/log.$old.gz" "$CONF/log.$new.gz" &> /dev/null done } dew_main() { CONF="$CONFIG_DIR/$NAME" PIDF="$PIDS_DIR/$NAME" CMD=$1 [[ -z $CMD ]] || shift case $CMD in ("status" | "") [[ ! -d $CONF ]] && <<< "No such service $NAME" >&2 && return 1 dew_check running=$? [[ $running = 0 ]] && <<< "$NAME - Running" >&2 || <<< "$NAME - Stopped" >&2 <<< "" >&2 logfile="$CONF/log.0" if [[ -f "$CONF/log.0" ]]; then tail "$CONF/log.0" $@ else <<< "- No log -" >&2 fi return $running ;; ("log") [[ ! -d $CONF ]] && <<< "No such service $NAME" >&2 && return 1 [[ -z $1 ]] && num=0 || num=$1 if [[ -f "$CONF/log.$num" ]]; then less "$CONF/log.$num" elif [[ -f "$CONF/log.$num.gz" ]]; then gzip -cd "$CONF/log.$num.gz" | less else <<< "- No log -" >&2 fi ;; ("archive") [[ ! -d $CONF ]] && <<< "No such service $NAME" >&2 && return 1 dew_log_gz <<< "Archived logs for $NAME" >&2 ;; ("start") [[ ! -d $CONF ]] && <<< "No such service $NAME" >&2 && return 1 dew_check && <<< "Already running $NAME" >&2 || dew_start $@ || <<< "Process died" ;; ("stop") [[ ! -d $CONF ]] && <<< "No such service $NAME" >&2 && return 1 ! dew_check && <<< "Already stopped $NAME" >&2 || dew_stop || <<< "Couldn't kill process" ;; ("restart") [[ ! -d $CONF ]] && <<< "No such service $NAME" >&2 && return 1 dew_check && ( dew_stop || return 1 ) dew_start $@ ;; ("cmd") mkdir -pm 700 $CONF [[ -z $EDITOR ]] && e=vi || e=$EDITOR if [[ ! -f "$CONF/cmd" ]]; then > "$CONF/cmd" << CMD #!$SHELL exec ... CMD chmod 755 "$CONF/cmd" fi $e "$CONF/cmd" ;; ("rm") [[ ! -d $CONF ]] && <<< "No such service $NAME" >&2 && return 1 dew_stop && rm -r $CONF ;; (*) dew_usage ;; esac } NAME=$1 [[ -z $NAME ]] && dew_help && return 1 shift if [[ $NAME = "all" ]]; then dew_list | while read NAME; do dew_main $@ <<< "" <<< "" done else dew_main $@ fi