Using pcsk to Supervise mythbackend

From MythTV Official Wiki
Revision as of 16:10, 27 November 2007 by Djk (talk | contribs)

Jump to: navigation, search

If mythbackend has ever crashed on you, odds are that you didn't know and you missed out on recording a favourite programme. Here is a nifty rc script I wrote using the default /etc/init.d/mythtv-backend script in Ubuntu 7.04 (Feisty) as a base, but modified to have the following improvements:

1) The Process Creation's Swiss-Army Knife (PCSK) is used to supervise the mythbackend process and respawn it if it dies. Alternatives to pcsk include daemontools and psmon, but I like pcsk as it is small and simple and does exactly what I need.
2) The script is more clever about whether a process is actually running, rather than just checking for the existence of a .pid file (which will never work if the process crashed instead of being deliberately stopped).
3) You can simply run /etc/init.d/mythtv-backend start and know that no matter what the status was previously, mythbackend will be running afterwards (useful if another process or script is used to start mythbackend).
4) Non-root users stand a good chance of being able to determine the status of mythbackend without requiring sudo.
5) Additional status information is provided using Andrew Ruthven's mythtv-status perl script. This isn't essential, but is used if you have it installed.
6) Behaviour of force-reload is clearer

A disadvantage for you may be that this method loses the ability to 'nice' the mythbackend process (eg using /etc/default/mythtv-backend). I wasn't using nice, so it was no problem for me.

PCSK

First, download, compile and install pcsk from http://www.nix.hu/projects/pcsk/. Something along the lines of:

cd
mkdir pcsk
cd pcsk
wget http://downloads.nix.hu/downloads/pcsk/pcsk-0.0.5.tar.bz2
tar xjf pcsk-0.0.5.tar.bz2
cd pcsk-0.0.5
make install

mythtv-backend.pcsk

Then, using your favourite text editor, create the following script in the appropriate place for your distro. For my Ubuntu installation I chose /etc/init.d/mythtv-backend.pcsk.


Script.png /etc/init.d/mythtv-backend.pcsk

#! /bin/sh
### BEGIN INIT INFO
# Provides:          mythtv-backend
# Required-Start:    $local_fs
# Required-Stop:     $local_fs
# Default-Start:     24
# Default-Stop:      S
# Short-Description: Start/Stop the MythTV server.
### END INIT INFO
#
# modified mythtv-backend rc script
# v0.2 by DJK 28th Nov 2007:
#       Added pcsk supervision to restart if it dies
#       Had to lose NICE for now, sorry.  Might be able to use renice sometime in the future?
#       Call mythtv-status script for extra details
#

PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
DAEMON=/usr/bin/mythbackend
NAME="mythbackend"
DESC="MythTV server"

# Check that the mythbackend binary exists
if ! [ -x "$DAEMON" ] ; then
        echo "Could not find $DAEMON binary"
        exit 1
fi

# Check that the pcsk binary exists somewhere in the path (I choose /usr/local/bin)
if ! PCSKBINARY="`which pcsk`" > /dev/null 2>&1; then
        echo "Could not find pcsk binary to supervise $NAME.  Try www.nix.hu/projects/pcsk..."
        exit 1
fi

# Arguments to give to the mythtv-status script
MYTHTVSTATUSARGS="-c"

# Check that the  mythtv-status perl script exists somewhere in the path (I choose /usr/local/bin)
if ! MYTHTVSTATUSBINARY="`which mythtv-status`" > /dev/null 2>&1; then
        # It's not disasterous if it's not there - just let the user know where to find it if they try status
        MYTHTVSTATUSBINARY="echo mythtv-status perl script not found.  Try www.etc.gen.nz/projects/mythtv/mythtv-status.html"
        MYTHTVSTATUSARGS=
fi

set -e

. /lib/lsb/init-functions

USER=mythtv
RUNDIR=/var/run/mythtv
# mythbackend arguments - I've removed --daemon since pcsk will daemonise it for me
ARGS=" --logfile /var/log/mythtv/mythbackend.log --pidfile $RUNDIR/$NAME.pid"
EXTRA_ARGS=""
NICE=0

# The pid and log files for pcsk to use
PCSKPIDFILE="$RUNDIR/$NAME-pcsk.pid"
PCSKLOGFILE="/var/log/mythtv/mythbackend-pcsk.log"

# Command line options for pcsk:
# -r: restart the program if it dies
# -e: log stderr, if there is any
# -o: log stdout, if there is any
# -0: don't restart if the exit code was 0
# -w3: wait 3 sec before restart (default in original script)
# -i10: increment each wait between restarts by 10s
# -c10: restart up to 10 times if the restart is unsuccessful, before giving up
# -m60: restart wasn't successful if the program exits within 60 sec
# -p $PIDFILE: use that file for the pid
# -l $PCSKLOGFILE: log file to keep track of things - good to set up logrotate
PCSKOPTS="-reo0 -w3 -i10 -c10 -m60 -p $PCSKPIDFILE -l $PCSKLOGFILE"
# Check for alternate options for mythtv-backend
if [ -f /etc/default/mythtv-backend ]; then
  . /etc/default/mythtv-backend
fi

ARGS="$ARGS $EXTRA_ARGS"

PCSKARGS="$PCSKOPTS $DAEMON -- $ARGS"

