#!/bin/bash
#
# luci    high availability management application
#
# chkconfig: - 99 01
# description: Starts and stops luci
#
#
### BEGIN INIT INFO
# Provides:        luci
# Required-Start:  $named $network $time
# Required-Stop:   $network $time
# Default-Start:
# Default-Stop:
# Short-Description:  Starts and stops luci
# Description:  Starts and stops the luci high availability management application
### END INIT INFO

# For messages capture/debugging on systemd-enabled system use, e.g., syslog
ECHOFUNC=echo
#ECHOFUNC="logger -t luci --"

USEFUNCS=1
# Source function library.
if [ -e /etc/rc.d/init.d/functions ]; then
    . /etc/rc.d/init.d/functions
else
    PATH="/sbin:/usr/sbin:/bin:/usr/bin"
    export PATH
    success() { return; }
    failure() { return; }
    USEFUNCS=0
fi


prog="luci"
config="/var/lib/luci/etc/luci.ini"
sysconfig="/etc/sysconfig/luci"
exec="/usr/sbin/luci"
sqlite_bin="/usr/bin/sqlite3"

logfile="/var/log/luci/luci.log"

# Defaults that can be overridden by the content of $sysconfig
LOG_FILE="$logfile"
KEEP_RUNTIME_DATA=0
PID_FILE_WAIT=1

# Override defaults with values from initscript configuration file
[ -e "$sysconfig" ] && . "$sysconfig"


lockfile="/var/lock/subsys/luci"

# Following values are not intended to be overridden by $sysconfig

PKG_NAME="luci"
DAEMON_USER="luci"
DAEMON_GROUP="luci"
# These are only used to inform user and only when the port a/o host is not
# redefined in initscript configuration file ($sysconfig), otherwise these
# values are obtained from this file
HOST=0.0.0.0
PORT=8084

STATE_DIR="/var/lib/luci"
DB_FILE="/var/lib/luci/data/luci.db"
DB_DIR=$(dirname "$DB_FILE")
BACKUP_DIR=$(dirname "$DB_FILE")
DB_PERMS=0640
BACKUP_PERMS=0640

RUNTIMEDATA_DIR="/var/run/luci"
CACHE_DIR="/var/run/luci/cache"
SESSIONS_DIR="/var/run/luci/sessions"
PID_FILE="/var/run/luci/luci.pid"

CERT_KEY_BITS=2048
CERT_KEY_LIFE_DAYS=1825
CERT_CONFIG="/var/lib/luci/etc/cacert.config"
CERT_PEM="/var/lib/luci/certs/host.pem"
CERT_DIR=$(dirname "$CERT_PEM")

# Run $exec in a subshell with changed umask and with sane CWD
do_exec() { (cd / && umask 027 && $exec "$@"); }


# Check some conditions and return respective return code
entry_check() {
    this_identity=$(whoami)
    if [ "$this_identity" != "root" -a "$this_identity" != "$DAEMON_USER" ]; then
        # Must be either root or the luci user to run this
        $ECHOFUNC "Insufficient permissions" >&2
        return 4
    fi
}

