#!/bin/sh
# For boot-up and system shutdown, most UNIXes explicitly run a shell
# interpreter.  In that case, the interpreter line above is ignored.
# There are a few UNIXes (notably Darwin) that require the interpreter line.

# Copyright (c) 2001-2004, The HSQL Development Group
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# Neither the name of the HSQL Development Group nor the names of its
# contributors may be used to endorse or promote products derived from this
# software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
# OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


# $Id: hsqldb,v 1.38 2007/08/29 22:55:10 unsaved Exp $

# UNIX init script for HSQLDB.

# See the "UNIX Quick Start" chapter of the Hsqldb User Guide for how to
# use this file.

# This stuff will be ignored by systems that don't use chkconfig.
# chkconfig: 345 87 13
# description: Hsqldb, A High Performance Java Database
# pidfile: /var/run/hsqldb.pid
# config: /etc/sysconfig/hsqldb

# This stuff for SuSE Linux.  Contributed by M. Bisping.
### BEGIN INIT INFO
# Provides:          HSQLDB-Server
# Required-Start:    $syslog $remote_fs $network $named
# Required-Stop:
# Default-Start:     3 5
# Default-Stop:      0 1 2 6
# Short-Description: HSQLDB-Server
# Description:       Hsqldb, A High Performance Java Database Server
### END INIT INFO

# N.b.  Being a system script, this script does not use inherited variables.
# If you want to adjust a setting, edit the config file.

# Strategy of this init script is to avoid shell-specific functionality,
# and use only lowest-common-denominator Bourne capabilities.
# We don't include OS-specific functions, and we don't use shell-
# implementation-specific functionality like "echo ...\c" or "echo -n...".
# Since some Bourne shells don't support shell functions at all, we don't
# even define any local functions.

# This script has been generalized to the point that it can now "start"
# any combination of classes with the normal static main methods.
# However, you must still have a TARGET_CLASS which is a HSQLDB Server or
# WebServer  We connect to this server to check for status and to shut down.
# This is the one class which must use the default server property file
# name, either "server.properties" or "webserver.properties" (because
# we get the list of urlids to check from this file).
# Other than these constraints, you can supply invocation arguments to the
# Server/WebServer invocation, and can start as many other classes as you
# wish by using the INVOC_ADDL_ARGS setting (this includes running
# multiple HSQLDB Servers of various types).

# Recommended locations for configuration file:
#   Darwin, SunOS, Solaris:  /etc/hsqldb.conf
#              (However, Sunfreeware.com builds use /usr/local/etc).
#   Linux:  /etc/sysconfig
#   FreeBSD:  /usr/local/etc/hsqldb.cfg
# You can put it at any of these locations and it will be used.  For
# your sanity, only put a file at one of these locations.

#  -- blaine.simpson@admc.com

set +u

# This is only used for recursive invocations.
# Will not necessarily be set correctly at system bootup invocations
# (where it is sometimes invoked like "sh... /path/to/hsqldb start"),
# but, in those cases there will be no recursion.
INVOC_PATH=`dirname "$0"` || {
    echo "'dirname' failed" 1>&2
    exit 2
}
[ -n "$INVOC_PATH" ] && INVOC_PATH="${INVOC_PATH}/"

SYNTAX_MSG="SYNTAX:  ${INVOC_PATH}hsqldb start|stop|stopcompact|restart|restartcmpacted|status"

# You can override any of these default values in your config file:

# Allow this much time for background su command to start up and echo pid.
# (0 works for moderately fast servers).
SU_ECHO_SECS=1
# File used as semaphore.  If file is removed, a running pid checker
# process will exit.
PIDCHECKER_FLAGFILE=/tmp/pidchecker.run
# The following settings get overridden by optional setting in the config file.
# Time to allow for JVM to die after all HSQLDB instances stopped.
MAX_TERMINATE_SECS=1
# We require all Server/WebServer instances to be accessible within
# $MAX_START_SECS from when the Server/WebServer is started.
MAX_START_SECS=60
# Class in the org.hsqldb package to start.
# At this time, only the values "Server" or "WebServer" will work.
TARGET_CLASS=Server

