#!/bin/bash
#
#  runvdr extreme
#
#  configurable vdr launcher script
#
#      by Udo Richter <udo_richter(a)gmx.de>
#      http://www.richter-udo.de/vdr/scripts.html#runvdr
#

RUNVDRCONF=""


# Some unix commands being used:

PGREP="pgrep"
PS="ps"
GETOPT="getopt"
KILL="kill"
SLEEP="sleep"
CHVT="chvt"
DATE="date"
SETTERM="setterm"

# Options summary and conf file entries of runvdr:
#
# -C # --runvdr-conf=#   RUNVDRCONF       location of runvdr config file
# --                     ADDPARAMS        Additional parameters to pass to VDR
# --pluginsetup-conf=#   PLUGINSETUPCONF  location of plugin-setup-runvdr.conf
# --setupplugin-conf=#   SETUPPLUGINCONF  location of setup-plugin sysconfig
# --vdr=#                VDRPRG           location and name of the vdr binary
# --switchterminal=#     SWITCHTERMINAL   console terminal to switch to
# --xserver=#            XSERVER          start X server for VDR
# --xserver-proxy=#                       runvdr internal use only
# --runvdr-pid=#         RUNVDRPID        location of runvdr.pid file
# --dvb-load=#           DVBLOAD          command to load DVB drivers
# --dvb-unload=#         DVBUNLOAD        command to unload DVB drivers
# --wrapper=#            WRAPPER          wrapper command for calling vdr
# --terminate[=#]        TERMINATE        Terminate runvdr by pid
# --wait[=#]             WAIT             Wait # seconds for --terminate to finish
# --restart[=#]          RESTART          Send restart signal by pid
# --dvb-restart[=#]      DVBRESTART       Send dvb-restart signal by pid
# --term-timeout=#       TERMTIMEOUT      Timeout for VDR to react on SIGTERM
# --kill-timeout=#       KILLTIMEOUT      Timeout for VDR to react on SIGKILL
# --maxrestarts=#        MAXRESTARTS      Max number of rapid restarts before give-up
# --restarttime=#        RESTARTTIME      Max time for restart to be rapid
# --language=#           LANGUAGE         Locale to set for VDR
# -V --version           VERSION          print version information and exit
# -h --help              HELP             print this help and exit
#
# Supported options of VDR
#
#  -a #  --audio #     AUDIO            send Dolby Digital audio to stdin of command #
#  -c #  --config #    CONFIGDIR        read config files from DIR
#  -d    --daemon      DAEMON           run in daemon mode
#  -D #  --device #    DVBDEVICE        use only the given DVB device (NUM = 0, 1, 2...)
#  -E #  --epgfile #   EPGFILE          write the EPG data into the given FILE. - to disable.
#  -g #  --grab #      GRAB             write images from the SVDRP command GRAB into the given DIR;
#  -L #  --lib #       LIBDIR           search for plugins in DIR (default is %s)
#        --lirc #?     LIRC             use a LIRC remote control device, attached to PATH
#        --localedir # LOCALEDIR        search for locale files in DIR
#  -l #  --log #       LOGLEVEL         set log level (default: 3)
#  -m    --mute        MUTE             mute audio of the primary DVB device at startup
#        --no-kbd      NOKBD            don't use the keyboard as an input device
#  -P #  --plugin #    PLUGINS          load a plugin defined by the given options
#  -p #  --port #      SVDRPPORT        use PORT for SVDRP
#        --rcu #?      RCU              use a remote control device, attached to PATH
#  -r #  --record #    RECORDCMD        call CMD before and after a recording
#  -s #  --shutdown #  SHUTDOWN         call CMD to shutdown the computer
#  -t #  --terminal #  TERMINAL         controlling tty
#  -u #  --user #      USER             run as user USER; only applicable if started as root
#        --userdump    USERDUMP         allow coredumps if -u is given (debugging)
#        --vfat        VFAT             encode special characters in recording names
#  -v #  --video #     VIDEODIR         use DIR as video directory
#  -w #  --watchdog #  WATCHDOG         activate the watchdog timer with a timeout of SEC