# Automatically adds subjectAltName values for hostname domain names and/or IP
# addresses to the configuration of self-managed self-signed certificate
certconfig_complete() {
    if [ "$(tail -n 1 "$CERT_CONFIG")" == "###" ]; then
        $ECHOFUNC "Adding following auto-detected host IDs (IP addresses/domain" \
                  "names), corresponding to \`$HOST' address, to the"            \
                  "configuration of self-managed certificate \`$CERT_CONFIG'"    \
                  "(you can change them by editing \`$CERT_CONFIG', removing"    \
                  "the generated certificate \`$CERT_PEM' and restarting"        \
                  "$prog):" >&2
        CNT_DOM_NAME=16
        CNT_IP_ADDR=16
        IP_ADDRS=$(ip addr show scope global | sed '/^[ \t]*inet/!d; s/[ \t]*inet[^ \t]*[ \t]\+\([^ \t/]\+\).*$/\1/')
        CUSTOM_HOST=$(echo "$INIT_CONFIG" | sed 's/\$/\n/g' | sed '/^[ \t]*host[ \t]*=/!d; s/[^=]*=[ \t]*\([^$]\+\)$/\1/')
        [ -n "$CUSTOM_HOST" ] && HOST="$CUSTOM_HOST"
        if [ "$HOST" == "0.0.0.0" ]; then
            # Use only resolved names of all global host IP addresses for
            # identification within the certificate, no information about
            # these IPs added (for confidentality reasons)
            for IP_ADDR in $IP_ADDRS; do
                DOM_NAME=$(/usr/bin/python -Es -c "from socket import getfqdn; print(getfqdn('$IP_ADDR'))" 2>/dev/null)
                if [ -n "$DOM_NAME" -a "$DOM_NAME" != "$IP_ADDR" ]; then
                    echo "DNS.$CNT_DOM_NAME = $DOM_NAME" >>"$CERT_CONFIG"
                    $ECHOFUNC -e "\tDNS: $DOM_NAME" >&2
                    let CNT_DOM_NAME++
                fi
            done
        else
            # Use only resolved names of particular global host IP address
            # corresponding to the host IP address luci binds at, together
            # with this IP
            for IP_ADDR in $IP_ADDRS; do
                if [ "$IP_ADDR" == "$HOST" ]; then
                    echo "IP.$CNT_IP_ADDR = $IP_ADDR" >>"$CERT_CONFIG"
                    $ECHOFUNC -e "\tIP: $IP_ADDR" >&2
                    let CNT_IP_ADDR++
                    DOM_NAME=$(/usr/bin/python -Es -c "from socket import getfqdn; print(getfqdn('$IP_ADDR'))" 2>/dev/null)
                    if [ -n "$DOM_NAME" -a "$DOM_NAME" != "$IP_ADDR" ]; then
                        echo "DNS.$CNT_DOM_NAME = $DOM_NAME" >>"$CERT_CONFIG"
                        $ECHOFUNC -e "\tDNS: $DOM_NAME" >&2
                        let CNT_DOM_NAME++
                    fi
                fi
            done
        fi
        if [ $CNT_DOM_NAME -eq 16 -a $CNT_IP_ADDR -eq 16 ]; then
            $ECHOFUNC -e "\t(none suitable found, you can still do it manually" \
                         "as mentioned above)" >&2
        fi
        $ECHOFUNC -ne "\n" >&2
    fi
}

prepare_config() {
    # Touching $config first and then using ``.. make-config .. --overwrite''
    # does not work now (see http://trac.pythonpaste.org/pythonpaste/ticket/450)
    do_exec make-config $PKG_NAME "$config" --no-default-sysconfig --no-install &>/dev/null
    if [ $? -ne 0 ]; then
        rm -f -- "$config" &>/dev/null
        $ECHOFUNC "Unable to create the $PKG_NAME base configuration file (\`$config')." >&2
        return 6
    fi
    # luci does not write back to its config, better to keep access read-only
    chgrp $DAEMON_GROUP "$config" \
    && chmod 0640 "$config"
    if [ $? -ne 0 ]; then
        rm -f -- "$config" &>/dev/null
        $ECHOFUNC "Unable to change ownership/attributes of the $PKG_NAME base configuration file (\`$config')." >&2
        return 1
    fi
}

prepare_db() {
    touch "$DB_FILE"
    chown $DAEMON_USER:$DAEMON_GROUP "$DB_FILE" \
    && chmod -- $DB_PERMS "$DB_FILE"
    if [ $? -ne 0 ]; then
        rm -f -- "$DB_FILE" &>/dev/null
        $ECHOFUNC "Unable to change ownership/attributes of the $PKG_NAME database file (\`$DB_FILE')." >&2
        return 1
    fi
    do_exec setup-app "$config" --no-default-sysconfig &>/dev/null
    if [ $? -ne 0 ]; then
        rm -f -- "$DB_FILE" &>/dev/null
        $ECHOFUNC "Unable to create the $PKG_NAME database file (\`$DB_FILE')." >&2
        return 6
    fi
}

