#!/bin/sh

 #=============================================================================#
 # Copyright (c) 2009, 2023 Stephan Wahlbrink and others.
 # 
 # This program and the accompanying materials are made available under the
 # terms of the Eclipse Public License 2.0 which is available at
 # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
 # which is available at https://www.apache.org/licenses/LICENSE-2.0.
 # 
 # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
 # 
 # Contributors:
 #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
 #=============================================================================#


##
# Shell script to start R server (for StatET)
##
# Usually you have to change only the CONFIG sections. You should set at least:
#     R_HOME
#     JAVA_HOME
# Depending on the system configuration, it can be required to set:
#     S_HOSTADDRESS
# 
# The authentication is set to 'fx' (method using SSH) by default.
##
# Usage of this script:
#     startup.sh <address> [options]
#     startup.sh <name> [options]
# It starts the R server asynchronously in background by default.
#     <address>                        the complete RMI-address
#     <name>                           the session name only (the hostname
#                                      should be set in this file)
# Options:
#     -wd=<working directory>          initial R working directory
#     -debug                           enables debug output and
#                                      runs R in foreground
#
# Note: This script does not start an RMI registry! You have to launch it
# as system daemon or manually (see Java documentation), e.g. by:
#     $JAVA_HOME/bin/rmiregistry -J-Djava.rmi.server.codebase=file:///<path to file org.eclipse.statet.rj.server.jar> &
# or if appropriate by uncommenting the section "Start registry" in this script.
##
# Author: Stephan Wahlbrink <sw@wahlbrink.eu>
###############################################################################

###############################################################################
# SCRIPT - INIT / READING PARAMETERS ##########################################
###############################################################################
OS=`uname | tr '[:upper:]' '[:lower:]'`
case "$OS" in
darwin)
	C_READLINK=greadlink
	LD_LIB_VAR="DYLD_LIBRARY_PATH"
	LD_PRELOAD_VAR="DYLD_INSERT_LIBRARIES"
	;;
*)
	C_READLINK=readlink
	LD_LIB_VAR="LD_LIBRARY_PATH"
	LD_PRELOAD_VAR="LD_PRELOAD"
	;;
esac

if [ -z "$1" ]; then
	echo "Missing address or name for R server"
	exit -1
fi
ADDRESS=$1
S_NAME=`basename ${ADDRESS}`
S_HOSTADDRESS=""
S_REGISTRY_PORT=""
if [ "${ADDRESS}" != "${S_NAME}" ]; then
	S_HOSTADDRESS=`expr "_${ADDRESS}" : "_[^/]*\/\/\([^:/]*\).*"`
	S_REGISTRY_PORT=`expr "_${ADDRESS}" : "_[^/]*\/\/[^:/]*:\([0-9]\+\)\/.*"`
fi
shift
renvId="default"
wd=${HOME}
scriptPath=`$C_READLINK -f "$0"`
scriptDirPath=`dirname ${scriptPath}`

until [ -z "$1" ]; do
	case "$1" in
	-wd=*)
		wd=${1##-wd=}
		;;
	-renv=*)
		renvId=${1##-renv=}
		;;
#	-host=*)
#		S_HOSTADDRESS=${1##-host=}
#		;;
	-debug*)
		DEBUG=1
		echo Debug mode enabled
		;;
	-dev*)
		DEV=1
		echo Development mode enabled
		;;
	*)
		echo Unknown parameter: $1
		;;
	esac
	shift
done
###############################################################################

###############################################################################
# CONFIG - SYSTEM SETTINGS ####################################################
###############################################################################
# Configure the startup in the following sections

###############################################################################
# Set the home and work directory of this R server.
# The home must contain the jar files of this server, if this file is in this
# directory, you don't have to change the value.
# The working directory is for files like log files
##
# Example:
#     RJS_HOME="${scriptDirPath}"
#     RJS_WORK="${HOME}/R/rjserver/work"

RJS_HOME="${scriptDirPath}"
RJS_WORK="${HOME}/R/rjserver/work"

###############################################################################
# Required system specific settings
# 
# Set the path to home of R and Java.
# If parts of the R installation (e.g. the documentation) are located in not
# sub-directories of R home, it is often required to set additional variables
# specifying the several installation locations (see example).
# 
# Example defining the configuration directly:
#     R_HOME="/usr/lib/R"
#     R_ARCH="/x64"
#     R_SHARE_DIR="/usr/share/R"
#     R_INCLUDE_DIR="/usr/include/R"
#     R_DOC_DIR="/usr/share/doc/R"
#     R_LIBS_SITE="/usr/local/lib/R/site-library"
#     JAVA_HOME="/usr/lib/jvm/java-11-openjdk"
# 
# Example reading the R configuration from renv.properties:
#     . ${RJS_HOME}/renv.sh-lib
#     loadREnvConfiguration ${scriptDirPath}/renvs/${renvId}
#     JAVA_HOME="/usr/lib/jvm/java-17"
# To create the 'renv.properties' initialized with the default values of an R
# installation, run:
#      R -e "rj::.renv.createConfigProperties(out= '<renv-folder>')"

