Keeping MythBackend Up And Running

>>>>>>
<<<<<<
MythBackend has a propensity to crash at inopportune moments (e.g. a couple hours after you've just left on a week's vacation). Well, nobody's perfect. Stuff happens. You can't expect to write code that never crashes. But, given Myth's status as a consumer appliance, you wonder why the Mythbuntu bozos didn't bother to include this watchdog for MythBackend in the distro. As it is now, you have to install it yourself.

This section was distilled from:

     http://www.mythtv.org/wiki/index.php/Using_pcsk_to_Supervise_mythbackend

Obtain the latest PCSK from http://www.nix.hu/projects/pcsk/. Uncompress and unpack the distro and build it. For example:

     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
     sudo make install

Create the following script (from Mythbuntu 8.04LTS) in your pcsk directory (see below for a Mythbuntu 9.04 script). I called mine mythtv-backend.pcsk:

     #! /bin/sh
     ### BEGIN INIT INFO
     # Provides:          mythtv-backend
     # Required-Start:    $local_fs $remote_fs $network
     # Required-Stop:     $local_fs
     # Default-Start:     50
     # Default-Stop:      S
     # Short-Description: Start/Stop the MythTV server.
     ### END INIT INFO
     #
     # modified mythtv-backend rc script
     # V0.22 by EW  6th Jun 2008:
     #       Updated for Mythbuntu 8.04LTS
     #       Removed mythtv-status
     # v0.21 by DJK 3rd Dec 2007:
     #       Removed -0 option for pcsk
     #               mythbackend seems to catch all signals (even when core
     #               dumping) and still exits 0! so we actually need to restart
     #               it no matter what its exit code was...
     # 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
             log_end_msg "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
             log_end_msg "Could not find pcsk binary to supervise $NAME.  \
                 Try www.nix.hu/projects/pcsk..."
             exit 1
     fi
     . /lib/lsb/init-functions
     set -e
     prime_firewire()
     {
         if [ "$ENABLE_FIREWIRE" = "TRUE" ]; then
         log_daemon_msg "Priming Firewire "
             su - $USER -c "/usr/bin/mythprime"
         log_end_msg $?
         fi
     }
     USER=mythtv
     USER_HOME=$(grep ^$USER /etc/passwd | awk -F : '{print $6}')
     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 - not using this at the moment
     # -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="-reo -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"
     mkdir -p $RUNDIR
     chown -R $USER $RUNDIR
     unset DISPLAY
     unset SESSION_MANAGER
     #create a symbolic link for mysql.txt so it can't be overwritten
     mkdir -p $USER_HOME/.mythtv
     chown -R $USER $USER_HOME/.mythtv
     if [ ! -e $USER_HOME/.mythtv/mysql.txt ]; then
             ln -s /etc/mythtv/mysql.txt $USER_HOME/.mythtv/mysql.txt
     fi
     case "$1" in
       start)
         # 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
                         log_success_msg "pcsk($NAME) (pid $PCSKPID) is already \
                             running, supervising $NAME (pid $DAEMONPID); \
                             use restart instead."
                         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
                     log_success_msg "pcsk($NAME) (pid $PCSKPID) is running but \
                         $NAME is currently stopped.  Restarting now..."
                     $0 restart
                     exit $?
                 fi
             fi
         fi
         prime_firewire
         log_daemon_msg "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
             log_end_msg "failed (start-stop-daemon exited with code $?)"
             exit 1
         fi
         log_end_msg $?
         ;;
       stop)
         log_daemon_msg "Stopping $DESC: pcsk($NAME) "
         start-stop-daemon --stop --oknodo --pidfile "$PCSKPIDFILE" \
             --chuid $USER --retry 5 --exec "$PCSKBINARY" -- $PCSKARGS
         log_end_msg $?
         [ -e "$PCSKPIDFILE" ] && rm -f "$PCSKPIDFILE"
         [ -e "$RUNDIR/$NAME.pid" ] && rm -f "$RUNDIR/$NAME.pid"
         ;;
       restart)
         $0 stop
         sleep 3
         $0 start
         ;;
       force-reload)
         log_daemon_msg "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=
         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"
             fi
         else
             echo "pcsk($NAME) is stopped"
         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"
             fi
         else
             echo "$NAME is stopped"
         fi
         ;;
       *)
         echo "Usage: $0 {start|stop|restart|status|force-reload(unsupported)}" \
             >&2
         exit 2
         ;;
     esac
     exit 0