# Prepare self-managed self-signed certificate (/var/lib/luci/certs/host.pem)
prepare_cert() {
    touch "$CERT_PEM"
    chown $DAEMON_USER:$DAEMON_GROUP "$CERT_PEM" \
    && chmod 0600 "$CERT_PEM"
    if [ $? -ne 0 ]; then
        rm -f -- "$CERT_PEM" &>/dev/null
        $ECHOFUNC "Unable to change ownership/attributes of the $PKG_NAME host certificate file (\`$CERT_PEM')." >&2
        return 1
    fi
    certconfig_complete
    [ -x /usr/bin/openssl ] || exit 6
    # Generate the SSL certificate (PEM file containing also private key)
    # Note: Explicit specification of file containing random data is needed
    #       to suppress complaints (and we also control where this file is)
    export RANDFILE=$(mktemp -q --tmpdir="$CERT_DIR" "cert_rnd.XXXXXX")
    out="$(/usr/bin/openssl req -new -x509 -nodes              \
                                -newkey rsa:"$CERT_KEY_BITS"   \
                                -config "$CERT_CONFIG"         \
                                -days "$CERT_KEY_LIFE_DAYS"    \
                                -set_serial "$(/bin/date +%s)" \
                                -keyout "$CERT_PEM"            \
                                -out "$CERT_PEM" 2>&1)"
    ret=$?
    rm -f -- "$RANDFILE" &>/dev/null
    $ECHOFUNC "$out" | sed "/^[.+-]/d" >&2
    if [ $ret -ne 0 ]; then
        rm -f -- "$CERT_PEM" &>/dev/null
        $ECHOFUNC "Unable to generate the $PKG_NAME host certificate file (\`$CERT_PEM')." >&2
        return 6
    fi
}

initialize() {
    # Ensure the existence of the log file(s) incl. the parent directory,
    # which is a prerequisite for prepare_db, if not earlier
    logfiles="$logfile"
    [ "$LOG_FILE" = "$logfile" ] || logfiles="$logfiles $LOG_FILE"
    for l in $logfiles; do
        if [ ! -f "$l" ]; then
            install -o $DAEMON_USER -g $DAEMON_GROUP -d "$(dirname "$l")"
            touch "$l"
            chown $DAEMON_USER:$DAEMON_GROUP "$l" && chmod 0640 "$l"
            if [ $? -ne 0 ]; then
                $ECHOFUNC "Unable to change ownership/attributes of the $PKG_NAME log file (\`$l')." >&2
                return 1
            fi
        fi
    done
    # Ensure the existence of base configuration file
    if [ ! -f "$config" ]; then
        prepare_config || return $?
    fi
    # Ensure the existence of database file
    if [ ! -f "$DB_FILE" ]; then
        prepare_db || return $?
    fi
    # Ensure existence of SSL certificate (also check the state of custom
    # certificate possibly defined in $sysconfig)
    # TODO: When luci is able to handle the change of certificate used for
    #       connections to ricci conveniently via its GUI, there will be no need
    #       to generate it if custom one provided (double "if" -> "if - elif")
    CUSTOM_CERT_PEM=$(echo "$INIT_CONFIG" | sed 's/\$/\n/g' \
                      | sed -n '/^[ \t]*ssl[_.]pem[ \t]*=/!d; s/[^=]*=[ \t]*\([^$%]\+\)$/\1/p' \
                      | tail -n1)
    if [ -n "$CUSTOM_CERT_PEM" ]; then
        if [ ! -r "$CUSTOM_CERT_PEM" ]; then
            $ECHOFUNC "ERROR: Custom certificate \`$CUSTOM_CERT_PEM' (defined in \`$sysconfig'): cannot read" >&2
            return 6
        fi
    fi
    if [ ! -f "$CERT_PEM" ]; then
        prepare_cert || return $?
    fi
    # Ensure the existence of directory for run-time data
    # Note: As our rule is the directories for cache and sessions data are
    #       direct subdirectories of run-time data dir, we only check that
    #       this holds and do not care about them further as they are created
    #       automatically in run-time with the correct attributes when needed:
    #       - 0750 perms.     ~ /usr/lib*/python*/site-packages/beaker/util.py
    #       - owner           ~ user running luci
    #       - SELinux context ~ selinux-policy (process context)
    if [ "$(dirname "$CACHE_DIR")" != "$RUNTIMEDATA_DIR" \
         -o "$(dirname "$SESSIONS_DIR")" != "$RUNTIMEDATA_DIR" ]; then
        $ECHOFUNC -n "(Internal error) " >&2
        $ECHOFUNC "Directories for both cache and sessions data have to be direct subdirectories of run-time data directory." >&2
        return 1
    elif [ ! -d "$RUNTIMEDATA_DIR" ]; then
        install -m 0755 -o $DAEMON_USER -g $DAEMON_GROUP -d -- "$RUNTIMEDATA_DIR" \
        && restorecon -- "$RUNTIMEDATA_DIR" &>/dev/null
        if [ $? -ne 0 ]; then
            $ECHOFUNC "Unable to create the $PKG_NAME directory for run-time data (\`$RUNTIMEDATA_DIR')." >&2
            return 1
        fi
    fi
}

