 #=============================================================================#
 # 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 (PowerShell) 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 'none' 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.exe -J-Djava.rmi.server.codebase=file:///<path to file org.eclipse.statet.rj.server.jar>
# or in a detached process (not official):
#     %JAVA_HOME%/bin/javaw.exe -Djava.rmi.server.codebase=file:///<path to file org.eclipse.statet.rj.server.jar> sun.rmi.registry.RegistryImpl
##
# Author: Stephan Wahlbrink <sw@wahlbrink.eu>
###############################################################################

###############################################################################
# SCRIPT - INIT / READING PARAMETERS ##########################################
###############################################################################
param([String] $ADDRESS)

if ([String]::IsNullOrEmpty("$ADDRESS"))
{
	echo "Missing address or name for R server"
	exit -1
}

$S_NAME=$ADDRESS -replace '.*\/([^/]+)$', '$1'
$S_HOSTADDRESS=""
$S_REGISTRY_PORT=""
if ( $ADDRESS -ne $S_NAME ) {
	$S_HOSTADDRESS=$ADDRESS -replace '[^/]*\/\/([^:/]*).*', '$1'
	$S_REGISTRY_PORT=$ADDRESS -replace '[^/]*\/\/[^:/]*:?([0-9]*)\/.*', '$1'
}

$WD=$HOME
$SCRIPT=$MyInvocation.MyCommand.Path

foreach ($1 in $Args) {
	switch -wildcard ("$1") {
	"-wd=*" {
		$WD=$1.Substring(4)
		}
#	"-host=*" {
#		$S_HOSTADDRESS=$1.Substring(6)
#		}
	"-debug*" {
		$DEBUG=1
		echo "Debug mode enabled"
		}
	"-dev*" {
		$DEV=1
		echo "Development mode enabled"
		}
	default {
		echo "Unknown parameter: $1"
		}
	}
}
###############################################################################

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

###############################################################################
# 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:
#     R_HOME="/usr/lib/R"
#     R_ARCH="/x64"
#     R_DOC_DIR="/usr/share/doc/R"
#     R_SHARE_DIR="/usr/share/R"
#     R_INCLUDE_DIR="/usr/include/R"
#     R_LIBS_SITE="/usr/local/lib/R/site-library"
#     JAVA_HOME="C:\Program Files\Java\jdk-11

$R_HOME=""
$R_ARCH="/x64"
$JAVA_HOME=""

###############################################################################
# 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=`dirname "$SCRIPT"`
#     RJS_WORK="$HOME/R/rjserver/work"

$RJS_HOME=Split-Path -Path $SCRIPT -Parent
$RJS_WORK="$HOME/R/rjserver/work"

###############################################################################
# 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=[System.Security.Principal.WindowsIdentity]::GetCurrent().Name
$AUTH_FX_MASK=new-object System.Security.AccessControl.FileSystemAccessRule $AUTH_FX_USER, 'FullControl', 'Allow'


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

if ( !( Test-Path "$RJS_WORK" ) ) {
	# New-Item -ItemType directory -Force -Path "$RJS_WORK"
	mkdir -p "$RJS_WORK"
}

## Final RMI address
if ( $S_HOSTADDRESS ) {
	$S_ADDRESS="//$S_HOSTADDRESS"
	if ( $S_REGISTRY_PORT ) {
		$S_ADDRESS="$S_ADDRESS:$S_REGISTRY_PORT"
	}
	$S_ADDRESS="$S_ADDRESS/$S_NAME"
} else {
	$S_ADDRESS="///$S_NAME"
}

## Finish auth configuration
if ( $FORCE_AUTH ) {
	$AUTH="$FORCE_AUTH"
}

if ( $AUTH -eq "name-pass:file" ) {
	$AUTH="name-pass:file=$AUTH_PW_FILE"
}

if ( $AUTH -eq "local-shaj" ) {
	$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"
}

