#!/bin/bash
### BEGIN REDHAT INFO
# chkconfig: 2345 99 20
# description: The Neo4J graph database server. See http://neo4j.org
### END REDHAT INFO
### BEGIN INIT INFO
# Provides:          neo4j-service
# Required-Start:
# Required-Stop:
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
### END INIT INFO
# Copyright (c) 2002-2012 "Neo Technology,"

# Network Engine for Objects in Lund AB [http://neotechnology.com]
#
# This file is part of Neo4j.
#
# Neo4j is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

FRIENDLY_NAME="Neo4j Server"
LAUNCHD_NAME="org.neo4j.server"

function findBaseDirAndCdThere {
# This seems to not be safe to run at any time. If that
# is the case, it should be fixed to be so, if possible.
  SCRIPT=$0

  cd "`dirname "$SCRIPT"`"
  SCRIPT=`basename "$SCRIPT"`

  while [ -L "$SCRIPT" ]
  do
    SCRIPT=$( readlink "$SCRIPT" )
    cd "$(dirname "$SCRIPT")"
    SCRIPT=`basename "$SCRIPT"`
  done
  NEO4J_HOME=`cd $( dirname "$SCRIPT" )/.. && dirs -l +0`
  NEO4J_INSTANCE=$NEO4J_HOME

  cd "$NEO4J_HOME"
}

function parseConfig {
  if [ ${BASH_VERSINFO[0]} -eq 3 ] ; then
    if [ ${BASH_VERSINFO[1]} -lt 2 ] ; then
      getconfigquoted "${NEO4J_INSTANCE}/conf/neo4j-wrapper.conf"
      getconfigquoted "${NEO4J_INSTANCE}/conf/neo4j-server.properties"
      return
    fi
  fi
  getconfig "${NEO4J_INSTANCE}/conf/neo4j-wrapper.conf"
  getconfig "${NEO4J_INSTANCE}/conf/neo4j-server.properties"
}

findBaseDirAndCdThere
source bin/utils
parseConfig

NEO4J_OPTS="-Dlog4j.configuration=file:conf/log4j.properties -Dorg.neo4j.server.properties="${NEO4J_INSTANCE}"/conf/neo4j-server.properties -Djava.util.logging.config.file="${NEO4J_INSTANCE}/conf/logging.properties" -Dneo4j.home="${NEO4J_HOME}" -Dneo4j.instance="${NEO4J_INSTANCE}""

JAVA_OPTS="-server -XX:+DisableExplicitGC ${wrapper_java_additional}"
[ -z "${wrapper_java_initmemory}" ] || JAVA_OPTS="$JAVA_OPTS -Xms${wrapper_java_initmemory}m"
[ -z "${wrapper_java_maxmemory}" ] || JAVA_OPTS="$JAVA_OPTS -Xmx${wrapper_java_maxmemory}m"

#NEO4J_SERVER_PORT=`( egrep "^org.neo4j.server.webserver.port" $NEO4J_INSTANCE/conf/neo4j-server.properties || echo 7474 ) | sed -e 's/.*=//'`
NEO4J_SERVER_PORT=${org_neo4j_server_webserver_port:=7474}

LAUNCHD_NAME="${LAUNCHD_NAME}.${NEO4J_SERVER_PORT}"

HEADLESS=false

if [ -z "${wrapper_user}" ]; then
  NEO4J_USER=`id -un`
else
  NEO4J_USER=${wrapper_user}
fi

# Username to propose for neo4j user, can be overridden by -u USERNAME option
DEFAULT_USER='neo4j'

SCRIPT_NAME="${NEO4J_HOME}/bin/neo4j"
SERVICE_NAME=${wrapper_ntservice_name:=neo4j-service}
LAUNCHD_DIR=~/Library/LaunchAgents/

TIMEOUT=120

