#!/bin/sh

#
# Copyright (c) 1999, 2006 Tanuki Software Inc.
#
# Java Service Wrapper sh script.  Suitable for starting and stopping
#  wrapped Java applications on UNIX platforms.
#

#-----------------------------------------------------------------------------
# These settings can be modified to fit the needs of your application

# Application
APP_NAME="Freenet"
APP_LONG_NAME="Freenet 0.7"

# Wrapper
WRAPPER_CMD="./bin/wrapper"
WRAPPER_CONF="./wrapper.conf"

# Priority at which to run the wrapper.  See "man nice" for valid priorities.
#  nice is only used if a priority is specified.

# Note that Freenet will scale its usage within the specifed niceness, some
# threads will have a lower priority (higher nice value) than this. Also please
# don't renice Freenet once it's started.
PRIORITY=10

# Location of the pid file.
PIDDIR="."

# If uncommented, causes the Wrapper to be shutdown using an anchor file.
#  When launched with the 'start' command, it will also ignore all INT and
#  TERM signals.
IGNORE_SIGNALS=true

# If specified, the Wrapper will be run as the specified user.
# IMPORTANT - Make sure that the user has the required privileges to write
#  the PID file and wrapper.log files.  Failure to be able to write the log
#  file will cause the Wrapper to exit without any way to write out an error
#  message.
# NOTE - This will set the user which is used to run the Wrapper as well as
#  the JVM and is not useful in situations where a privileged resource or
#  port needs to be allocated prior to the user being changed.
#RUN_AS_USER=

# The following two lines are used by the chkconfig command. Change as is
#  appropriate for your application.  They should remain commented.
# chkconfig: 2345 20 80
# description: @app.long.name@

# Do not modify anything beyond this point
#-----------------------------------------------------------------------------

if [ "X`id -u`" = "X0" -a -z "$RUN_AS_USER" ]
then
    echo "Do not run this script as root."
    exit 1
fi