if ( "$AUTH" -eq "fx:file" ) {
	$AUTH="fx:file=$AUTH_FX_FILE"
	$AUTH_FX_FOLDER=Split-Path -Path $AUTH_FX_FILE -Parent
	mkdir -p "$AUTH_FX_FOLDER"
	echo "00000000" > "$AUTH_FX_FILE"
	
	$ACL=Get-Acl "$AUTH_FX_FILE"
	$ACL.SetAccessRule($AUTH_FX_MASK)
	#$SystemAccessRule = new-object System.Security.AccessControl.FileSystemAccessRule 'SYSTEM', 'FullControl', 'Allow'
	#$ACL.SetAccessRule($SystemAccessRule)
	$ACL.SetAccessRuleProtection($True, $False)
	Set-Acl "$AUTH_FX_FILE" $ACL
}

$OPTS=@("-auth=$AUTH")
if ( $DEBUG ) {
	$OPTS+="-verbose"
}

## RJ package implementation to load
$RLIB_PATHS=@("$R_LIBS", "$R_LIBS_USER", "$R_LIBS_SITE", "$R_HOME/library")
:ITER_PATHS foreach ( $LIB_PATH in $RLIB_PATHS ) {
	$ITEMS= $LIB_PATH.split(";")
	foreach ( $ITEM in $ITEMS ) {
		if (( $ITEM ) -and ( Test-Path "$ITEM/rj/DESCRIPTION" )) {
			echo "R package 'rj' found at '$ITEM/rj'."
			$RJS_BOOT_CP="$ITEM/rj/server/rj-boot.jar"
			break ITER_PATHS;
		}
	}
}
if ( ! $RJS_BOOT_CP ) {
	echo "R package 'rj' not found."
	exit -2
}
$RJS_BOOT_CLASS="RJSrv"

## Java config
$JAVA_EXE="$JAVA_HOME/bin/java.exe"
if ( $DEV )
{
	$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=""
}

$JAVA_OPTS+="-Djava.security.policy=$RJS_HOME/security.policy"
$JAVA_OPTS+="-Djava.rmi.server.codebase=$RMI_BASE"
if ( $S_HOSTADDRESS ) {
	$JAVA_OPTS+="-Djava.rmi.server.hostname=$S_HOSTADDRESS"
}
if ( $JAVA_OPTS_LIB ) {
	$JAVA_OPTS+="-Djava.library.path=$JAVA_OPTS_LIB"
}
if ( $RJAVA_CP ) {
	$JAVA_OPTS+="-Drjava.class.path=$RJAVA_CP"
}

## Other environment
$PATH="$R_HOME/bin$R_ARCH$PATH_SEP$Env:PATH"
$LD_LIBRARY_PATH="$R_HOME/lib"

$env:PATH = $PATH
$env:JAVA_HOME = $JAVA_HOME
$env:R_HOME = $R_HOME
$env:R_ARCH = $R_ARCH
$env:R_LIBS = $R_LIBS
$env:R_LIBS_USER = $R_LIBS_USER
$env:R_LIBS_SITE = $R_LIBS_SITE
$env:R_DOC_DIR = $R_DOC_DIR
$env:R_SHARE_DIR = $R_SHARE_DIR
$env:R_INCLUDE_DIR = $R_INCLUDE_DIR
$env:LC_ALL = $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 ) {
	# Set-ExecutionPolicy RemoteSigned -Scope Process -Force
	echo "S_HOSTADDRESS= $S_HOSTADDRESS"
	echo "S_REGISTRY_PORT= $S_REGISTRY_PORT"
	echo "PATH= $PATH"
	echo "LD_LIBRARY_PATH= $LD_LIBRARY_PATH"
	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
	& $JAVA_EXE $START_ARGS
	$START_EXIT=$LastExitCode
	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=$LastExitcode
	if ( $CLEAN_EXIT -ne 0 ) {
		echo "Check and cleanup of old server failed (CODE=$CLEAN_EXIT), cancelling startup."
		exit $CLEAN_EXIT
	}
	
	# Start server detached
	$JAVA_EXE="$JAVA_HOME/bin/javaw.exe"
	$START_PROCESS=Start-Process $JAVA_EXE -NoNewWindow -PassThru `
			-RedirectStandardOutput "$RJS_WORK/session-$S_NAME.out" `
			-RedirectStandardError "$RJS_WORK/session-$S_NAME.err" `
			$START_ARGS
	#$START_EXIT=
	$START_PID=$START_PROCESS.Id
	if ( ! $START_PROCESS.HasExited ) {
		echo "Started server in background (PID=$START_PID)."
		exit 0
	} else {
		echo "Startup failed"
		exit -1
	}
}