which $PGREP     >/dev/null || { echo missing $PGREP... >&2 ;   exit 1 ; }
which $PS        >/dev/null || { echo missing $PS... >&2 ;      exit 1 ; }
which $GETOPT    >/dev/null || { echo missing $GETOPT... >&2 ;  exit 1 ; }
which $KILL      >/dev/null || { echo missing $KILL... >&2 ;    exit 1 ; }
which $SLEEP     >/dev/null || { echo missing $SLEEP... >&2 ;   exit 1 ; }
which $CHVT      >/dev/null || { echo missing $CHVT... >&2 ;    exit 1 ; }
which $DATE      >/dev/null || { echo missing $DATE... >&2 ;    exit 1 ; }
which $SETTERM   >/dev/null || { echo missing $SETTERM... >&2 ; exit 1 ; }



function ParseCommandLine_Step1() {
    # Parse command line, step 1
    # Stores pre-processed options in $OPTIONS
    # evaluates -C, --runvdr-conf and --xserver-proxy only

    SHORTOPT="a:c:C:dD:E:g:hl:L:mp:P:r:s:t:u:v:Vw:"
    LONGOPT="runvdr-conf:,xserver-proxy:,pluginsetup-conf:,setupplugin-conf:,vdr:,\
        switchterminal:,maxrestarts:,restarttime:,xserver:,\
        runvdr-pid:,dvb-load:,dvb-unload:,language:,wrapper:,\
        term-timeout:,kill-timeout:,terminate::,restart::,dvb-restart::,\
        wait::,audio:,config:,daemon,device:,epgfile:,grab:,help,lib:,lirc::,\
        log:,localedir:,mute,no-kbd,plugin:,port:,rcu::,record:,shutdown:,\
        terminal:,user:,userdump,version,vfat,video:,watchdog:"

    # prepare all optios for later processing
    OPTIONS=`$GETOPT -o "$SHORTOPT" --long "$LONGOPT" -n "$0" -- "$@"` || exit 1

    eval set -- "$OPTIONS"

    XSERVERPROXY=
    while true ; do case "$1" in
        -C|--runvdr-conf)  RUNVDRCONF="$2";  shift 2;;
           --xserver-proxy) XSERVERPROXY="$2"; shift 2;;
           --pluginsetup-conf)               shift 2;;
           --setupplugin-conf)               shift 2;;
           --vdr)                            shift 2;;
           --switchterminal)                 shift 2;;
           --xserver)                        shift 2;;
           --runvdr-pid)                     shift 2;;
           --dvb-load)                       shift 2;;
           --dvb-unload)                     shift 2;;
           --language)                       shift 2;;
           --wrapper)                        shift 2;;
           --term-timeout)                   shift 2;;
           --kill-timeout)                   shift 2;;
           --terminate)                      shift 2;;
           --wait)                           shift 2;;
           --restart)                        shift 2;;
           --dvb-restart)                    shift 2;;
           --maxrestarts)                    shift 2;;
           --restarttime)                    shift 2;;
        -h|--help)                           shift  ;;
        -V|--version)                        shift  ;;

        -a|--audio)                          shift 2;;
        -c|--config)                         shift 2;;
        -d|--daemon)                         shift  ;;
        -D|--device)                         shift 2;;
        -E|--epgfile)                        shift 2;;
        -g|--grab)                           shift  ;;
        -l|--log)                            shift 2;;
        -L|--lib)                            shift 2;;
           --lirc)                           shift 2;;
           --localedir)                      shift 2;;
        -m|--mute)                           shift  ;;
           --no-kbd)                         shift  ;;
        -p|--port)                           shift 2;;
        -P|--plugin)                         shift 2;;
           --rcu)                            shift 2;;
        -r|--record)                         shift 2;;
        -s|--shutdown)                       shift 2;;
        -t|--terminal)                       shift 2;;
        -u|--user)                           shift 2;;
           --userdump)                       shift  ;;
        -v|--video)                          shift 2;;
           --vfat)                           shift  ;;
        -w|--watchdog)                       shift 2;;

        --)
            shift
            break
            ;;
        *)
            echo "Internal error!" >&2
            exit 1
            ;;
    esac ; done
}