# Get the fully qualified path to the script
case $0 in
    /*)
        SCRIPT="$0"
        ;;
    *)
        PWD=`pwd`
        SCRIPT="$PWD/$0"
        ;;
esac

# Resolve the true real path without any sym links.
CHANGED=true
while [ "X$CHANGED" != "X" ]
do
    # Change spaces to ":" so the tokens can be parsed.
    SCRIPT=`echo $SCRIPT | sed -e 's; ;:;g'`
    # Get the real path to this script, resolving any symbolic links
    TOKENS=`echo $SCRIPT | sed -e 's;/; ;g'`
    REALPATH=
    for C in $TOKENS; do
        REALPATH="$REALPATH/$C"
        while [ -h "$REALPATH" ] ; do
            LS="`ls -ld "$REALPATH"`"
            LINK="`expr "$LS" : '.*-> \(.*\)$'`"
            if expr "$LINK" : '/.*' > /dev/null; then
                REALPATH="$LINK"
            else
                REALPATH="`dirname "$REALPATH"`""/$LINK"
            fi
        done
    done

    # Change ":" chars back to spaces.
    REALPATH="`echo $REALPATH | sed -e 's;:; ;g'`"
    SCRIPT="`echo $SCRIPT | sed -e 's;:; ;g'`"

    if [ "$REALPATH" = "$SCRIPT" ]
    then
        CHANGED=""
    else
        SCRIPT="$REALPATH"
    fi
done

# Change the current directory to the location of the script
cd "`dirname \"$REALPATH\"`"
REALDIR="`pwd`"

# Resolve the os
DIST_OS=`uname -s | tr [:upper:] [:lower:] | tr -d " \t\r\n"`
case "$DIST_OS" in
    'sunos')
        DIST_OS="solaris"
        ;;
    'hp-ux' | 'hp-ux64')
        DIST_OS="hpux"
        ;;
    'darwin' | 'oarwin')
        DIST_OS="macosx"
        ;;
    'unix_sv')
        DIST_OS="unixware"
        ;;
    'freebsd' | 'openbsd' | 'netbsd')
        DIST_OS="freebsd"
        ;;
esac

if [ "$DIST_OS" = "macosx" ]
then
	# If there's a modern (1.8+) JVM, use that...
	JAVA_HOME="/Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/"
	if ! test -e "$JAVA_HOME/bin/java"
	then
		# otherwise hope for the best
		JAVA_HOME="`/usr/libexec/java_home -v 1.6+`"
	fi
fi

JAVA_REAL_IMPL="java"
# Attempt to second-guess the user and find a JRE we can actually use...
# The wrapper needs an ELF binary... some distros are using a shell wrapper
# see bug #6217
#
# /usr/lib64/jvm/jre-1.7.0-openjdk/bin/java SuSE
# /usr/lib/jvm/java-7-openjdk/jre/bin/java Debian
CANDIDATES="$JAVA_HOME/bin/java
`which java`
/etc/java-config-2/current-system-vm/bin/java
/usr/lib/jvm/java-default-runtime/bin/java
/usr/lib/jvm/java-8-openjdk/jre/bin/java
/usr/lib/jvm/java-7-openjdk/jre/bin/java
/usr/lib64/jvm/jre-1.8.0-openjdk/bin/java
/usr/lib64/jvm/jre-1.7.0-openjdk/bin/java"

# set IFS to newline in portable way to split the vertical CANDIDATES list, allows for whitespace in paths
set -f; IFS='
'
for candidate in $CANDIDATES
do
	if test -s "$candidate"
	then
		if head -1 "$candidate"|grep '^#' >/dev/null 2>&1
		then
			echo "Your java executable at $candidate is a script... looking for alternatives..."
		elif echo "$candidate"|grep -i 'gcj' >/dev/null 2>&1
		then
			echo "Freenet won't work well with GCJ, ignoring $candidate"
		else
			if test -n "$JAVA_HOME" -a "$JAVA_HOME/bin/java" != "$candidate"
			then
				echo "Your JAVA_HOME seems to be incompatible with your PATH, ignoring it."
				unset JAVA_HOME
			fi
			echo "Your java executable at $candidate seems suitable"
			ESCAPED_CANDIDATE=`echo "$candidate"|sed 's/\(\/\)/\\\\\1/g'`
			perl -i -pe "s/^wrapper.java.command=.*$/wrapper.java.command=$ESCAPED_CANDIDATE/" "$REALDIR/wrapper.conf"
			JAVA_REAL_IMPL="$candidate"
			break
		fi
	fi
done
set +f; unset IFS

# and get java implementation too, Sun JDK or Kaffe
JAVA_IMPL=`$JAVA_REAL_IMPL -version 2>&1 | head -n 1 | cut -f1 -d' '`

# If the PIDDIR is relative, set its value relative to the full REALPATH to avoid problems if
#  the working directory is later changed.
FIRST_CHAR="`echo $PIDDIR | cut -c1,1`"
if [ "$FIRST_CHAR" != "/" ]
then
    PIDDIR="$REALDIR/$PIDDIR"
fi
# Same test for WRAPPER_CMD
FIRST_CHAR="`echo $WRAPPER_CMD | cut -c1,1`"
if [ "$FIRST_CHAR" != "/" ]
then
    WRAPPER_CMD="$REALDIR/$WRAPPER_CMD"
fi
# Same test for WRAPPER_CONF
FIRST_CHAR="`echo \"$WRAPPER_CONF\" | cut -c1,1`"
if [ "$FIRST_CHAR" != "/" ]
then
    WRAPPER_CONF="$REALDIR/$WRAPPER_CONF"
fi

# Process ID
ANCHORFILE="$PIDDIR/$APP_NAME.anchor"
PIDFILE="$PIDDIR/$APP_NAME.pid"
LOCKDIR="$REALDIR"
LOCKFILE="$LOCKDIR/$APP_NAME"
pid=""

# Resolve the architecture
DIST_ARCH=`uname -m | tr [:upper:] [:lower:] | tr -d " \t\r\n"`
case "$DIST_ARCH" in
    'amd64' | 'ia32' | 'i386' | 'i486' | 'i586' | 'i686' | 'x86_64')
        DIST_ARCH="x86"
        ;;
    'ia64' | 'ia-64')
	DIST_ARCH="ia64"
	;;
    'ip27' | 'mips')
        DIST_ARCH="mips"
        ;;
    'powermacintosh' | 'power' | 'powerpc' | 'power_pc' | 'ppc64')
        DIST_ARCH="ppc"
        ;;
    'pa_risc' | 'pa-risc')
        DIST_ARCH="parisc"
        ;;
    'sun4u' | 'sparcv9')
        DIST_ARCH="sparc"
        ;;
    '9000/800')
        DIST_ARCH="parisc"
        ;;
    armv*)
        if [ -z "`readelf -A /proc/self/exe | grep Tag_ABI_VFP_args`" ] ; then
            DIST_ARCH="armel"
        else
            DIST_ARCH="armhf"
        fi
        ;;
esac

# Check if we are running on 64bit platform, seems like a workaround for now...
DIST_BIT=`uname -m | tr [:upper:] [:lower:] | tr -d " \t\r\n"`
case "$DIST_BIT" in
    'amd64' | 'ia64' | 'x86_64' | 'ppc64')
        DIST_BIT="64"
        ;;
#    'pa_risc' | 'pa-risc') # Are some of these 64bit? Least not all...
#       BIT="64"
#        ;;
    'sun4u' | 'sparcv9') # Are all sparcs 64?
        DIST_BIT="64"
        ;;
#    '9000/800')
#       DIST_BIT="64"
#        ;;
    *) # In any other case default to 32
        DIST_BIT="32"
        ;;
esac

# Decide on the wrapper binary to use.
# 64bit wrapper by default on 64bit platforms, because
# they might not have 32bit emulation libs installed. 
# For macosx, we also want to look for universal binaries.

WRAPPER_TEST_CMD="$WRAPPER_CMD-$DIST_OS-$DIST_ARCH-$DIST_BIT"

if [ -x "$WRAPPER_TEST_CMD" ]
then
    WRAPPER_CMD="$WRAPPER_TEST_CMD"
else
    if [ "$DIST_OS" = "macosx" ] # Some osx weirdness, someone please check that this still works
    then
	# We don't have a binary for OS/X on 64-bit at present.
	DIST_BIT="32"
        WRAPPER_TEST_CMD="$WRAPPER_CMD-$DIST_OS-universal-$DIST_BIT"
        if [ -x "$WRAPPER_TEST_CMD" ]
        then
            WRAPPER_CMD="$WRAPPER_TEST_CMD"
        else
            WRAPPER_TEST_CMD="$WRAPPER_CMD-$DIST_OS-$DIST_ARCH-$DIST_BIT"
            if [ -x "$WRAPPER_TEST_CMD" ]
            then
                WRAPPER_CMD="$WRAPPER_TEST_CMD"
            else
                WRAPPER_TEST_CMD="$WRAPPER_CMD-$DIST_OS-universal-$DIST_BIT"
                if [ -x "$WRAPPER_TEST_CMD" ]
                then
                    WRAPPER_CMD="$WRAPPER_TEST_CMD"
                else
                    if [ ! -x "$WRAPPER_CMD" ]
                    then
                        echo "Unable to locate any of the following binaries:"
                        echo "  $WRAPPER_CMD-$DIST_OS-$DIST_ARCH-$DIST_BIT"
                        echo "  $WRAPPER_CMD-$DIST_OS-universal-$DIST_BIT"
                        echo "  $WRAPPER_CMD"
			#
			# We need -Djava.net.preferIPv4Stack=true on FreeBSD, otherwise recent jvms thow an IllegalArgumentException when we create the socket
			#
                        NO_WRAPPER="$JAVA_REAL_IMPL -Djava.net.preferIPv4Stack=true -cp freenet-ext.jar:freenet.jar:bcprov-jdk15on-154.jar freenet.node.NodeStarter"
                    fi
                fi
            fi
        fi
    else
        if [ ! -x "$WRAPPER_CMD" ]
        then
            echo "Unable to locate any of the following binaries:"
            echo "  $WRAPPER_CMD-$DIST_OS-$DIST_ARCH-$DIST_BIT"
            echo "  $WRAPPER_CMD"
            NO_WRAPPER="$JAVA_REAL_IMPL -cp freenet-ext.jar:freenet.jar:bcprov-jdk15on-154.jar freenet.node.NodeStarter"
        fi
    fi
fi

# Build the nice clause
if [ "X$PRIORITY" = "X" ]
then
    CMDNICE=""
else
    CMDNICE="nice -$PRIORITY"
fi

# Build the anchor file clause.
if [ "X$IGNORE_SIGNALS" = "X" ]
then
   ANCHORPROP=
   IGNOREPROP=
else
   ANCHORPROP=wrapper.anchorfile=\"$ANCHORFILE\"
   IGNOREPROP=wrapper.ignore_signals=TRUE
fi

# Build the lock file clause.  Only create a lock file if the lock directory exists on this platform.
if [ -d "$LOCKDIR" ]
then
    LOCKPROP=wrapper.lockfile=\"$LOCKFILE\"
else
    LOCKPROP=
fi

checkUser() {
    # Check the configured user.  If necessary rerun this script as the desired user.
    if [ "X$RUN_AS_USER" != "X" ]
    then
        # Resolve the location of the 'id' command
        IDEXE="/usr/xpg4/bin/id"
        if [ ! -x $IDEXE ]
        then
            IDEXE="/usr/bin/id"
            if [ ! -x $IDEXE ]
            then
                echo "Unable to locate 'id'."
                echo "Please report this message along with the location of the command on your system."
                exit 1
            fi
        fi
    
        if [ "`$IDEXE -u -n`" = "$RUN_AS_USER" ]
        then
            # Already running as the configured user.  Avoid password prompts by not calling su.
            RUN_AS_USER=""
        fi
    fi
    if [ "X$RUN_AS_USER" != "X" ]
    then
        # If LOCKPROP and $RUN_AS_USER are defined then the new user will most likely not be
        # able to create the lock file.  The Wrapper will be able to update this file once it
        # is created but will not be able to delete it on shutdown.  If $2 is defined then
        # the lock file should be created for the current command
        if [ "X$LOCKPROP" != "X" ]
        then
            if [ "X$2" != "X" ]
            then
                # Resolve the primary group 
                RUN_AS_GROUP=`groups $RUN_AS_USER | awk '{print $3}' | tail -1`
                if [ "X$RUN_AS_GROUP" = "X" ]
                then
                    RUN_AS_GROUP=$RUN_AS_USER
                fi
                touch "$LOCKFILE"
                chown $RUN_AS_USER:$RUN_AS_GROUP "$LOCKFILE"
            fi
        fi

        # Still want to change users, recurse.  This means that the user will only be
        #  prompted for a password once.
        su -m $RUN_AS_USER -c "$REALPATH $1"

        # Now that we are the original user again, we may need to clean up the lock file.
        if [ "X$LOCKPROP" != "X" ]
        then
            getpid
            if [ "X$pid" = "X" ]
            then
                # Wrapper is not running so make sure the lock file is deleted.
                if [ -f "$LOCKFILE" ]
                then
                    rm "$LOCKFILE"
                fi
            fi
        fi

        exit 0
    fi
}

getpid() {
    if [ -f "$PIDFILE" ]
    then
        if [ -r "$PIDFILE" ]
        then
            pid="`cat \"$PIDFILE\"`"
            if [ "X$pid" != "X" ]
            then
                # It is possible that 'a' process with the pid exists but that it is not the
                #  correct process.  This can happen in a number of cases, but the most
                #  common is during system startup after an unclean shutdown.
                # So make sure the process is one of "ours" -- that we can send
		# a signal to it.  (We don't use ps(1) because that's neither
		# safe nor portable.
		if ! kill -0 $pid 2>/dev/null
                then
                    # This is a stale pid file.
                    rm -f "$PIDFILE"
                    echo "Removed stale pid file: $PIDFILE"
                    pid=""
                fi
		# Sometimes the pid exists and it's ours!
		if [ "$DIST_OS" = "linux" ]
		then
			if ! test -f /proc/$pid/cwd/Freenet.pid
			then
                    		# This is a stale pid file.
                    		rm -f "$PIDFILE"
                    		echo "Removed stale pid file2: $PIDFILE"
                    		pid=""
		    
			fi
		fi
            fi
        else
            echo "Cannot read $PIDFILE."
            exit 1
        fi
    fi
}

testpid() {
    if ! kill -0 $pid 2>/dev/null
    then
        # Process is gone so remove the pid file.
        rm -f "$PIDFILE"
        pid=""
    fi
}

console() {
    echo "Running $APP_LONG_NAME..."
    getpid
    if [ "X$pid" = "X" ]
    then
        COMMAND_LINE="$CMDNICE \"$WRAPPER_CMD\" \"$WRAPPER_CONF\" wrapper.syslog.ident=\"$APP_NAME\" wrapper.pidfile=\"$PIDFILE\" $LDPROP $ANCHORPROP $LOCKPROP"
        eval $COMMAND_LINE
    else
        echo "$APP_LONG_NAME is already running."
        exit 1
    fi
}
 
start() {
    echo "Starting $APP_LONG_NAME..."
    getpid
    if [ "X$pid" = "X" ]
    then
        if [ "$NO_WRAPPER" ] # Check if we don't have usable wrapper, and run without it
        then
            echo ""
	    echo "Let's start the node without the wrapper, you'll have to daemonize it yourself."
            eval $NO_WRAPPER
        else                 # Otherwise use the wrapper
            COMMAND_LINE="$CMDNICE \"$WRAPPER_CMD\" \"$WRAPPER_CONF\" wrapper.syslog.ident=\"$APP_NAME\" wrapper.pidfile=\"$PIDFILE\" $LDPROP wrapper.daemonize=TRUE $ANCHORPROP $IGNOREPROP $LOCKPROP"
            eval $COMMAND_LINE
        fi
    else
        echo "$APP_LONG_NAME is already running."
        exit 1
    fi
}
 
stopit() {
    echo "Stopping $APP_LONG_NAME..."
    getpid
    if [ "X$pid" = "X" ]
    then
        echo "$APP_LONG_NAME was not running."
    else
        if [ "X$IGNORE_SIGNALS" = "X" ]
        then
            # Running so try to stop it.
            kill $pid
            if [ $? -ne 0 ]
            then
                # An explanation for the failure should have been given
                echo "Unable to stop $APP_LONG_NAME."
                exit 1
            fi
        else
            rm -f "$ANCHORFILE"
            if [ -f "$ANCHORFILE" ]
            then
                # An explanation for the failure should have been given
                echo "Unable to stop $APP_LONG_NAME."
                exit 1
            fi
        fi

        # We can not predict how long it will take for the wrapper to
        #  actually stop as it depends on settings in $WRAPPER_CONF
        #  Loop until it does.
        savepid=$pid
        CNT=0
        TOTCNT=0
        while [ "X$pid" != "X" ]
        do
            # Show a waiting message every 5 seconds.
            if [ "$CNT" -lt "5" ]
            then
                CNT=`expr $CNT + 1`
            else
                echo "Waiting for $APP_LONG_NAME to exit..."
                CNT=0
            fi
            TOTCNT=`expr $TOTCNT + 1`

            sleep 1

            testpid
        done

        pid=$savepid
        testpid
        if [ "X$pid" != "X" ]
        then
            echo "Failed to stop $APP_LONG_NAME."
            exit 1
        else
            echo "Stopped $APP_LONG_NAME."
        fi
    fi
}

status() {
    getpid
    if [ "X$pid" = "X" ]
    then
        echo "$APP_LONG_NAME is not running."
        exit 1
    else
        echo "$APP_LONG_NAME is running ($pid)."
        exit 0
    fi
}

dump() {
    echo "Dumping $APP_LONG_NAME..."
    getpid
    if [ "X$pid" = "X" ]
    then
        echo "$APP_LONG_NAME was not running."

    else
        kill -QUIT $pid

        if [ $? -ne 0 ]
        then
            echo "Failed to dump $APP_LONG_NAME."
            exit 1
        else
            echo "Dumped $APP_LONG_NAME."
        fi
    fi
}

getHardwareMemory() {
    detected=0
    if [ $DIST_OS = "macosx" ]
    then
       detected=$((`sysctl hw.memsize | sed s/"hw.memsize: "//`/1024/1024))
    elif [ $DIST_OS = "freebsd" ]
    then
       detected=$((`sysctl hw.physmem | sed s/"hw.physmem: "//`/1024/1024))
    elif [ $DIST_OS = "linux" ]
    then
       detected=$((`cat /proc/meminfo | grep MemTotal | awk '{print $2}'`/1024))
    fi
# Exit codes only support values between 0 and 255. So use stdout.
    echo $detected
}

setMemoryLimitIfNeeded() {
   if [ -f memory.autolimit ]
   then
       return
   else
       touch memory.autolimit
       echo OS is $DIST_OS
       currentmem=`getHardwareMemory`
       echo Detected memory: $currentmem
       echo $currentmem > memory.autolimit	
       if [ $currentmem -le 256 ]
       then
           echo "not enough memory to run"
	   rm memory.autolimit
           exit 1
       elif [ $currentmem -le 512 ]
       then
           echo "128" > memory.autolimit
           memorylimit=128
       elif [ $currentmem -le 1024 ] 
       then 
           echo "192" > memory.autolimit
           memorylimit=192
       elif [ $currentmem -le 2048 ]
       then
           echo "256" > memory.autolimit
           memorylimit=256
       else
           echo "512" > memory.autolimit
           memorylimit=512
       fi 
   mv "$WRAPPER_CONF" "${WRAPPER_CONF}.old"
   sed "s/wrapper.java.maxmemory=.*/wrapper.java.maxmemory=$memorylimit/g" "${WRAPPER_CONF}.old" > "$WRAPPER_CONF"
   fi
}

case "$1" in

    'console')
        checkUser $1 touchlock
        console
        ;;

    'start')
        checkUser $1 touchlock
        setMemoryLimitIfNeeded
        start
        ;;

    'stop')
        checkUser $1
        stopit
        ;;

    'restart')
        checkUser $1 touchlock
        stopit
        start
        ;;

    'status')
        checkUser $1
        status
        ;;

    'dump')
        checkUser $1
        dump
        ;;

    *)
        echo "Usage: $0 { console | start | stop | restart | status | dump }"
        exit 1
        ;;
esac

exit 0