The mythtv-backend script has been updated for Mythbuntu 9.04. Here is an updated version of the script for Mythbuntu 9.04:

     #! /bin/sh
     ### BEGIN INIT INFO
     # Provides:          mythtv-backend
     # Required-Start:    $local_fs $remote_fs $network
     # Required-Stop:     $local_fs
     # Default-Start:     2 3 4 5
     # Default-Stop:      0 1 6
     # Short-Description: Start/Stop the MythTV server.
     ### END INIT INFO
     #
     # modified mythtv-backend rc script
     # V0.23 by EW  22th Sep 2009:
     #       Updated for Mythbuntu 9.04
     # V0.22 by EW  6th Jun 2008:
     #       Updated for Mythbuntu 8.04LTS
     #       Removed mythtv-status
     # v0.21 by DJK 3rd Dec 2007:
     #       Removed -0 option for pcsk
     #               mythbackend seems to catch all signals (even when core
     #               dumping) and still exits 0! so we actually need to restart
     #               it no matter what its exit code was...
     # 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"
     . /lib/lsb/init-functions
     # Check that the mythbackend binary exists
     if ! [ -x "$DAEMON" ] ; then
         log_failure_msg "Could not find $DAEMON binary"
         log_end_msg 1
         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
         log_failure_msg "Could not find pcsk binary to supervise $NAME.  \
             Try www.nix.hu/projects/pcsk..."
         log_end_msg 1
         exit 1
     fi
     set -e
     prime_firewire()
     {
         if [ "$ENABLE_FIREWIRE" = "TRUE" ]; then
             log_daemon_msg "Priming Firewire "
             su - $USER -c "/usr/bin/mythprime"
             log_end_msg $?
         fi
     }
     USER=mythtv
     USER_HOME=$(grep ^$USER /etc/passwd | awk -F : '{print $6}')
     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 - not using this at the moment
     # -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="-reo -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"
     mkdir -p $RUNDIR
     chown -R $USER $RUNDIR
     unset DISPLAY
     unset SESSION_MANAGER
     # Check that the MythTV home directory exists.
     if [ ! -d $USER_HOME ]; then
         log_failure_msg "MythTV home directory, $USER_HOME does not exist."
         exit 1
     fi
     #create a symbolic link for mysql.txt so it can't be overwritten
     mkdir -p $USER_HOME/.mythtv
     chown -R $USER $USER_HOME/.mythtv
     if [ ! -e $USER_HOME/.mythtv/mysql.txt ]; then
         ln -s /etc/mythtv/mysql.txt $USER_HOME/.mythtv/mysql.txt
     fi
     #create config.xml for bindings
     if [ ! -e $USER_HOME/.mythtv/config.xml ]; then
         ln -s /etc/mythtv/config.xml $USER_HOME/.mythtv/config.xml
     fi
     case "$1" in
       start)
           # 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
                           log_success_msg "pcsk($NAME) (pid $PCSKPID) is \
                               already running, supervising $NAME \
                               (pid $DAEMONPID); use restart instead."
                           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
                       log_success_msg "pcsk($NAME) (pid $PCSKPID) is running \
                           but $NAME is currently stopped.  Restarting now..."
                       $0 restart
                       exit $?
                   fi
               fi
           fi
           prime_firewire
           log_daemon_msg "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
               log_failure_msg "failed (start-stop-daemon exited with code $?)"
               log_end_msg 1
               exit 1
           fi
           log_end_msg $?
           ;;
       stop)
           log_daemon_msg "Stopping $DESC: pcsk($NAME) "
           start-stop-daemon --stop --oknodo --pidfile "$PCSKPIDFILE" \
               --chuid $USER --retry 5 --exec "$PCSKBINARY" -- $PCSKARGS
           log_end_msg $?
           [ -e "$PCSKPIDFILE" ] && rm -f "$PCSKPIDFILE"
           [ -e "$RUNDIR/$NAME.pid" ] && rm -f "$RUNDIR/$NAME.pid"
           ;;
       restart|force-reload)
           log_daemon_msg "Restarting $DESC: $NAME "
           $0 stop
           sleep 3
           $0 start
           log_end_msg $?
           ;;
       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=
           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"
               fi
           else
               echo "pcsk($NAME) is stopped"
           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"
               fi
           else
               echo "$NAME is stopped"
           fi
           ;;
       *)
           N=/etc/init.d/$NAME
           echo "Usage: $N {start|stop|restart|force-reload|status}" >&2
           exit 2
           ;;
     esac
     exit 0

Once you've created the script, stop the backend, copy the script to the /etc/init.d directory, rename the original script to get it out of the way and then symlink the new script as the backend startup script:

     sudo /etc/init.d/mythtv-backend stop
     sudo cp mythtv-backend.pcsk /etc/init.d/mythtv-backend.pcsk
     cd /etc/init.d
     sudo chmod ugo+x mythtv-backend.pcsk
     sudo mv mythtv-backend mythtv-backend.orig
     sudo ln -s mythtv-backend.pcsk mythtv-backend
     sudo /etc/init.d/mythtv-backend start

Finally, you'll probably want to update the logrotate config file for mythtv-backend (/etc/logrotate.d/mythtv-backend) to rotate the log file created by pcsk too, by adding the following lines to it:

     /var/log/mythtv/mythbackend-pcsk.log {
         daily
         rotate 7
         notifempty
         copytruncate
     }