runit-user-spawn
Following the announcement of dinit-user-spawn and related issues I wondered if something similar existed for runit which I use.
First off I could not find any simple way to run a script at user login/logout. I could use pam.d/system-login to add a session optional pam_exec, but that would not be easily controlled.
I noticed that directories /run/user/uid depend on login/logout so scanning /run/user seems like a way to tell who is logged in.
I wrote this flaky bash script to test the idea, It seems to work when installed as /etc/runit/sv/users/run and enabled. It uses a polling loop which is rather awful. I could possibly use inotifywatch to monitor /run/user for changes which would eliminate the polling loop.
Personally I don't need such a system as I use openbox autostart to run services needed by the desktop. If I felt restarts were required I suppose I could start a runsvdir directly in autostart. I don't think I would need such when lgging in on a console or using ssh.
Anyhow here is the code that works inelegantly
#!/bin/bash
exec >> /tmp/users-run.log 2>&1
echo "#############################################################"
declare -A UD
alive(){
ps -p "$1" >/dev/null 2>&1
}
do_kill(){
echo "do_kill $@"
kill "$1" "$2"
}
cleanup(){
echo "##### cleanup on EXIT"
for k in ${!UD[@]}; do
pid="${UD["$k"]}"
if alive "$pid"; then
do_kill -HUP "$pid"
if alive "$pid"; then
sleep 1
do_kill -TERM "$pid"
sleep 1
if alive "$pid"; then
echo "!!!!! cannot kill runsvdir ($pid)" 1>&2
continue
fi
fi
fi
done
}
trap "cleanup" EXIT
while true; do
sleep 1
#first check deleted
for k in ${!UD[@]}; do
if [ ! -d "/run/user/$k/" ]; then
echo "detected logout of $k"
pid="${UD["$k"]}"
if alive "$pid"; then
user="$(id -nu $k)"
do_kill -HUP "$pid"
sleep 1
if alive "$pid"; then
do_kill -TERM "$pid"
sleep 1
if alive "$pid"; then
echo "!!!!! cannot kill runsvdir ($pid,$user)" 1>&2
continue
fi
fi
fi
unset UD[$k]
fi
done
#now additions
for d in $(ls -d /run/user/*/); do
k="$(basename $d)"
pid="${UD["$k"]}"
if [ -z "$pid" ]; then # || ! alive "$pid"; then
user="$(id -nu $k)"
home="$(getent passwd "$user" | cut -d: -f6)"
svdir="$home/${USER_RUNIT:-.config/runit}/service"
if [ -d "$svdir" ]; then
OUSER="$USER"
OHOME="$HOME"
export USER="$user"
export HOME="$home"
groups="$(id -Gn "$USER" | tr ' ' ':')"
chpst -u "$USER:$groups" -C "$HOME" runsvdir -P "$svdir" ................................................................ &
pid="$!"
UD["$k"]="$pid"
echo "runsvdir ($pid,$user)"
OUSER="$USER"
OHOME="$HOME"
fi
fi
done
done