list_database_dumps() {
    /bin/ls -1 -- $BACKUP_DIR/luci-backup*.db 2>/dev/null
    return 0
}

dump_database() {
    if [ -n "$1" ]; then
        output_file=$(readlink -f -- "$1")  # normalize path (for output)
    else
        output_file="$BACKUP_DIR/luci-backup$(/bin/date +%Y%m%d%H%M%S).db"
    fi

    if [ -e "$output_file" ]; then
        if [ "$2" != "--force" ] && [ "$2" != "-f" ]; then
            $ECHOFUNC "A file named \`$output_file' already exists. Pass the additional argument of --force to overwrite it." >&2
            return 1
        fi
    fi
    $sqlite_bin "$DB_FILE" '.dump' > "$output_file"
    if [ $? -ne 0 ]; then
        $ECHOFUNC "Failed to create \`$output_file' properly." >&2
        rm -f -- "$output_file"
        return 1
    fi
    chown $DAEMON_USER:$DAEMON_GROUP "$output_file" \
    && chmod -- $BACKUP_PERMS "$output_file"
    if [ $? -ne 0 ]; then
        $ECHOFUNC "Unable to change ownership/attributes of the $PKG_NAME database backup file (\`$output_file')." >&2
        rm -f -- "$output_file" >&/dev/null
        return 1
    fi
    restorecon -R -- "$DB_DIR" "$BACKUP_DIR" >&/dev/null
    return 0
}