PID_FILE=${NEO4J_INSTANCE}/data/neo4j-service.pid
buildclasspath() {
  # confirm library jars
  LIBDIR="$NEO4J_HOME"/lib
  if [ ! -e "$LIBDIR" ] ; then
    echo "Error: missing Neo4j Library, expected at $LIBDIR"
    exit 1
  fi

  # confirm system jars
  SYSLIBDIR="$NEO4J_HOME"/system/lib
  if [ ! -e "$SYSLIBDIR" ] ; then
    echo "Error: missing Neo4j System Library, expected at $SYSLIBDIR"
    exit 1
  fi

  ALL_JARS=""
  for jar in "$LIBDIR"/*.jar "$SYSLIBDIR"/*.jar ; do
    [ -z "$ALL_JARS" ] && ALL_JARS="$jar" || ALL_JARS="$ALL_JARS":"$jar"
  done

  # add any plugin jars
  for jar in "$NEO4J_HOME"/plugins/*.jar ; do
    if [ -e "$jar" ] ; then
      ALL_JARS="$ALL_JARS":"$jar"
    fi
  done
  
  # add any plugin jars in nested folders
  for jar in "$NEO4J_HOME"/plugins/**/*.jar ; do
    if [ -e "$jar" ] ; then
      ALL_JARS="$ALL_JARS":"$jar"
    fi
  done

  if [ -e "$NEO4J_HOME"/system/coordinator/lib ]; then
    for jar in "$NEO4J_HOME"/system/coordinator/lib/*.jar ; do
      if [ -e "$jar" ] ; then
        ALL_JARS="$ALL_JARS":"$jar"
      fi
    done
  fi

  CLASSPATH=${ALL_JARS}
}

checkupgrade() {
    if [ $UID == 0 ] ; then
      su $NEO4J_USER -c "$JAVACMD -cp '$CLASSPATH' $JAVA_OPTS -Dlog4j.configuration=file:conf/log4j.properties \
        -Dorg.neo4j.server.properties=\"${NEO4J_INSTANCE}/conf/neo4j-server.properties\" \
        -Djava.util.logging.config.file=\"${NEO4J_INSTANCE}/conf/logging.properties\" \
        -Dneo4j.home=\"${NEO4J_HOME}\" -Dneo4j.instance=\"${NEO4J_INSTANCE}\" \
        org.neo4j.server.storemigration.PreStartupStoreUpgrader"
    else
      checkwriteaccess
      $JAVACMD -cp "${CLASSPATH}" $JAVA_OPTS -Dlog4j.configuration=file:conf/log4j.properties \
        -Dorg.neo4j.server.properties="${NEO4J_INSTANCE}/conf/neo4j-server.properties" \
        -Djava.util.logging.config.file="${NEO4J_INSTANCE}/conf/logging.properties" \
        -Dneo4j.home="${NEO4J_HOME}" -Dneo4j.instance="${NEO4J_INSTANCE}" \
        org.neo4j.server.storemigration.PreStartupStoreUpgrader
    fi

    if [ $? -ne 0 ] ; then
      exit 1
    fi
}

detectrunning() {
  ## This could be achieved with filtering using -sTCP:LISTEN but this option is not available
  ## on lsof v4.78 which is the one bundled with some distros. So we have to do this grep below
  newpid=$(lsof -i :$NEO4J_SERVER_PORT -F T -Ts | grep -i "TST=LISTEN" -B1 | head -n1)
  newpid=${newpid:1}
}

startit() {

  detectos
  exitonnojava
  checkstatus
  checklimits

  detectrunning
  if [ $newpid ] ; then
	     echo "Another server-process is running with [$newpid], cannot start a new one. Exiting."
	     exit 2;
  fi

  if [ $DIST_OS = "macosx" ] ; then
    getlaunchdpid
    if [ $LAUNCHDPID -eq 0 ] ; then
      echo "Detected installation in launchd, starting it..."
      launchctl start $LAUNCHD_NAME
      exit 0
    elif [ $LAUNCHDPID -gt 0 ] ; then
      echo "Instance already running via launchd with PID $LAUNCHDPID"
      exit 1
    fi
    # We fall through here since if there is no launchd install we start manually
  fi

  if [ -z $NEO4J_PID ] ; then
    printf "Starting $FRIENDLY_NAME..."

    buildclasspath
    checkupgrade
    checkandrepairenv

    if [ $UID == 0 ] ; then
      su $NEO4J_USER -c "$JAVACMD -cp '$CLASSPATH' $JAVA_OPTS -Dlog4j.configuration=file:conf/log4j.properties \
        -Dorg.neo4j.server.properties=\"${NEO4J_INSTANCE}/conf/neo4j-server.properties\" \
        -Djava.util.logging.config.file=\"${NEO4J_INSTANCE}/conf/logging.properties\" \
        -Dneo4j.home=\"${NEO4J_HOME}\" -Dneo4j.instance=\"${NEO4J_INSTANCE}\" \
        -Dfile.encoding=UTF-8 \
        org.neo4j.server.Bootstrapper >> \"$NEO4J_INSTANCE/data/log/console.log\" 2>&1 & echo \$! > \"$PID_FILE\" "
    else
      checkwriteaccess
      echo "WARNING: not changing user"
      $JAVACMD -cp "${CLASSPATH}" $JAVA_OPTS -Dlog4j.configuration=file:conf/log4j.properties \
        -Dorg.neo4j.server.properties="${NEO4J_INSTANCE}/conf/neo4j-server.properties" \
        -Djava.util.logging.config.file="${NEO4J_INSTANCE}/conf/logging.properties" \
        -Dneo4j.home="${NEO4J_HOME}" -Dneo4j.instance="${NEO4J_INSTANCE}" \
        org.neo4j.server.Bootstrapper >> "${NEO4J_INSTANCE}/data/log/console.log" 2>&1 & echo $! > "${PID_FILE}"
    fi
    STARTED_PID=$( cat "$PID_FILE" )

    echo -n "process [$STARTED_PID]"

	if [ $WAIT = "true" ] ; then
	echo -n "... waiting for server to be ready."
	    x=0
	    while ( [ $x -lt $TIMEOUT ] ) ; do
	      ## wait for start, pick up the server listening on the port
	      detectrunning
	      if [ $newpid ] ; then
	         break
	      fi

	      kill -0 $STARTED_PID 2> /dev/null || break
	      printf "."
	      sleep 1
	      x=$[$x +1]
	    done

	    if kill -0 $STARTED_PID 2>/dev/null ; then
	      if [ "$newpid" != "$STARTED_PID" ] ; then
		    rm "$PID_FILE"
	        kill -9 $STARTED_PID
	        echo " Failed to start within $TIMEOUT seconds."
	        echo "$FRIENDLY_NAME failed to start, please check the logs for details."
	        echo "If startup is blocked on a long recovery, use '$0 start-no-wait' to give the startup more time."
	        exit 2
	      fi

	      echo " OK."
        echo "Go to http://localhost:$NEO4J_SERVER_PORT/webadmin/ for administration interface."
	      exit 0
	    fi

	    echo " Failed to start within $TIMEOUT seconds."
	    echo "$FRIENDLY_NAME may have failed to start, please check the logs."
	    rm "$PID_FILE"
	    exit 1
    else
        echo "...Started the server in the background, returning..."
    fi
  else
    echo "$FRIENDLY_NAME already running with pid $NEO4J_PID"
    exit 0
  fi
}

console() {

  checkstatus
  checklimits

  if [ -z $NEO4J_PID ] ; then
    echo "Starting $FRIENDLY_NAME console-mode..."

    exitonnojava
    buildclasspath
    checkwriteaccess
    checkandrepairenv

    $JAVACMD -cp "${CLASSPATH}" $JAVA_OPTS -Dlog4j.configuration=file:conf/log4j.properties \
        -Dorg.neo4j.server.properties="${NEO4J_INSTANCE}/conf/neo4j-server.properties" \
        -Djava.util.logging.config.file="${NEO4J_INSTANCE}/conf/logging.properties" \
        -Dneo4j.home="${NEO4J_HOME}" -Dneo4j.instance="${NEO4J_INSTANCE}" \
        org.neo4j.server.Bootstrapper

  else
    echo "$FRIENDLY_NAME already running with pid $NEO4J_PID"
    exit 1
  fi
}

# Modifies neo4j config to set the effective user when running as a service
# usage: modify_user_config <username> <created=false>
# pass in "true" for created to mark that the user was created (instead than pre-existing)
modify_user_config() {
  created=${2:-"false"}
  if `grep -q "wrapper\.user=" "$NEO4J_INSTANCE/conf/neo4j-wrapper.conf"` ; then
    sed -i -e "s/^.*wrapper\.user=.*$/wrapper\.user=$1/" "$NEO4J_INSTANCE/conf/neo4j-wrapper.conf"
  else
    echo "wrapper.user=$1" >> "$NEO4J_INSTANCE/conf/neo4j-wrapper.conf"
  fi

  if `grep -q "wrapper\.user\.created=" "$NEO4J_INSTANCE/conf/neo4j-wrapper.conf"` ; then
    sed -i -e "s/^.*wrapper\.user\.created=.*$/wrapper\.user\.created=${created}/" "$NEO4J_INSTANCE/conf/neo4j-wrapper.conf"
  else
    echo "wrapper.user.created=${created}" >> "$NEO4J_INSTANCE/conf/neo4j-wrapper.conf"
  fi
}

installservice() {
    detectos
    findjava
    selectinittool
    if [ ! -x "$JAVACMD" ] ; then
      if [[ $HEADLESS == false ]]; then
        read -p "It was not possible to determine JAVA_HOME. Do you want to continue with the installation? [yN]" yn
      else
        yn='y'
      fi
      case $yn in
              [yY]* ) echo "WARNING: Alright, but $FRIENDLY_NAME will fail to launch until Java has been properly installed and configured."
              ;;
      * ) echo "Aborting installation."
          exit 1
              ;;
      esac
    fi

    if [[ -d "/etc/init.d" ]]; then
      if [[ -e "/etc/init.d/${SERVICE_NAME}" ]]; then
        echo "${SERVICE_NAME} already installed."
      else
        if [ $UID != 0 ] ; then
          eval echo  'Must be root to perform this action.'
          exit 1
        else
          checkstatus
          if [ ! -z $NEO4J_PID ] ; then
            stopit
          fi
          if [[ $NEO4J_USER == $wrapper_user ]]; then 
            default_user=$wrapper_user
          else
            default_user=$DEFAULT_USER
          fi

          if [[ $HEADLESS == false ]]; then
            read -p "Graph-like power should be handled carefully. What user should run Neo4j? [$default_user] " proposed_user
          fi
          proposed_user=${proposed_user:-$default_user}

          if ! `id $proposed_user &> /dev/null` ; then
            if [[ $HEADLESS == false ]]; then
              read -p "User \"$proposed_user\" does not yet exist. Shall I create the account for you? [Yn]" yn
            else
              yn="y"
            fi
            case $yn in
              [Nn]* ) echo "WARNING: Alright, but Neo4j will fail to launch until that user has been created."
              set_user $proposed_user
              ;;
            * ) create_user $proposed_user
              ;;
            esac
          else
            modify_user_config $proposed_user
          fi
          ln -s "${SCRIPT_NAME}" "/etc/init.d/${SERVICE_NAME}"
          if [ $INIT_TOOL == 'chkconfig' ]; then
            chkconfig --add ${SERVICE_NAME}
          else
            update-rc.d ${SERVICE_NAME} defaults
          fi
          chown -R $proposed_user: "$NEO4J_HOME/data" "$NEO4J_HOME/conf"
        fi
      fi
    elif [[ $DIST_OS -eq "macosx" ]] ; then
      sed -e "s^NEO4J_INSTANCE^${NEO4J_INSTANCE}^" -e "s^LAUNCHD_NAME^${LAUNCHD_NAME}^" <"${NEO4J_INSTANCE}/bin/org.neo4j.server.plist" >"${LAUNCHD_DIR}/${LAUNCHD_NAME}.plist"
      launchctl load -w ${LAUNCHD_DIR}/${LAUNCHD_NAME}.plist
      launchctl start ${LAUNCHD_NAME}
    else
      echo "Sorry, don't know how to install on ${DIST_OS}."
    fi

    if [ -x "$JAVACMD" ] ; then
      if [[ $DIST_OS -ne "macosx" ]] ; then
        startit
      fi
    fi
}

showinfo() {
  reportstatus

  exitonnojava
  buildclasspath

  echo "NEO4J_HOME:        $NEO4J_HOME"
  echo "NEO4J_SERVER_PORT: $NEO4J_SERVER_PORT"
  echo "NEO4J_INSTANCE:    $NEO4J_INSTANCE"
  echo "JAVA_HOME:         $JAVA_HOME"
  echo "JAVA_OPTS:         $JAVA_OPTS"
  echo "CLASSPATH:         $CLASSPATH"
}

# END FUNCTIONS
# BEGIN MAIN

# Parse option flags
while getopts "u:h" opt; do
  case $opt in
    h)
      echo "Running in headless (-h) mode" >&2
      HEADLESS=true
      ;;
    u)
      echo "Installing with user $OPTARG"
      DEFAULT_USER=$OPTARG
      ;;
    \?)
      echo "Invalid option: -$OPTARG" >&2
      exit 1
      ;;
    :)
      echo "Option -$OPTARG requires an argument." >&2
      exit 1
      ;;
  esac
done

case "${!OPTIND}" in
  console)
    console
    exit 0
    ;;

  start)
    WAIT=true
    startit
    ;;

  start-no-wait)
    WAIT=false
    startit
    exit 0
    ;;

  stop)
    stopit
    exit 0
    ;;

  restart)
    WAIT=true
    stopit
    startit
    exit 0
    ;;

  status)
    reportstatus
    exit 0
    ;;

  info)
    showinfo
    exit 0
    ;;
  install)
    WAIT=true
    installservice
    exit 0
    ;;
  remove)
    removeservice
    exit 0
    ;;
  *)
    echo "Usage: neo4j { start | stop | restart | status | info | install | remove }"
    exit 0;;

esac

exit $?