CLIENT_JVMARGS=
SERVER_JVMARGS=
TLS_KEYSTORE=
TLS_PASSWORD=
CFGFILE=
LOGFILE=
PIDFILE=
BASEDIR=
AUTH_FILE=
SHUTDOWN_OPTION=
SERVER_ADDL_CLASSPATH=
INVOC_ADDL_ARGS=
case "`uname`" in
    Darwin)     # I.e. Mac OS X.  I don't know about older Mac OSes.
        LOGFILE=/var/log/hsqldb.log
        PIDFILE=/var/run/hsqldb.pid
    ;;
    Linux)
        LOGFILE=/var/log/hsqldb.log
        PIDFILE=/var/run/hsqldb.pid
    ;;
    FreeBSD)
        LOGFILE=/var/log/hsqldb.log
        PIDFILE=/var/run/hsqldb.pid
    ;;
    SunOS)
        LOGFILE=/var/log/hsqldb.log
        PIDFILE=/etc/hsqldb.pid
    ;;
    *)
        LOGFILE=/var/log/hsqldb.log
        PIDFILE=/etc/hsqldb.pid
    ;;
esac

for candidate in /etc/sysconfig/hsqldb /etc/hsqldb.conf /etc/hsqldb.cfg  \
    /usr/local/etc/hsqldb.cfg; do
    [ -f $candidate ] && {
        CFGFILE=$candidate
        break
    }
done
[ -n "$CFGFILE" ] || {
    echo "No global config file found in any of allowed locations" 1>&2
    exit 11
}

# Sanity check
[ -n "$LOGFILE" ] && [ -n "$PIDFILE" ] || {
    echo "Internal problem in init script" 1>&2
    exit 11
}

[ $# -eq 1 ] || {
    echo "$SYNTAX_MSG" 1>&2
    exit 4
}

# It would be nice to permit some uses, like "status" by non-root users,
# but for now our goal is a superuser init script.
[ -w / ] || {   # Very portable, but perhaps not perfect, test for superuser.
    echo "Only 'root' may use this init script" 1>&2
    exit 4
}

# Use bsd-style enable/disable if it's in place.
BSDCFG=
[ -r /etc/rc.conf ] && [ -f /etc/rc.conf ] && {
    . /etc/rc.conf
    BSDCFG=1
}
[ -r /etc/rc.conf.local ] && [ -f /etc/rc.conf.local ] && {
    . /etc/rc.conf.local
    BSDCFG=1
}
[ -n "$BSDCFG" ] && {
    case "$hsqldb_enable" in [Yy][Ee][Ss]);; [Oo][Nn]);; [Tt][Rr][Uu][Ee]);;
        *) exit 0;;  # Don't run if not enabled for BSD startup
    esac
}

COMMAND="$1"; shift

[ -r "$CFGFILE" ] || {
    echo "Unable to read config file '$CFGFILE'" 1>&2
    exit 2
}
[ -f "$CFGFILE" ] || {
    echo "'$CFGFILE' is not a regular file" 1>&2
    exit 2
}
HSQLDB_OWNER=
JAVA_EXECUTABLE=
HSQLDB_JAR_PATH=
SERVER_HOME=
SHUTDOWN_URLIDS=
. "$CFGFILE"
# Suffix delimiter to $SERVER_ADDL_CLASSPATH, if it is set.
[ -n "$SERVER_ADDL_CLASSPATH" ] &&
SERVER_ADDL_CLASSPATH="${SERVER_ADDL_CLASSPATH}:"
# Validate that config file sets all required variables.
[ -n "$JAVA_EXECUTABLE" ] && [ -n "$HSQLDB_JAR_PATH" ] &&
[ -n "$SERVER_HOME" ] || {
    echo "Config file '$CFGFILE' does not set one or more of following variables
    JAVA_EXECUTABLE, HSQLDB_JAR_PATH, SERVER_HOME" 1>&2
    exit 2
}
[ -d "$SERVER_HOME" ] || {
    echo "SERVER_HOME variable in '$CFGFILE' is set to a non-directory." 1>&2
    exit 2
}
[ -f "$JAVA_EXECUTABLE" ] && [ -f "$HSQLDB_JAR_PATH" ] || {
    echo "JAVA_EXECUTABLE or HSQLDB_JAR_PATH in '$CFGFILE' is set to a non-file." 1>&2
    exit 2
}