function Clean() {
    # Clean all config variables
    ADDPARAM=
    ADDPARAMS=()
    PLUGINSETUPCONF=
    SETUPPLUGINCONF=
    VDRPRG=
    SWITCHTERMINAL=
    XSERVER=
    RUNVDRPID=
    DVBLOAD=
    DVBUNLOAD=
    LANGUAGE=
    WRAPPER=
    TERMTIMEOUT=
    KILLTIMEOUT=
    TERMINATE=
    RESTART=
    DVBRESTART=
    MAXRESTARTS=5
    RESTARTTIME=10
    WAIT=0
    HELP=
    VERSION=

    AUDIO=
    CONFIGDIR=
    DAEMON=
    DVBDEVICE=()
    EPGFILE=
    GRAB=
    LIBDIR=
    LIRC=
    LOGLEVEL=
    LOCALEDIR=
    MUTE=
    NOKBD=
    PLUGINS=()
    SVDRPPORT=
    RECORDCMD=
    RCU=
    SHUTDOWN=
    TERMINAL=
    USER=
    USERDUMP=
    VFAT=
    VIDEODIR=
    WATCHDOG=
}


# Helper functions
function AddPluginString() {
    # add $1 as plugin

    if [ -n "$1" -a -z "${1##-*}" ] ; then
        local pattern="${1#-}*"

        local -a plugins1
        plugins1=("${PLUGINS[@]}")
        PLUGINS=()

        for i in "${plugins1[@]}" ; do
            [ -n "${i##$pattern}" ] && PLUGINS[${#PLUGINS[*]}]="$i";
        done
    else
        [ -n "$1" ] && PLUGINS[${#PLUGINS[*]}]="$1";
    fi
}
function AddPlugin() {
    # add $* as plugin, do shell quoting

    local plugin=""
    while [ $# -gt 0 ] ; do
        # regexp magic
        # quote '\' to '\\'
        local par="$1"
        par="${par//'\'/\\\\}"
        # work around bash bug: double quoted '\'
        par="${par//'\\'/\\\\}"
        # quote '"' to '\"'
        par="${par//\"/\\\"}"
        # check if this splits into words
        local -a arr=($par)
        # if yes, wrap in quotes
        [ ${#arr[*]} -ne 1 ] && par="\"$par\""
        # add to plugin string
        if [ -n "$plugin" ] ; then plugin="$plugin $par" ; else plugin="$par" ; fi
        # next, please
        shift
    done
    AddPluginString "$plugin"
}
function AddDevice() {
    if [ -n "$1" -a -z "${1##-*}" ] ; then
        local pattern="${1#-}*"

        local -a devices1
        devices1=("${DVBDEVICE[@]}")
        DVBDEVICE=()

        for i in "${devices1[@]}" ; do
            [ -n "${i##$pattern}" ] && DVBDEVICE[${#DVBDEVICE[*]}]="$i";
        done
    else
        [ -n "$1" ] && DVBDEVICE[${#DVBDEVICE[*]}]="$1";
    fi
}

function AddParams() {
    # Add to unparsed parameter list
    for i ; do
        ADDPARAMS[${#ADDPARAMS[*]}]="$i"
    done
}        

function INCLUDE() {
    # include different conf file(s)

    for file ; do
        if [ -r "$file" ] ; then
            . "$file" || exit 1
        else
            echo "runvdr: $file not found." >&2
        fi
    done
}

function LoadConfFile() {

    # Load configuration file

    VDRUSER=""
    # Old option, supported for compatibility only

    if [ -z "$RUNVDRCONF" -a -r ~/.runvdr.conf ] ; then
        RUNVDRCONF=~/.runvdr.conf
    fi
    if [ -z "$RUNVDRCONF" -a -r /etc/runvdr.conf ] ; then
        RUNVDRCONF=/etc/runvdr.conf
    fi
    if [ -n "$RUNVDRCONF" ] ; then
        if [ -r "$RUNVDRCONF" ] ; then
            . "$RUNVDRCONF" || exit 1
        else
            echo "runvdr: $RUNVDRCONF not found." >&2
        fi
    fi

    if [ -n "$VDRUSER" ] ; then
        echo "runvdr: VDRUSER is deprecated. Use USER= instead."
        [ -n "$USER" ] && USER="$VDRUSER"
    fi

    # Transform some defaults, so empty parameters can have a
    # non-default meaning
    [ -z "$LIRC" ] && LIRC=0
    [ -z "$RCU"  ] && RCU=0
    [ -z "$TERMINATE" ] && TERMINATE=0
    [ -z "$RESTART" ] && RESTART=0
    [ -z "$DVBRESTART" ] && DVBRESTART=0
    [ -z "$MAXRESTARTS" ] && MAXRESTARTS=5
    [ -z "$RESTARTTIME" ] && RESTARTTIME=10
    
    # Compatibility: Map old ADDPARAM to new AddParams
    [ -n "$ADDPARAM" ] && eval "AddParams $ADDPARAM"

    return 0
}


function ParseCommandLine_Step2() {
    # Parse command line, step 2
    # Process all options in $OPTIONS, override
    # all options that are set by now

    eval set -- "$OPTIONS"

    while true ; do case "$1" in
        -C|--runvdr-conf)                    shift 2;;
           --xserver-proxy)                  shift 2;;
           --pluginsetup-conf) PLUGINSETUPCONF="$2"; shift 2;;
           --setupplugin-conf) SETUPPLUGINCONF="$2"; shift 2;;
           --vdr)          VDRPRG="$2";      shift 2;;
           --switchterminal) SWITCHTERMINAL="$2"; shift 2;;
           --xserver)      XSERVER="$2";     shift 2;;
           --runvdr-pid)   RUNVDRPID="$2";   shift 2;;
           --dvb-load)     DVBLOAD="$2";     shift 2;;
           --dvb-unload)   DVBUNLOAD="$2";   shift 2;;
           --language)     LANGUAGE="$2";    shift 2;;
           --wrapper)      WRAPPER="$2";     shift 2;;
           --term-timeout) TERMTIMEOUT="$2"; shift 2;;
           --kill-timeout) KILLTIMEOUT="$2"; shift 2;;
           --terminate)    TERMINATE="$2";   shift 2;;
           --wait)         WAIT="$2";        shift 2;;
           --restart)      RESTART="$2";     shift 2;;
           --dvb-restart)  DVBRESTART="$2";  shift 2;;
           --maxrestarts)  MAXRESTARTS="$2"; shift 2;;
           --restarttime)  RESTARTTIME="$2"; shift 2;;
        -h|--help)         HELP=1;           shift  ;;
        -V|--version)      VERSION=1;        shift  ;;


        -a|--audio)        AUDIO="$2";     shift 2;;
        -c|--config)       CONFIGDIR="$2"; shift 2;;
        -d|--daemon)       DAEMON=1;       shift  ;;
        -D|--device)       AddDevice "$2"; shift 2;;
        -E|--epgfile)      EPGFILE="$2";   shift 2;;
        -g|--grab)         GRAB="1";       shift  ;;
        -l|--log)          LOGLEVEL="$2";  shift 2;;
        -L|--lib)          LIBDIR="$2";    shift 2;;
           --lirc)         LIRC="$2";      shift 2;;
           --localedir)    LOCALEDIR="$2"; shift 2;;
        -m|--mute)         MUTE=1;         shift  ;;
           --no-kbd)       NOKBD=1;        shift  ;;
        -p|--port)         SVDRPPORT="$2"; shift 2;;
        -P|--plugin)       AddPluginString "$2"; shift 2;;
           --rcu)          RCU="$2";       shift 2;;
        -r|--record)       RECORDCMD="$2"; shift 2;;
        -s|--shutdown)     SHUTDOWN="$2";  shift 2;;
        -t|--terminal)     TERMINAL="$2";  shift 2;;
        -u|--user)         USER="$2";      shift 2;;
           --userdump)     USERDUMP="$2";  shift  ;;
        -v|--video)        VIDEODIR="$2";  shift 2;;
           --vfat)         VFAT=1;         shift  ;;
        -w|--watchdog)     WATCHDOG="$2";  shift 2;;


        --)
            shift
            break
            ;;
        *)
            echo "Internal error!" >&2
            exit 1
            ;;
    esac ; done

    # Add all remaining options directly to additional params

    AddParams "$@"

    return 0
}



