#!/bin/sh

set -e

usage()
{
    echo "Usage: $(basename $0) [OPTION]... COMMAND"
    echo
    echo "Valid options are:"
    echo "   -c <file>      store PID of child process in <file>"
    echo "   -d <seconds>   delay between automatic restart"
    echo "   -p <file>      store PID of daemon wrapper in <file>"
    echo "   -r <file>      restart daemon on termination unless <file> exists"
    echo "   -u <user>      change effective UID to <user>"
    echo "   -o <file>      redirect output to <file>"
    echo "   -l             log COMMAND start, restart and stop (only with -r)"
    echo "   -f             fork"
    echo
    echo "If the output is redirected with -o then this will happen as the"
    echo "user invoking daemon, not as the user specified with -u. The"
    echo "PID file specified with -p on the other hand is updated by the"
    echo "unprivileged user, so the script ensures that the file is writable"
    echo "before dropping privileges."
    echo
    echo "Almost all error messages are redirected when -o is specified,"
    echo "including failure to execute the command. The daemon does not fully"
    echo "detach from the shell unless -o is specified. Use -o /dev/null if"
    echo "the output is not relevant."
    echo
    echo "The PID stored with -c is always the PID of the last invocation of"
    echo "COMMAND. The PID stored with -p is the PID of process that invoked"
    echo "COMMAND. In case -f is specified and -r is not specified, then the"
    echo "daemon wrapper will terminate after COMMAND has been executed. In"
    echo "that case -p will store the PID of COMMAND."
    exit 2
} 1>&2

log()
{
    echo "`date '+%Y-%m-%d %H:%M:%S'` $*"
}

storePid() # in $1=pid, in $2=file
{
    if [ -n "$2" ]; then
        echo $1 > "$2"
    fi
}

run()
{
    "$@" &
    storePid $! "$childPid"
}

loop()
{
    if [ -n "$shouldLog" ]; then
        echo
        log "Launching $*"
    fi

    while run "$@"; wait $!; [ ! -f "$restart" ]; do
        if [ -n "$shouldLog" ]; then
            log "Process terminated unexpectedly"
        fi

        if [ -n "$delay" ] ; then
            if [ -n "$shouldLog" ]; then
                log "Sleeping $delay seconds"
            fi
            sleep "$delay"
        fi

        if [ -n "$shouldLog" ]; then
            echo
            log "Restarting $*"
        fi
    done

    if [ -n "$shouldLog" ]; then
        log "Process terminated"
    fi
}

createFileWithOwner() # in $1 = file, in $2 = owner
{
    if [ -n "$1" ]; then
        touch "$1"
        chown "$2" "$1"
    fi
}

while getopts c:d:p:r:u:o:lf opt "$@"; do
    case $opt in
        c) childPid="$OPTARG";;
        d) delay="$OPTARG";;
        p) pid="$OPTARG";;
        r) restart="$OPTARG";;
        u) user="$OPTARG";;
        o) output="$OPTARG";;
        l) shouldLog=1;;
        f) fork=1;;
    esac
done

shift `expr $OPTIND - 1`

[ $# -lt 1 ] && usage

# Redirect if -o is given
exec </dev/null
if [ -n "$output" ]; then
    exec 1>> "$output" 2>&1
fi

# Switch user if -u is given
if [ -n "$user" ]; then
    # Make sure the user is able to write to the PID file
    createFileWithOwner "$childPid" "$user"
    createFileWithOwner "$pid" "$user"

    # To preserve white space, we need to quote each argument.  Notice
    # that ${x:+-x "$x"} doesn't work on Solaris due to the space in
    # the expression.
    param="${fork:+-f} ${shouldLog:+-l} ${childPid:+-c} ${childPid:+\"$childPid\"} ${pid:+-p} ${pid:+\"$pid\"} ${restart:+-r} ${restart:+\"$restart\"} ${delay:+-d} ${delay:+\"$delay\"}"
    for i in "$@"; do
        param="$param \"$i\""
    done

    exec su "$user" -c "/bin/sh \"$0\" $param"
fi

# Ensure that PID can be created (eg directory exists)
touch $pid

# Run the program
if [ -n "$restart" ]; then
    if [ -n "$fork" ]; then
        loop "$@" &
        storePid $! "$pid"
    else
        storePid $$ "$pid"
        loop "$@"
    fi
else
    run "$@"
    if [ -n "$fork" ]; then
        storePid $! "$pid"
    else
        storePid $$ "$pid"
        wait $!
    fi
fi