R_HOME=
JAVA_HOME=

###############################################################################
# Set explicitly the hostname and port you want to use to access the RMI
# registry/R server.
# It is recommended to use the IP address instead of a name.
# By default, the hostname is extracted form the specified RMI address
# (script parameter). Nevertheless there are some reasons to set it explicitly
# here, e.g.:
#  - For SSH tunnel connections '127.0.0.1' (localhost) is sufficient;
#    recommend, if no other network connections are used; and required if the
#    public IP address is blocked by the firewall.
#  - To make sure that the correct IP address is set even the hostname is
#    used in the RMI address.
##
# Usage:
#     S_HOSTADDRESS=<ip or hostname>
#     S_REGISTRY_PORT=<port of rmi-registry>
# Example:
#     S_HOSTADDRESS=192.168.1.80

#S_HOSTADDRESS=""


###############################################################################
# Add additional java options here
##
# Example:
#     JAVA_OPTS="-server -Djava.net.preferIPv4Stack=true"
#     JAVA_OPTS="-server -Dorg.eclipse.statet.rj.rmi.disableSocketFactory=true"

JAVA_OPTS="-server"
JAVA_OPTS_LIB=


###############################################################################
# CONFIG - AUTHENTICATION METHODS #############################################
###############################################################################
# You must specify the method (exactly one) to use for authentication when
# clients wants to connect to the R.
# There are several methods available. The methods provided by default
# are described in the following subsections. To change the method uncomment
# the lines of method you want to use and comment the previous AUTH definition.
##
# General usage:
#     RJ parm:     -auth=<method-id>[:<config>]
#                  -auth=<classname>[:<config>]
# 
# <config> option depends on the selected method

###############################################################################
# Authentication method: disabled / 'none'
# 
# Disables authentication. Anybody can connect to R. Use it only if you are in
# a secure environment! All users can connect to R get full user rights!
##
# General usage:
#     RJ param:    -auth=none
# 
# Script Usage:
#     AUTH="none"

#AUTH="none"

###############################################################################
# Authentication method: password file / 'name-pass'
# 
# Authentication using loginname-password combination.
##
# General usage:
#     RJ Param:    -auth=name-pass:file=<passwordfile>
# 
# Script usage:
#     AUTH="name-pass:file"
#     AUTH_PW_FILE="$HOME/R/rjserver/logins"
# 
# <passwordfile> is the path to a property file with
#     pairs of loginname and password, for example:
#         myname=mypassword
#     Make sure that only authorized users can read this file!

#AUTH="name-pass:file"
#AUTH_PW_FILE="$HOME/R/rjserver/logins"

###############################################################################
# Authentication method: local user account / 'local-shaj'
# 
# Authentication using your local loginname-password combination (PAM).
# The 'Shaj' library is required for this method. It is provided in sources and
# binary form for several platforms. If no binary match your environment, you
# have to build it for your system. You find the files inside the folder
#     shaj
# of this remotetools package.
##
# General Usage:
#     RJ Param:    -auth=local-shaj
#     Java Prop:   java.library.path=<folder of library>
# 
# Script Usage:
#     AUTH="local-shaj"
#     AUTH_SHAJ_LD_DIR="<folder of library>"
# Example:
#     AUTH=local-shaj
#     AUTH_SHAJ_LD_DIR="$RJS_HOME/shaj/linux-x64"

#AUTH="local-shaj"
#AUTH_SHAJ_LD_DIR="$RJS_HOME/shaj/<platform>"

###############################################################################
# Authentication method: ssh / 'fx' (exchange over file)
# 
# Authentication for automatic startup (via ssh by StatET).
##
# General Usage:
#     RJ Param:    -auth=fx:file=<keyfile>
# 
# Script Usage:
#     AUTH="fx:file"
#     AUTH_FX_FILE="<keyfile>"
#     AUTH_FX_USER=$USER
#     AUTH_FX_MASK="600"
# 
# <keyfile> a (empty). The fs permission to edit the files represents the
#     right to login into R, so:
#     The file must be writable for authorized user only!
#     If the script setup is used, the files is created automatically and
#     the permission is set according to AUTH_FX_USER and AUTH_FX_MASK