function OnlineHelp() {
    cat <<END-OF-HELP
Usage: $0 [OPTIONS]

runvdr Options:
-C #, --runvdr-conf=# location of runvdr config file
--pluginsetup-conf=#  location of plugin-setup-runvdr.conf
--setupplugin-conf=#  setup-plugin sysconfig
--vdr=#               location and name of the vdr binary
--switchterminal=#    console terminal number to switch to
--xserver=#           fire up own X server for VDR
--runvdr-pid=#        location of runvdr.pid file
--dvb-load=#          command to load DVB drivers
--dvb-unload=#        command to unload DVB drivers
--wrapper=#           wrapper command for calling vdr
--language=#          Locale to set for VDR
--terminate[=#]       Terminate runvdr by pid
--wait[=#]            Wait # seconds for --terminate to finish
--restart[=#]         Send restart signal by pid
--dvb-restart[=#]     Send dvb-restart signal by pid
--term-timeout=#      Timeout for VDR to react on SIGTERM
--kill-timeout=#      Timeout for VDR to react on SIGKILL
--maxrestarts=#       Max number of rapid restarts before give-up
--restarttime=#       Max time for restart to be rapid

-V, --version         print version information and exit
-h, --help            print this help and exit

Parsed VDR options:
-a # --audio=#     send Dolby Digital audio to stdin of command CMD
-c # --config=#    read config files from DIR
-d   --daemon      run in daemon mode
-D # --device=#    use only the given DVB device (NUM = 0, 1, 2...)
                   Use '-' to override devices from config file
                   Use '-x' to ignore device x from config file
-E # --epgfile=#   write the EPG data into the given FILE. - to disable.
-g # --grab=#      write images from the SVDRP command GRAB into the given DIR
-L # --lib=#       search for plugins in DIR
     --lirc[=#]    use a LIRC remote control device, attached to PATH
     --localedir=# search for locale files in DIR
-l # --log=#       set log level
-m   --mute        mute audio of the primary DVB device at startup
     --no-kbd      don't use the keyboard as an input device
-P # --plugin=#    load a plugin defined by the given options
                   Use '-' to ignore all plugins from config file
                   Use '-xx' to ignore plugin xx from config file
-p # --port=#      use PORT for SVDRP
     --rcu[=#]     use a remote control device, attached to PATH
-r # --record=#    call CMD before and after a recording
-s # --shutdown=#  call CMD to shutdown the computer
-t # --terminal=#  controlling tty
-u # --user=#      run as user USER; only applicable if started as root
     --userdump    allow coredumps if -u is given (debugging)
     --vfat        encode special characters in recording names
-v # --video=#     use DIR as video directory
-w # --watchdog=#  activate the watchdog timer with a timeout of SEC

All runvdr parameters after '--' will be passed to VDR without modification
END-OF-HELP
}


function AddCommandLine() {
    # Add all parameters to command line array
    for params ; do
        VDRCOMMAND[${#VDRCOMMAND[*]}]="$params"
    done
}

function BuildCommand() {
    # Based on all options, build command line in ${VDRCOMMAND[]}

    VDRCOMMAND=()

    # complete command with path
    VDRPRG=`which "$VDRPRG"`
    if [ -z "$VDRPRG" ] ; then
        echo "VDR command binary not found."
        exit 1
    fi

    # Build up command line:

    [ -n "$WRAPPER"   ] && AddCommandLine $WRAPPER
    # No quotes around $WRAPPER!

    AddCommandLine "$VDRPRG"

    [ "${#ADDPARAMS[*]}" -gt 0 ] && AddCommandLine "${ADDPARAMS[@]}"

    [ -n "$AUDIO"     ] && AddCommandLine -a "$AUDIO"
    [ -n "$CONFIGDIR" ] && AddCommandLine -c "$CONFIGDIR"
    [ -n "$DAEMON"    ] && AddCommandLine -d

    for i in "${DVBDEVICE[@]}" ; do
        [ -n "$i" ] && AddCommandLine -D "$i"
    done

    [ -n "$EPGFILE"   ] && AddCommandLine -E "$EPGFILE"
    [ -n "$GRAB"      ] && AddCommandLine -g "$GRAB"
    [ -n "$LOGLEVEL"  ] && AddCommandLine -l "$LOGLEVEL"
    [ -n "$LIBDIR"    ] && AddCommandLine -L "$LIBDIR"
    case "$LIRC" in
        1|"") AddCommandLine --lirc";;
        0) ;;
        *) AddCommandLine --lirc=\"$LIRC\"";;
    esac
    [ -n "$LOCALEDIR" ] && AddCommandLine --localedir "$LOCALEDIR"
    [ -n "$MUTE"      ] && AddCommandLine -m
    [ -n "$NOKBD"     ] && AddCommandLine --no-kbd
    [ -n "$SVDRPPORT" ] && AddCommandLine -p "$SVDRPPORT"

    for p in "${PLUGINS[@]}" ; do
        if [ -n "$p" ] ; then
            AddCommandLine -P "$p"
        fi
    done

    if [ -n "$PLUGINSETUPCONF" ] ; then
        eval "AddCommandLine `< $PLUGINSETUPCONF`"
    fi

    if [ -n "$SETUPPLUGINCONF" ] ; then
        while read -r line ; do
            [ "${line:0:11}" == "PLUGINLIST=" ] && eval "eval \"AddCommandLine ${line:11}\""
        done < "$SETUPPLUGINCONF"
    fi

    [ -n "$RECORDCMD" ] && AddCommandLine -r "$RECORDCMD"
    case "$RCU" in
        1|"") AddCommandLine --rcu;;
        0) ;;
        *) AddCommandLine --rcu="$RCU";;
    esac

    [ -n "$SHUTDOWN"  ] && AddCommandLine -s "$SHUTDOWN"
    [ -n "$TERMINAL"  ] && AddCommandLine -t "$TERMINAL"
    [ -n "$USER"      ] && AddCommandLine -u "$USER"
    [ -n "$USERDUMP"  ] && AddCommandLine --userdump
    [ -n "$VFAT"      ] && AddCommandLine --vfat
    [ -n "$VIDEODIR"  ] && AddCommandLine -v "$VIDEODIR"
    [ -n "$WATCHDOG"  ] && AddCommandLine -w "$WATCHDOG"


    [ -z "$TERMTIMEOUT" ] && TERMTIMEOUT=20
    [ -z "$KILLTIMEOUT" ] && KILLTIMEOUT=5

    return 0
}

function GetChilds() {
    # Get PIDs of all forked childs of PID=$1, binary executable=$2
    # Returns list of PIDs in childlist

    child="$1"
    childlist=($child)
    IFSBACKUP="$IFS"
    for ((i=0;i<10;i++)) do
        len=${#childlist[*]}
        IFS=","
        child=`{ echo "$child" ; $PGREP -f "^$2 " -P "${childlist[*]}" ; } | sort -u`
        IFS="$IFSBACKUP"
        childlist=($child)

        [ "$len" -eq "${#childlist[*]}" ] && break
    done
}

function WaitKill() {
    # Terminates/Kills process $1, binary $2, timeout1 $3, timeout2 $4

    GetChilds "$1" "$2"

    echo -n "Sending ${#childlist[*]} processes the TERM signal."
    $KILL -TERM ${childlist[*]} >&- 2>&-

    for ((i=0;i<$3;i++)) ; do
        $PS ${childlist[*]} >&- 2>&- || { echo terminated. ; return ; }

        echo -n .
        $SLEEP 1
    done
    echo

    echo -n "Sending ${#childlist[*]} processes the KILL signal."
    $KILL -KILL ${childlist[*]} >&- 2>&-

    for ((i=0;i<$4;i++)) ; do
        $PS ${childlist[*]} >&- 2>&- || { echo terminated. ; return ; }
        echo -n .
        $SLEEP 1
    done
    echo failed.
}



#### ---------------
####   Main script
#### ---------------


# Parse command line, step 1
ParseCommandLine_Step1 "$@" || exit 1

# Act as a proxy for calling VDR, from within X session
if [ -n "$XSERVERPROXY" ] ; then
    # Unpack proxy command line
    eval "VDRCOMMAND=($VDRCOMMANDPROXY)"
    "${VDRCOMMAND[@]}" &
    PID=$!
    echo "PID $PID" > "$XSERVERPROXY"
    wait $PID
    echo "RET $?" >> "$XSERVERPROXY"
    exit 0
fi

# Clean variables
Clean

# Load and process all configuration
LoadConfFile || exit 1

# Process command line
ParseCommandLine_Step2 || exit 1


if [ -n "$HELP" ] ; then
    OnlineHelp
    exit 0
fi

if [ -n "$VERSION" ] ; then
    echo "runvdr extreme version 0.4.0"
    exit 0
fi

# Get old runvdr pid and move it to options
if [ -n "$RUNVDRPID" ] ; then
    OLDRUNVDRPID=""
    [ -r "$RUNVDRPID" ] && OLDRUNVDRPID=`<$RUNVDRPID`

    [ -z "$TERMINATE"  ] && TERMINATE="$OLDRUNVDRPID"
    [ -z "$RESTART"    ] && RESTART="$OLDRUNVDRPID"
    [ -z "$DVBRESTART" ] && DVBRESTART="$OLDRUNVDRPID"
fi

if [ "$TERMINATE" != "0" ] ; then
    if [ -n "$TERMINATE" ] ; then
        echo -n "Terminating runvdr (PID=$TERMINATE)"
        $KILL -TERM $TERMINATE
        while [ -z "$WAIT" ] || [ "$WAIT" -gt 0 ] ; do
            $PS $TERMINATE >&- || { echo done ; break ; }
            echo -n "."
            $SLEEP 1
            [ -n "$WAIT" ] && let WAIT=WAIT-1
        done

        echo ""
    else
        echo "No runvdr process to terminate."
    fi
    exit 0
fi

if [ "$RESTART" != "0" ] ; then
    if [ -n "$RESTART" ] ; then
       echo -n "Restarting runvdr (PID=$RESTART)..."
       $KILL -USR1 $RESTART
       echo ""
    else
        echo "No runvdr process to restart."
    fi
    exit 0
fi

if [ "$DVBRESTART" != "0" ] ; then
    if [ -n "$DVBRESTART" ] ; then
        echo -n "DVB-restarting runvdr (PID=$DVBRESTART)..."
        $KILL -USR2 $DVBRESTART
        echo ""
    else
        echo "No runvdr process to dvb-restart."
    fi
    exit 0
fi


# Build up VDR command
BuildCommand || exit 1


# Switch front console

[ -n "$SWITCHTERMINAL" ] && $CHVT $SWITCHTERMINAL

if [ -n "$LANGUAGE" ] ; then
    LC_ALL="$LANGUAGE"
    export LC_ALL
fi

# Remember PID of this process

[ -n "$RUNVDRPID" ] && echo $$ > $RUNVDRPID


# Prepare terminal redirection
if [ -n "$TERMINAL" ] ; then
    exec 1>"$TERMINAL"
    exec 2>"$TERMINAL"
fi


# Load driver if it hasn't been loaded already:
eval "$DVBLOAD"

# Count how often VDR terminated very quickly
SHORTRUNTIMES=0

# Initialize for now
PROXYFILE=

while (true) do
    echo -n "Starting VDR at " ; $DATE

    # Trap some signals sent to this script
    trap "SIG=HUP" SIGHUP
    trap "SIG=INT" SIGINT
    trap "SIG=QUIT" SIGQUIT
    trap "SIG=TERM" SIGTERM
    trap "SIG=USR1" SIGUSR1
    trap "SIG=USR2" SIGUSR2

    # clean up signal variable
    SIG=

    # Remember start time
    STARTTIME=`$DATE +%s`

    echo "${VDRCOMMAND[@]}"

    if [ -z "$XSERVER" ] ; then
        # Run VDR
        "${VDRCOMMAND[@]}" &

        # Remember PID of VDR process
        PID=$!

        # no X server PID
        XSERVERPID=

        # Wait for VDR to end or signal to arrive
        wait $PID

        # Remember return value of VDR
        RET=$?
    else
        # Run X server that runs VDR
        PROXYFILE="/tmp/runvdr-$$"
        [ -f "$PROXYFILE" ] && rm "$PROXYFILE"
        
        # Pack command array into single env string:
        export VDRCOMMANDPROXY=`printf '%q ' "${VDRCOMMAND[@]}"`
        
        # Fire up X server that calls back runvdr, and run into XSERVERPROXY there
        eval "xinit \""$0"\" --xserver-proxy=\""$PROXYFILE"\" -- $XSERVER &"

        # Remember X server PID
        XSERVERPID=$!

        # ... also temporary as VDR PID
        PID=$XSERVERPID

        # Wait for VDR to end or signal to arrive
        wait $XSERVERPID

        # No known return of VDR yet
        RET=0
    fi

    # If proxy, read proxy return values
    if [ -n "$PROXYFILE" ] ; then
        while read var arg ; do case "$var" in
            PID) PID="$arg";;
            RET) RET="$arg";;
        esac ; done < "$PROXYFILE"
        rm "$PROXYFILE"
    fi

    # Remember stop time
    STOPTIME=`$DATE +%s`
    # Count if time is less than RESTARTTIME seconds,
    # forget otherwise
    if [ "$RESTARTTIME" -gt 0 -a $((STOPTIME-STARTTIME)) -le "$RESTARTTIME" ] ; then
        SHORTRUNTIMES=$((SHORTRUNTIMES+1))
        echo "VDR died within $RESTARTTIME seconds, this happened $SHORTRUNTIMES time(s)."
    else
        SHORTRUNTIMES=0
    fi

    # Reset terminal status
    [ -n "$TERMINAL" ] && $SETTERM -initialize

    case "$SIG" in
      HUP | INT | QUIT | TERM)
        echo -n "Terminating by request at " ; $DATE

        # Kill remaining VDR traces
        WaitKill $PID $VDRPRG $TERMTIMEOUT $KILLTIMEOUT

        # and exit
        break
        ;;
      USR1)
        echo -n "Restarting VDR by request at " ; $DATE

        # Kill remaining VDR traces
        WaitKill $PID $VDRPRG $TERMTIMEOUT $KILLTIMEOUT

        # Wait for X server to be down too
        [ "$XSERVERPID" ] && wait "$XSERVERPID"

        # and loop
        ;;
      USR2)
        echo -n "Restarting VDR and DVB by request at " ; $DATE

        # Kill remaining VDR traces
        WaitKill $PID $VDRPRG $TERMTIMEOUT $KILLTIMEOUT

        # Wait for X server to be down too
        [ "$XSERVERPID" ] && wait "$XSERVERPID"

        # reload DVB stuff
        eval "$DVBUNLOAD"
        eval "$DVBLOAD"
        ;;
      *)  # Non-signal termination
        if [ $RET -eq 0 -o $RET -eq 2 ] ; then
            echo -n "Terminating by error level $RET at " ; $DATE

            # Kill remaining VDR traces
            WaitKill $PID $VDRPRG $TERMTIMEOUT $KILLTIMEOUT

            # and exit
            break
        fi
        if [ $SHORTRUNTIMES -gt "$MAXRESTARTS" ] ; then
            echo -n "Terminating because VDR died $SHORTRUNTIMES times in a row quickly at " ; $DATE

            # Kill remaining VDR traces
            WaitKill $PID $VDRPRG $TERMTIMEOUT $KILLTIMEOUT

            # and exit
            break;
        fi
        echo -n "Restarting VDR and DVB by error level $RET at " ; $DATE

        # Kill remaining VDR traces
        WaitKill $PID $VDRPRG $TERMTIMEOUT $KILLTIMEOUT

        # Wait for X server to be down too
        [ "$XSERVERPID" ] && wait "$XSERVERPID"

        # reload DVB
        eval "$DVBUNLOAD"
        eval "$DVBLOAD"

        # and loop
        ;;
    esac

    # reload configuration
    Clean
    LoadConfFile || exit 1
    ParseCommandLine_Step2 || exit 1
    BuildCommand || exit 1

    # Catch remaining in-between signals
    case "$SIG" in
      HUP | INT | QUIT | TERM)
        break
        ;;
    esac
done

# Clean up PID file
[ -n "$RUNVDRPID" ] && rm $RUNVDRPID >&- 2>&-

# Clean up proxy file, if used
[ -n "$PROXYFILE" ] && rm $PROXYFILE >&- 2>&-