# PROPERTY_FILE is a derived value.
case "$TARGET_CLASS" in
    Server) PROPERTY_FILE="$SERVER_HOME"/server.properties;;
    WebServer) PROPERTY_FILE="$SERVER_HOME"/webserver.properties;;
    *)
        echo 'Unsupported value for $TARGET_CLASS:  '"$TARGET_CLASS" 1>&2
        exit 2;;
esac
[ -f "$PROPERTY_FILE" ] || {
    echo "'$PROPERTY_FILE' is missing" 1>&2
    exit 2
}
[ -r "$PROPERTY_FILE" ] || {
    echo "'$PROPERTY_FILE' isn't readable" 1>&2
    exit 2
}
[ -r "$HSQLDB_JAR_PATH" ] || {
    echo "'$HSQLDB_JAR_PATH' isn't readable" 1>&2
    exit 2
}
[ -x "$JAVA_EXECUTABLE" ] || {
    echo "No Java executable found at '$JAVA_EXECUTABLE'" 1>&2
    exit 2
}

# "chown" lives here on some UNIXes.
PATH="$PATH:/usr/sbin"

# Make a good effort (but not bullet-proof) check on permissions of the
# auth file.  Unfortunately, if auth-file is not specified, this depends
# upon both (a) $HOME being set; and (b) SqlToolSprayer and SqlTool defaults.
# On the other hand, it works great if AUTH_FILE is set explicitly by user.
if [ -z "$AUTH_FILE" ] && [ -z "$HOME" ]; then
    : # Lousy init environment didn't set $HOME, so can't find dflt cfg file.
else
    _AUTH_TEST_PATH="$AUTH_FILE"
    [ -n "${_AUTH_TEST_PATH}" ] || _AUTH_TEST_PATH="$HOME/sqltool.rc"
    [ -f "$_AUTH_TEST_PATH" ] || {
        echo "No auth file found at '$_AUTH_TEST_PATH'" 1>&2
        exit 2
    }
    [ -r "$_AUTH_TEST_PATH" ] || {
        echo "Auth file '$_AUTH_TEST_PATH' not readable" 1>&2
        exit 2
    }
    ls -ld "$_AUTH_TEST_PATH" | grep '^-..------' > /dev/null 2>&1 || {
        echo "Fix permissions on '$_AUTH_TEST_PATH' like 'chmod 600 $_AUTH_TEST_PATH'" 1>&2
        exit 2
    }
fi

# Set HSQLDB_PID according to pid file.
HSQLDB_PID=
[ -r "$PIDFILE" ]  && {
    [ -f "$PIDFILE" ] || {
        echo "'$PIDFILE' is not a regular file" 1>&2
        exit 6
    }
    [ -w "$PIDFILE" ] || {
        echo "'$PIDFILE' is not writable" 1>&2
        exit 6
    }
    HSQLDB_PID="`cat $PIDFILE`" || {
        echo "Failed to read pid file '$PIDFILE'" 1>&2
        exit 6
    }
    case "$HSQLDB_PID" in
        *[a-zA-Z/!@#$%*+=_~]*) HSQLDB_PID=;;
        *'^'*) HSQLDB_PID=;;
    esac
    [ -n "$HSQLDB_PID" ] || {
        echo "Pid file '$PIDFILE' does not contain a valid process identifier" 1>&2
        exit 6
    }
    kill -0 "$HSQLDB_PID" > /dev/null 2>&1 || {
        echo 'Removing stale pid file'
        rm -f "$PIDFILE" || {
            echo "Failed to remove pid file '$PIDFILE'" 1>&2
            exit 6
        }
        HSQLDB_PID=
    }
    #echo "PID is ($HSQLDB_PID)"
}