load_database() {
    db_dump_file=$(readlink -f "$1")  # normalize path (for output)
    if [ ! "$db_dump_file" ]; then
        $ECHOFUNC "No database backup file was specified. Run \`$0 list-backups' to get a list of known existing backup files." >&2
        return 1
    fi

    if [ ! -r "$db_dump_file" ]; then
        $ECHOFUNC "The database backup in \`$db_dump_file' cannot be read." >&2
        return 1
    fi

    tmp_db=$(mktemp -q --tmpdir="$DB_DIR")
    if [ $? -ne 0 ]; then
        $ECHOFUNC "Unable to create a temporary file in \`$DB_DIR'." >&2
        return 1
    fi
    $sqlite_bin $tmp_db ".read $db_dump_file"
    if [ $? -ne 0 ] || [ ! -s "$tmp_db" ]; then
        # sqlite will return 0 even if you feed it non-sql input. In the case
        # of bad input, the database file will have size 0.
        $ECHOFUNC "Unable to restore the database from the backup file \`$db_dump_file'" >&2
        rm -f -- "$tmp_db" >&/dev/null
        return 1
    fi
    chown $DAEMON_USER:$DAEMON_GROUP "$tmp_db" \
    && chmod -- $DB_PERMS "$tmp_db"
    if [ $? -ne 0 ]; then
        $ECHOFUNC "Unable to change ownership/attributes of the $PKG_NAME database file (\`$tmp_db')." >&2
        rm -f -- "$tmp_db" >&/dev/null
        return 1
    fi

    old_exists=0
    if [ -e "$DB_FILE" ]; then
        old_exists=1
        bknum=0
        while [ -e "$DB_FILE".$bknum ]; do
            bknum=$(($bknum+1))
        done
        old_db_name="$DB_FILE".$bknum

        mv -n -- "$DB_FILE" "$old_db_name"
        if [ $? -ne 0 ]; then
            # it appeared out of nowhere
            $ECHOFUNC "Unable to move the existing database file to \`$old_db_name'." >&2
            rm -f -- "$tmp_db" >&/dev/null
            return 1
        fi
    fi

    ret_code=0
    mv -- "$tmp_db" "$DB_FILE"
    if [ $? -ne 0 ]; then
        rm -f -- "$tmp_db" >&/dev/null
        if [ $old_exists -eq 1 ]; then
            $ECHOFUNC "Unable to move the newly created database file to \`$DB_FILE'. Trying to restore database from \`$old_db_name'." >&2
            mv -- "$old_db_name" "$DB_FILE"
        else
            $ECHOFUNC "Unable to move the newly created database file to \`$DB_FILE'." >&2
        fi
        ret_code=1
    fi

    restorecon -R -- "$DB_DIR" "$BACKUP_DIR" >&/dev/null
    return $ret_code
}

start_server() {
    # TODO: Can the dependency on saslauthd running be solved in a better way?
    #       LSB header doesn't seem to help there.
    /sbin/service saslauthd start || return 1

    do_exec serve --daemon --user "$DAEMON_USER" --group "$DAEMON_GROUP" \
                --log-file="$LOG_FILE" --pid-file="$PID_FILE"          \
                --server-name=init --app-name=init "$config"  INITPID=$$ >/dev/null
    # This has a bit unreliable return code, hence in case of success:
    # (1) server already running when start fired, hence $PID_FILE surely exists
    # (2) server started anew, the child is still running, but $PID_FILE
    #     not created yet
    # (3) ditto, but it progressed so that $PID_FILE is already created
    #
    # To add more uncertainty, the executed process may fail at any time
    # during the post-fork initialization procedure (incorrect config, etc.)
    # we are trying to establish if it has already finished that properly.
    # We know that the process failed once:
    # (a) pgrep'ing purposefully (and uniquely-enough) marked process fails
    #     twice in row (because of just single fork out of original double
    #     fork is to be carried)
    # (b) existence of non-empty $PID_FILE was once detected during this
    #     supervision run (i.e. $PID_FILE a priori removed to be created
    #     within), but subsequently found missing
    # but otherwise we have no easy way to detect the process progressed beyond
    # the initialization phase without possibly waiting forever (halting
    # problem) and we don't want to impose artificial timeout limits that
    # will always be wrong for some use cases, so it's safer to defer such
    # extremely rare cases to administrator by erring on the safe side,
    # returning failure and providing the hint that real state can be
    # discovered with a subsequent "status" action after a reasonable
    # delay.
    #
    # Hence we split the detection routine into two parts:
    # (I)  initial, performed here, that attempts the early distinguishing
    #      of (1) [returns failure early] and (3) if there's enough time
    #      for it to evolve from (2), otherwise this unresolved (2) case
    #      is deferred to second part (II) by signalling a "tentative
    #      success" (exit status 200); only (a) check is used here
    # (II) final check at the call site, which will be combination of
    #      (a) and (b), i.e., verifying that both $PID_FILE exists and
    #      specify PID of existing process
    # In case of (2), return distinguishing return code that is to signal
    # "tentative success"
    pgrep -f -u ${DAEMON_USER} -- " INITPID=$$" &>/dev/null \
        || pgrep -f -u ${DAEMON_USER} -- " INITPID=$$" &>/dev/null && {
            # wait a little if $PID_FILE appears
            [ -s "${PID_FILE}" ] && return 0
            sleep "${PID_FILE_WAIT}"
            pgrep -f -u ${DAEMON_USER} -- " INITPID=$$" &>/dev/null || return 1
            [ -s "${PID_FILE}" ] && return 0
        }
    return 200
}