AUTH="fx:file"
AUTH_FX_FILE="$RJS_WORK/session-$S_NAME.lock"
AUTH_FX_USER=$USER
AUTH_FX_MASK=600


###############################################################################
# SCRIPT - STARTUP SERVER #####################################################
###############################################################################
# Usually you don't have to edit the lines below
PATH_SEP=":"

mkdir -p "$RJS_WORK"

## Final RMI address
if [ -n "$S_HOSTADDRESS" ]; then
	S_ADDRESS="//$S_HOSTADDRESS"
	if [ -n "$S_REGISTRY_PORT" ]; then
		S_ADDRESS="$S_ADDRESS:$S_REGISTRY_PORT"
	fi
	S_ADDRESS="$S_ADDRESS/$S_NAME"
else
	S_ADDRESS="///$S_NAME"
fi

S_FINAL_REGISTRY_HOST="${S_HOSTADDRESS:-127.0.0.1}"
S_FINAL_REGISTRY_PORT="${S_REGISTRY_PORT:-1099}"

## Finish auth configuration
if [ "$FORCE_AUTH" ]; then
	AUTH="$FORCE_AUTH"
fi

if [ "$AUTH" = "name-pass:file" ]; then
	AUTH="name-pass:file=$AUTH_PW_FILE"
fi

if [ "$AUTH" = "local-shaj" ]; then
	AUTH="local-shaj"
	JAVA_OPTS_LIB="$JAVA_OPTS_LIB$PATH_SEP$AUTH_SHAJ_LD_DIR"
	JAVA_CP="$JAVA_CP$PATH_SEP$RJS_HOME/shaj/auth.jar"
fi

if [ "$AUTH" = "fx:file" ]; then
	AUTH="fx:file=$AUTH_FX_FILE"
	AUTH_FX_FOLDER=`dirname "$AUTH_FX_FILE"`
	mkdir -p "$AUTH_FX_FOLDER"
	echo "00000000" > "$AUTH_FX_FILE"
	if [ $AUTH_FX_USER ]; then
		chown $AUTH_FX_USER "$AUTH_FX_FILE"
	fi
	if [ $AUTH_FX_MASK ]; then
		chmod $AUTH_FX_MASK "$AUTH_FX_FILE"
	fi
fi

OPTS="-auth=$AUTH"
if [ $DEBUG ]; then
	OPTS="$OPTS -verbose"
fi

## RJ package implementation to load
RLIB_PATHS=( "$R_LIBS" "$R_LIBS_USER" "$R_LIBS_SITE" "$R_HOME/library" )
for LIB_PATH in "${RLIB_PATHS[@]}"; do
	BAK_IFS="$IFS"; IFS=":"; ITEMS=( $LIB_PATH ); IFS="$BAK_IFS"
	for ITEM in "${ITEMS[@]}"; do
		if [ -n "$ITEM" ] && [ -f "$ITEM/rj/DESCRIPTION" ]; then
			echo "R package 'rj' found at '$ITEM/rj'."
			RJS_BOOT_CP="$ITEM/rj/server/rj-boot.jar"
			break 2;
		fi
	done
done
if [ -z "$RJS_BOOT_CP" ]; then
	echo "R package 'rj' not found."
	exit -2
fi
RJS_BOOT_CLASS="RJSrv"

## Java config
JAVA_EXE="$JAVA_HOME/bin/java"
if [ $DEV ]; then
	JAVA_CP="$RJS_BOOT_CP$PATH_SEP$RJS_HOME/../org.eclipse.statet.rj.server/bin$PATH_SEP$RJS_HOME/../org.eclipse.statet.rj.data/bin$PATH_SEP$RJS_HOME/../org.eclipse.statet.jcommons.util/bin$PATH_SEP$RJS_HOME/bin$PATH_SEP$RJS_HOME/binShaj"
	RMI_BASE="file://$RJS_HOME/../org.eclipse.statet.rj.server/bin"
	RJAVA_CP=
else
	JAVA_CP="$RJS_BOOT_CP$PATH_SEP$RJS_HOME/org.eclipse.statet.rj.server.jar$PATH_SEP$RJS_HOME/org.eclipse.statet.rj.data.jar$PATH_SEP$RJS_HOME/org.eclipse.statet.jcommons.util.jar$PATH_SEP$JAVA_CP"
	RMI_BASE="file://$RJS_HOME/org.eclipse.statet.rj.server.jar"
	RJAVA_CP=
fi