case "$COMMAND" in
    status)
        [ -n "$HSQLDB_PID" ] || {
            echo "I don't know of any running hsqldb server."
            exit 0
        }
        echo "There is an hsqldb server loaded from $HSQLDB_JAR_PATH
running with pid $HSQLDB_PID."
        # I would give a nice ps command here, were ps not so damned
        # OS-specific.
        AUTH_FILE_SWITCH=
        # N.b., there will be a problem if there are special characters or
        # spaces inside of $AUTH_FILE.
        [ -n "$AUTH_FILE" ] &&
        AUTH_FILE_SWITCH="-Dsqltoolsprayer.rcfile=$AUTH_FILE"
        # Might as well set CLASSPATH for a cleaner command.
        CLASSPATH="$HSQLDB_JAR_PATH"
        export CLASSPATH
        export PATH   # Required only for some funny init environments.
        exec "$JAVA_EXECUTABLE" $AUTH_FILE_SWITCH $CLIENT_JVMARGS \
            "-Dsqltoolsprayer.propfile=$PROPERTY_FILE" \
            "-Dsqltoolsprayer.monfile=$PIDFILE" \
            org.hsqldb.util.SqlToolSprayer '--;'
    ;;
    start)
        [ -n "$HSQLDB_PID" ] && {
        echo "There is already a hsqldb server running with pid $HSQLDB_PID." 1>&2
            exit 1
        }
        TLS_SWITCHES=
        [ -n "$TLS_KEYSTORE" ] &&
        TLS_SWITCHES="-Djavax.net.ssl.keyStore=$TLS_KEYSTORE -Djavax.net.ssl.keyStorePassword=$TLS_PASSWORD"
        if [ -n "$HSQLDB_OWNER" ]; then
            touch "$PIDFILE" || {
                echo "Failed to create pid file" 1>&2
                exit 1
            }
            chown "$HSQLDB_OWNER" "$PIDFILE" || {
                echo "Failed to chown pid file to '$HSQLDB_OWNER'" 1>&2
                exit 1
            }
            # Some OSes choke if there are newlines in this string.
            # N.b.!!!  The shell of the -c command is the target user's default
            # login shell, so keep this command shell-independent!
            nohup su "$HSQLDB_OWNER" -c "cd '$SERVER_HOME' && echo "'$$'" > '$PIDFILE' && exec '$JAVA_EXECUTABLE' $SERVER_JVMARGS $TLS_SWITCHES -classpath '${SERVER_ADDL_CLASSPATH}${HSQLDB_JAR_PATH}' org.hsqldb.util.MainInvoker org.hsqldb.$TARGET_CLASS $INVOC_ADDL_ARGS" >> "$LOGFILE" 2>&1 &
        else
            cd "$SERVER_HOME" || {
                echo "Failed to cd to '$SERVER_HOME'" 1>&2
                exit 1
            }
            export JAVA_EXECUTABLE
            export HSQLDB_JAR_PATH
            export PIDFILE
            export TLS_SWITCHES
            export SERVER_JVMARGS
            export TARGET_CLASS
            export INVOC_ADDL_ARGS
            nohup sh -c '
                echo $$ > "$PIDFILE" || {
                    echo "Failed to write pid to pid file" 1>&2
                    exit 1
                }
                eval exec "$JAVA_EXECUTABLE" $SERVER_JVMARGS $TLS_SWITCHES -classpath "${SERVER_ADDL_CLASSPATH}${HSQLDB_JAR_PATH}"  org.hsqldb.util.MainInvoker org.hsqldb.$TARGET_CLASS $INVOC_ADDL_ARGS
            ' >> "$LOGFILE" 2>&1 &
        fi
        sleep $SU_ECHO_SECS  # Make sure bg commands have time to echo pid.
        AUTH_FILE_SWITCH=
        # N.b., there will be a problem if there are special characters or
        # spaces inside of $AUTH_FILE.
        [ -n "$AUTH_FILE" ] &&
        AUTH_FILE_SWITCH="-Dsqltoolsprayer.rcfile=$AUTH_FILE"
        # Might as well set CLASSPATH for a cleaner command.
        CLASSPATH="$HSQLDB_JAR_PATH"
        export CLASSPATH
        export PATH   # Required only for some funny init environments.
        # There are many reasons why we could fail to read the pid file,
        # but regardless of why, the pid file does not contain a valid pid.
        touch "$PIDCHECKER_FLAGFILE" || {
            echo "Failed to touch file '$PIDCHECKER_FLAGFILE'" 1>&2
            exit 1
        }
        export PIDCHECKER_FLAGFILE
        export PIDFILE
        (
            while true; do
                # -a and -e tests are not portable.
                [ -f "$PIDCHECKER_FLAGFILE" ] || exit 0
                kill -0 "`cat $PIDFILE`" > /dev/null 2>&1 || {
                    rm -f "$PIDFILE" "$PIDCHECKER_FLAGFILE"
                    exit 1
                }
                sleep 1
            done
        ) &
        "$JAVA_EXECUTABLE" $AUTH_FILE_SWITCH $CLIENT_JVMARGS \
            "-Dsqltoolsprayer.propfile=$PROPERTY_FILE" \
            "-Dsqltoolsprayer.monfile=$PIDFILE" \
            "-Dsqltoolsprayer.maxtime=${MAX_START_SECS}000" \
            org.hsqldb.util.SqlToolSprayer '--;' && {
            rm -f "$PIDCHECKER_FLAGFILE"
            echo "org.hsqldb.$TARGET_CLASS started with pid `cat $PIDFILE`"
            exit 0
        }
        rm -f "$PIDCHECKER_FLAGFILE"
        echo "Failed to start org.hsqldb.$TARGET_CLASS.