start() {
    step=$"Start $prog..."

    CUSTOM_HOST=$(echo "$INIT_CONFIG" | sed 's/\$/\n/g' | sed '/^[ \t]*host[ \t]*=/!d; s/[^=]*=[ \t]*\([^$]\+\)$/\1/')
    [ -n "$CUSTOM_HOST" ] && HOST="$CUSTOM_HOST"
    [ "$HOST" == "0.0.0.0" ] && HOST=$(/usr/bin/python -Es -c "from socket import getfqdn; print(getfqdn())" 2>/dev/null)
    [ -z "$HOST" ] && HOST="127.0.0.1"

    CUSTOM_PORT=$(echo "$INIT_CONFIG" | sed 's/\$/\n/g' | sed '/^[ \t]*port[ \t]*=/!d; s/[^=]*=[ \t]*\([^$]\+\)$/\1/')
    [ -n "$CUSTOM_PORT" ] && PORT="$CUSTOM_PORT"
    [ "$PORT" != "443" ] && PRINT_PORT=":$PORT"

    if [ "$DAEMON_USER" != "root" -a "$PORT" -lt "1024" ]; then
        $ECHOFUNC "Unable to use privileged port (<1024) because $PKG_NAME runs as a non-root user." >&2
        return 1
    fi

    if [ ! -e "$lockfile" ]; then
        touch "$lockfile"
        [ "$KEEP_RUNTIME_DATA" -eq "0" ] \
            && rm -rf -- "$CACHE_DIR" "$SESSIONS_DIR" &>/dev/null
    fi

    # running start on a service already running OK
    kill -s 0 -- "$(head -n1 "${PID_FILE}" 2>/dev/null)" &>/dev/null
    [ $? -eq 0 ] && return 0 || rm -f -- "${PID_FILE}"

    # see start_server comment for details on the arrangement
    initialize && start_server
    retnominal=$?
    ret=$((retnominal%200))

    if [ $ret -eq 0 ]; then
        # wait before testing the liveness of the process when PID file OK
        kill -s 0 -- "$(head -n1 "${PID_FILE}" 2>/dev/null \
                        && sleep "${PID_FILE_WAIT}")" &>/dev/null
        [ $? -eq 0 ] || ret=1
    else
        rm -f -- "$lockfile" "$PID_FILE"
    fi
    $ECHOFUNC -n "$step"
    [ $ret -eq 0 ] && success || failure
    [ $USEFUNCS -eq 0 ] && $ECHOFUNC " $ret" || $ECHOFUNC
    if [ $ret -eq 0 ]; then
        $ECHOFUNC "Point your web browser to https://$HOST$PRINT_PORT (or equivalent) to access $PKG_NAME" >&2
    elif [ $retnominal -eq 200 ]; then
        # statement below is just not to confuse user if we unexpectedly
        # won the race trying to read $PID_FILE while not created yet
        $ECHOFUNC "Start not finished yet, please refer to \"status\" or increase PID_FILE_WAIT in ${sysconfig} above ${PID_FILE_WAIT}" >&2
    fi
    return $ret
}