JAVA_OPTS="$JAVA_OPTS -Djava.security.policy=$RJS_HOME/security.policy"
JAVA_OPTS="$JAVA_OPTS -Djava.rmi.server.codebase=$RMI_BASE"
if [ -n "$S_HOSTADDRESS" ]; then
	JAVA_OPTS="$JAVA_OPTS -Djava.rmi.server.hostname=$S_HOSTADDRESS"
fi
if [ -n "$JAVA_OPTS_LIB" ]; then
	JAVA_OPTS="$JAVA_OPTS -Djava.library.path=$JAVA_OPTS_LIB"
fi
if [ -n "$RJAVA_CP" ]; then
	JAVA_OPTS="$JAVA_OPTS -Drjava.class.path=$RJAVA_CP"
fi

## Other environment
PATH=$R_HOME/bin$PATH_SEP$PATH
LD_LIB_PATH=$R_HOME/lib
#LD_PRELOAD_VALUE=$JAVA_HOME/jre/lib/amd64/server/libjsig.so


## Start registry
# For codebase see also:
# http://docs.oracle.com/javase/7/docs/technotes/guides/rmi/enhancements-7.html

# Netcat (nc) is used to test if the registry port is in use
#nc -z $S_FINAL_REGISTRY_HOST $S_FINAL_REGISTRY_PORT
#if [ $? -ne 0 ]; then
#	echo "No registry found at port $S_FINAL_REGISTRY_PORT, starting registry..."
#	nohup $JAVA_HOME/bin/rmiregistry $S_REGISTRY_PORT "-J-Djava.rmi.server.codebase=$RMI_BASE" >> "$RJS_WORK/registry-$S_FINAL_REGISTRY_PORT.out" 2>&1 < /dev/null &
#	sleep 1
#fi


## Prepare start

export PATH
export $LD_LIB_VAR=$LD_LIB_PATH
export $LD_PRELOAD_VAR=$LD_PRELOAD_VALUE
export JAVA_HOME
export R_HOME
#export R_ARCH
export R_LIBS
export R_LIBS_USER
export R_LIBS_SITE
export R_DOC_DIR
export R_SHARE_DIR
export R_INCLUDE_DIR
export LC_ALL

cd "${wd}"

START_ARGS="-cp $JAVA_CP $JAVA_OPTS $RJS_BOOT_CLASS start $S_ADDRESS $OPTS"
#echo $JAVA_EXE $START_ARGS

if [ $DEBUG ]; then
	echo "S_HOSTADDRESS= $S_HOSTADDRESS"
	echo "S_REGISTRY_PORT= $S_REGISTRY_PORT"
	echo "S_FINAL_REGISTRY_PORT= $S_FINAL_REGISTRY_PORT"
	echo "PATH= $PATH"
	# ${!LD_LIB_VAR} does not work e.g. in dash
	echo "$LD_LIB_VAR= $LD_LIB_PATH"
	echo "$LD_PRELOAD_VAR= $LD_PRELOAD_VALUE"
	echo "R_HOME= $R_HOME"
	echo "R_ARCH= $R_ARCH"
	echo "JAVA_HOME= $JAVA_HOME"
	echo "JAVA_EXE= $JAVA_EXE"
	echo "CLASSPATH= $JAVA_CP"
	echo "JAVA_OPTIONS= $JAVA_OPTS"
	echo "AUTH= $AUTH"
	
	# Start server directly
	echo "Starting server ..."
	$JAVA_EXE $START_ARGS
	START_EXIT=$?
	START_PID=$!
	exit $START_EXIT
else
	# First check if running or dead server is already bound
	CLEAN_ARGS="-cp $JAVA_CP $JAVA_OPTS $RJS_BOOT_CLASS clean $S_ADDRESS"
	$JAVA_EXE $CLEAN_ARGS
	CLEAN_EXIT=$?
	if [ $CLEAN_EXIT -ne 0 ]; then
		echo "Check and cleanup of old server failed (CODE=$CLEAN_EXIT), cancelling startup."
		exit $CLEAN_EXIT
	fi
	
	# Start server detached
	echo "Starting server ..."
	echo "`date --rfc-3339=seconds` RJServer Startup Script" > "$RJS_WORK/session-$S_NAME.out"
	echo "INFO: [Startup:$S_NAME] Cmd: $JAVA_EXE $START_ARGS" >> "$RJS_WORK/session-$S_NAME.out"
	nohup $JAVA_EXE $START_ARGS >> "$RJS_WORK/session-$S_NAME.out" 2>&1 < /dev/null &
	START_EXIT=$?
	START_PID=$!
	if [ $START_EXIT -eq 0 ]; then
		echo "Started server in background (PID=$START_PID)."
		exit 0
	else
		echo "Startup failed"
		exit $START_EXIT
	fi
fi