# If the run directory doesn't exist, then create it if we can and chown it to $USER
# I'm assuming if it's already there its permissions and ownership are OK (cheat...)
# This makes it easier for non-root users to still run this script to get status info
if ! [ -d "$RUNDIR" ] ; then
        if [ "`id -un`" = "root" ] ; then
                mkdir -p "$RUNDIR"
                chown -R "$USER" "$RUNDIR"
        else
                echo "Could not create $RUNDIR.  You need to be root to do that.  Try sudo."
                exit 1
        fi
fi

unset DISPLAY
unset SESSION_MANAGER

case "$1" in
  start)
        [ "`id -un`" = "root" ] || {
                echo "Failed.  You need to be root to $1 $DESC.  Try sudo."
                exit 1
        }
        # Check if pcsk is already supervising this process
        # by checking for the existance of a pidfile and that it's a currently running process
        PCSKPID=
        DAEMONPID=
        if [ -e "$PCSKPIDFILE" ] ; then
                read PCSKPID < "$PCSKPIDFILE"
                if [ -n "$PCSKPID" ] && [ -d "/proc/$PCSKPID" ] ; then
                        # pcsk is running, what about our daemon?
                        if [ -e "$RUNDIR/$NAME.pid" ] ; then
                                read DAEMONPID < "$RUNDIR/$NAME.pid"
                                if [ -n "$DAEMONPID" ] && [ -d "/proc/$DAEMONPID" ] ; then
                                        # daemon is running too
                                        echo "pcsk($NAME) (pid $PCSKPID) is already running, supervising $NAME (pid $DAEMONPID) "
                                        echo "Use restart if you need."
                                        exit 1
                                fi
                                # daemon isn't running at the moment (pcsk might be waiting to respawn it)
                                # anyway, take the opportunity to restart everything now
                                echo "pcsk($NAME) (pid $PCSKPID) is running but $NAME is currently stopped.  Restarting now..."
                                $0 restart
                                exit $?
                        fi
                fi
        fi
        echo -n "Starting $DESC: pcsk($NAME) "
        # NICE gets ignored because pcsk doesn't support it and start-stop-daemon would just nice pcsk, not the daemon.
        # Maybe we could somehow use renice?  Later...
        if ! start-stop-daemon --start --pidfile "$PCSKPIDFILE" --chuid $USER --exec "$PCSKBINARY" -- $PCSKARGS ; then
                echo "failed (start-stop-daemon exited with code $?)"
                exit 1
        fi
        echo "."
        ;;
  stop)        [ "`id -un`" = "root" ] || {
                echo "Failed.  You need to be root to $1 $DESC.  Try sudo."
                exit 1
        }
        echo -n "Stopping $DESC: pcsk($NAME) "
        start-stop-daemon --stop --oknodo --pidfile "$PCSKPIDFILE" --chuid $USER --retry 5 --exec "$PCSKBINARY" -- $PCSKARGS
        [ -e "$PCSKPIDFILE" ] && rm -f "$PCSKPIDFILE"
        [ -e "$RUNDIR/$NAME.pid" ] && rm -f "$RUNDIR/$NAME.pid"
        echo "."
        ;;
  restart)
        $0 stop
        sleep 3
        $0 start
        ;;
  force-reload)
        echo "force-reload not supported, restarting instead..."
        $0 restart
        ;;
  status)
        # We have an advantage in this mess that we know what the pidfiles (and therefore pids) are
        # Poor-man's status can be had by checking for the existance of a pidfile
        # and then that it's a currently running process by looking for that directory in /proc
        PCSKPID=
        DAEMONPID=
        EXITBAD=FALSE
        if [ -e "$PCSKPIDFILE" ] ; then
                read PCSKPID < "$PCSKPIDFILE"
                if [ -n "$PCSKPID" ] && [ -d "/proc/$PCSKPID" ] ; then
                        echo "pcsk($NAME) (pid $PCSKPID) is running..."
                else
                        echo "pcsk($NAME) is dead but pid file exists"
                        EXITBAD=TRUE
                fi
        else
                echo "pcsk($NAME) is stopped"
                EXITBAD=TRUE
        fi
        if [ -e "$RUNDIR/$NAME.pid" ] ; then
                read DAEMONPID < "$RUNDIR/$NAME.pid"
                if [ -n "$DAEMONPID" ] && [ -d "/proc/$DAEMONPID" ] ; then
                        echo "$NAME (pid $DAEMONPID) is running..."
                else
                        echo "$NAME is dead but pid file exists"
                        EXITBAD=TRUE
                fi
        else
                echo "$NAME is stopped"
                EXITBAD=TRUE
        fi
        [ "$EXITBAD" = "TRUE" ] && exit 1
        $MYTHTVSTATUSBINARY $MYTHTVSTATUSARGS
        ;;
  *)
        echo "Usage: $0 {start|stop|restart|status|force-reload(unsupported)}" >&2
        exit 1
        ;;
esac

exit 0

Stop mythbackend first.

sudo /etc/init.d/mythtv-backend stop

Keep a copy of the original script in case you need it, but make the new one the default.

cd /etc/init.d
sudo mv mythtv-backend mythtv-backend.original
sudo ln -s mythtv-backend.pcsk mythtv-backend

Start mythbackend again.

sudo mythtv-backend start