stop() {
    step=$"Stop $prog..."

    # If PID file does not exist, paster returns 1 otherwise 0
    do_exec serve --stop-daemon --pid-file="$PID_FILE" >/dev/null
    ret=$?
    if [ $ret -eq 0 ]; then
        if [ "$KEEP_RUNTIME_DATA" -eq "0" ]; then
            rm -rf -- "$CACHE_DIR" "$SESSIONS_DIR" &>/dev/null
        fi
        rm -f -- "$lockfile"
    fi
    $ECHOFUNC -n "$step"; [ $ret -eq 0 ] && success || failure
    [ $USEFUNCS -eq 0 ] && $ECHOFUNC " $ret" || $ECHOFUNC
    return $ret
}

restart() {
    stop
    start
}

# kludge to make SELinux related changes compatible with condrestart
# triggered upon update in %postun scriptlet (rhbz#1023202, rhbz#1026374)
condrestart_scriptlet_kludge() {
    luci_pid=$(head -n1 "${PID_FILE}")
    if [ -n "${luci_pid}" ] \
      && [ "$(ps --no-header -Zp "${luci_pid}" 2>/dev/null \
              | awk '{print $1}' | cut -d: -f3)" = "initrc_t" ]; then
        # XXX /etc/init.d/functions:killproc
        kill -15 -- "${luci_pid}" 2>/dev/null
        if [ $? -eq 0 ]; then
            cnt=0
            while kill -0 -- "${luci_pid}" 2>/dev/null; do
                if [ "${cnt}" -ge 5 ]; then
                    kill -9 -- "${luci_pid}" 2>/dev/null
                    break
                fi
                sleep 1
                let cnt++
            done
        fi
        rm -f -- "${PID_FILE}"
        start
    else
        # non-kludge way
        status &>/dev/null || exit 0
        restart
    fi
}

status() {
    # If PID file exists and contains valid PID, paster returns 0 otherwise 1
    out=$(do_exec serve --status --pid-file="$PID_FILE" "$config" 2>&1)
    ret=$?
    echo "$out" | tail -1
    if [ $ret -ne 0 ]; then
        # Check the existence of the PID file to choose the right return code
        [ -e "$PID_FILE" ] && ret=1 || ret=3
    fi
    return $ret
}


case "$1" in
    start)
        entry_check || exit $?
        #status &>/dev/null && exit 0
        $1
        ;;
    stop)
        entry_check || exit $?
        status &>/dev/null || exit 0
        $1
        ;;
    restart)
        entry_check || exit $?
        $1
        ;;
    reload|force-reload)
        entry_check || exit $?
        status &>/dev/null || exit 7
        restart
        ;;
    condrestart|try-restart)
        entry_check || exit $?
        condrestart_scriptlet_kludge
        ;;
    status)
        entry_check || exit 4
        $1
        ;;
    backup-db)
        entry_check || exit $?
        status &>/dev/null
        if [ $? -eq 0 ]; then
            $ECHOFUNC "The database backup operation can proceed only when luci is stopped. Stop luci, then try again." >&2
            exit 1
        fi
        shift
        dump_database $*
        ;;
    restore-db)
        entry_check || exit $?
        status &>/dev/null
        if [ $? -eq 0 ]; then
            $ECHOFUNC "The database restore operation can proceed only when luci is stopped. Stop luci, then try again." >&2
            exit 1
        fi
        shift
        load_database $*
        ;;
    list-backups)
        entry_check || exit $?
        list_database_dumps
        ;;
    *)
        echo $"Usage: $0 {start|stop|status|reload|restart|condrestart|try-restart|backup-db|restore-db|list-backups}"
        exit 2
esac