See log file '$LOGFILE'." 1>&2
        exit 1
    ;;
    stop|stopcompact)
        [ "$COMMAND" = stopcompact ] && SHUTDOWN_OPTION='compact'
        [ -n "$HSQLDB_PID" ] || {
            echo "I don't know of any running hsqldb server." 1>&2
            exit 1
        }
        AUTH_FILE_SWITCH=
        # N.b., there will be a problem if there are special characters or
        # spaces inside of $AUTH_FILE.
        [ -n "$AUTH_FILE" ] &&
        AUTH_FILE_SWITCH="-Dsqltoolsprayer.rcfile=$AUTH_FILE"
        # Might as well set CLASSPATH for a cleaner command.
        CLASSPATH="$HSQLDB_JAR_PATH"
        export CLASSPATH
        export PATH   # Required only for some funny init environments.
        "$JAVA_EXECUTABLE" $AUTH_FILE_SWITCH $CLIENT_JVMARGS \
            "-Dsqltoolsprayer.propfile=$PROPERTY_FILE" \
            org.hsqldb.util.SqlToolSprayer "shutdown ${SHUTDOWN_OPTION};" \
            $SHUTDOWN_URLIDS
        sleep $MAX_TERMINATE_SECS  # Give the JVM a sec. or 2 to fully exit.
        kill -0 "$HSQLDB_PID" > /dev/null 2>&1 && {
            echo "WARNING:  hsqldb is still running!" 1>&2
            exit 1
        }
        rm -f "$PIDFILE" || {
            echo "Failed to remove pid file '$PIDFILE'" 1>&2
            exit 1
        }
        echo "Successful shutdown ${SHUTDOWN_OPTION} (for the $TARGET_CLASS process)!"
        exit 0
    ;;
    restart|restartcompacted)
        STOP_COMMAND=stop
        [ "$COMMAND" = restartcompacted ] && STOP_COMMAND=stopcompact
        "${INVOC_PATH}"hsqldb $STOP_COMMAND || exit $?
        exec "${INVOC_PATH}"/hsqldb start
    ;;
    *)
        echo "$SYNTAX_MSG" 1>&2
        exit 5
    ;;
esac
