#!/bin/bash

source $(dirname $0)/shared-run

emitDiagnostic()
{
   if [[ -n "${RSESSION_DIAGNOSTICS_ENABLED}" ]]; then
      if ! echo "[$(date)] $1" >> $RSESSION_DIAGNOSTICS_FILE ; then
         echo "Error: $$ trying to append to $RSESSION_DIAGNOSTICS_FILE - turning off session diagnostics"
         RSESSION_DIAGNOSTICS_ENABLED=
      fi
   fi
}

getDiagnosticSessionOptions()
{
   if [[ -n "${RSESSION_DIAGNOSTICS_ENABLED}" ]]; then
      # load libSegFault and redirect its backtrace output to our diagnostics file
      echo "env SEGFAULT_SIGNALS='abrt segv' SEGFAULT_OUTPUT_NAME=$RSESSION_SEGFAULT_FILE LD_PRELOAD=$RSESSION_DIAGNOSTICS_LIBSEGFAULT"
   else
      echo ""
   fi
}

getDiagnosticSessionLdd()
{
   if [[ -n "${RSESSION_DIAGNOSTICS_ENABLED}" ]]; then
      echo "echo -e \"[$(date)] ---- ldd output ----\n[$(date)] $(ldd $(dirname ${BASH_SOURCE[0]})/rsession)\" $(getDiagnosticSessionRedirection) &&"
   else
      echo ""
   fi
}

getDiagnosticSessionStraceEnabled()
{
   if [[ -z "${RSESSION_DIAGNOSTICS_STRACE_ENABLED}" ]]; then
      echo "strace not enabled for this diagnostics report"
   elif command -v strace &> /dev/null; then
      echo "strace enabled for this diagnostics report"
      echo "strace report location: $RSESSION_STRACE_FILE"
      echo ""
   else
      echo "strace is not installed; cannot generate strace diagnostics"
   fi
}

getDiagnosticSessionStrace()
{
   if [[ -z "${RSESSION_DIAGNOSTICS_STRACE_ENABLED}" ]]; then
      echo ""
   elif command -v strace &> /dev/null; then
      echo "strace -fttTy -o $RSESSION_STRACE_FILE"
   else
      echo ""
   fi
}

getDiagnosticSessionRedirection()
{
   if [[ -n "${RSESSION_DIAGNOSTICS_ENABLED}" ]]; then
      echo "|& tee -a $RSESSION_DIAGNOSTICS_FILE"
   else
      echo ""
   fi
}

getDiagnosticsPrintenv()
{
   if [[ -n "${RSESSION_DIAGNOSTICS_ENABLED}" ]]; then
      echo "echo -e \"[$(date)] ---- launching rsession process ----\n[$(date)] ---- final env ----\n[$(date)] $(printenv)\" $(getDiagnosticSessionRedirection) &&"
   else
      echo ""
   fi
}

# Create the user PPM config dir if it does not exist
create_ppm_config_dir() {
   if [[ ! -d "${USER_PPM_CONFIG_DIR}" ]]; then
      mkdir -p "${USER_PPM_CONFIG_DIR}"
      if [[ $? -ne 0 ]]; then
         echo "Error: Could not create user PPM config directory: ${USER_PPM_CONFIG_DIR}"
         exit 1
      fi
   fi
}

findLibSegFault()
{
   # if the libsegfault location has not explicitly been set
   # try to find it in common distribution locations
   if [[ -z "${RSESSION_DIAGNOSTICS_LIBSEGFAULT}" ]]; then
      if [[ -e /lib/x86_64-linux-gnu/libSegFault.so ]]; then
         RSESSION_DIAGNOSTICS_LIBSEGFAULT=/lib/x86_64-linux-gnu/libSegFault.so
      elif [[ -e /lib/i386-linux-gnu/libSegFault.so ]]; then
         RSESSION_DIAGNOSTICS_LIBSEGFAULT=/lib/i386-linux-gnu/libSegFault.so
      elif [[ -e /lib64/libSegFault.so ]]; then
         RSESSION_DIAGNOSTICS_LIBSEGFAULT=/lib64/libSegFault.so
      elif [[ -e /lib/libSegFault.so ]]; then
         RSESSION_DIAGNOSTICS_LIBSEGFAULT=/lib/libSegFault.so
      fi
   fi
}



runHooks "start"

# eval variables like $USER in XDG_DATA_HOME and XDG_STATE_HOME
XDG_DATA_HOME=$(eval echo $XDG_DATA_HOME)
XDG_STATE_HOME=$(eval echo $XDG_STATE_HOME)

USER_DATA_HOME=${XDG_DATA_HOME:-${HOME}/.local/share}

# If system-level diagnostics are not enabled...
if [[ -z "${RSESSION_DIAGNOSTICS_ENABLED}" ]]; then
   # Allow individual users to enable session diagnostics to their local log directory
   USER_DIAG_CONFIG="${HOME}/.config/rstudio/rsession-diagnostics"
   USER_DIAG_LOG_DIR="${USER_DATA_HOME}/rstudio/log"
   if [[ -f "$USER_DIAG_CONFIG" ]] ; then
      USER_DIAG_MODE=$(cat "$USER_DIAG_CONFIG")
      if [[ "$USER_DIAG_MODE" =~ debug|strace ]] ; then
         export RSESSION_DIAGNOSTICS_ENABLED=true
         mkdir -p ${USER_DIAG_LOG_DIR}
         export RSESSION_DIAGNOSTICS_FILE="${USER_DIAG_LOG_DIR}/rsession-diagnostics.log"
         export RS_LOG_LEVEL=debug
         if [[ "$USER_DIAG_MODE" = "strace" ]] ; then
            export RSESSION_DIAGNOSTICS_STRACE_ENABLED=true
         fi
         echo "Enabling ${USER_DIAG_MODE} diagnostics with debug logging from config file ${USER_DIAG_CONFIG}"
         echo "Diag files: ${RSESSION_DIAGNOSTICS_FILE} + .strace, .segfault"
         echo "Debug logs: ${USER_DIAG_LOG_DIR}/rsession-${USER}.*.log"
      elif [[ "$USER_DIAG_MODE" != "off" ]] ; then
         echo "Ignoring invalid session-diagnostics value: ${USER_DIAG_MODE} for file: $USER_DIAG_CONFIG - use one of strace, debug or off"
      fi
   fi
fi

# escape backslashes in arguments to pass safely to bash below
RSESSION_ARGS="${@//\\/\\\\}"
USER_PPM_CONFIG_DIR="${USER_DATA_HOME}/rstudio/ppm"

# This environment variable contains a repository URL - use it to create a repos.conf file
if [[ -n "${PWB_PPM_REPO_URL}" ]]; then
   # Pass the PWB_PPM_REPO_URL to the R session as a default package manager repo URL
   RSESSION_ARGS="${RSESSION_ARGS} --r-default-package-manager-repo-url ${PWB_PPM_REPO_URL}"
   
   if [[ "$USER_DIAG_MODE" =~ debug|strace ]]; then
      echo "Using posit package manager url: ${PWB_PPM_REPO_URL}"
   fi
fi

# If metadata key is set, check if we have an API key path available
if [[ -n "${PWB_PPM_METADATA_KEY}" && (-z "${PWB_PPM_API_KEY_PATH}" || ! -f "${PWB_PPM_API_KEY_PATH}") ]]; then
   echo "Warning: posit-package-manager-metadata-key is configured but posit-package-manager-api-key-path is not available. Metadata features require authentication."
fi

# if PWB_PPM_API_KEY_PATH is set read the API key and create a netrc file - set NETRC to the path of the netrc file
if [[ -n "${PWB_PPM_API_KEY_PATH}" ]]; then 
   if [[ -f "${PWB_PPM_API_KEY_PATH}" ]]; then
      create_ppm_config_dir

      # Strip the schema (http:// or https://) from the PWB_PPM_HOST_NAME
      PWB_PPM_API_KEY=$(cat "${PWB_PPM_API_KEY_PATH}")

      if [[ -n "${PWB_PPM_API_KEY}" ]]; then
         cat > "${USER_PPM_CONFIG_DIR}/netrc" << EOF
machine ${PWB_PPM_HOST_NAME}
login __token__
password ${PWB_PPM_API_KEY}
EOF

         chmod 600 "${USER_PPM_CONFIG_DIR}/netrc"
         export NETRC="${USER_PPM_CONFIG_DIR}/netrc"

         # Only pass metadata key arguments to the R session if we have a valid API key
         if [[ -n "${PWB_PPM_METADATA_KEY}" ]]; then
            RSESSION_ARGS="${RSESSION_ARGS} --posit-package-manager-metadata-key ${PWB_PPM_METADATA_KEY}"
            
            # Use the display name passed from ServerJobLauncherRpc.cpp
            if [[ -n "${PWB_PPM_METADATA_KEY_DISPLAY_NAME}" ]]; then
               RSESSION_ARGS="${RSESSION_ARGS} --posit-package-manager-metadata-key-display-name ${PWB_PPM_METADATA_KEY_DISPLAY_NAME}"
            fi
         fi

         if [[ "$USER_DIAG_MODE" =~ debug|strace ]]; then
            echo "Created ${USER_PPM_CONFIG_DIR}/netrc file with PWB API key from ${PWB_PPM_API_KEY_PATH}."
         fi
      else
         echo "Warning: PWB_PPM_API_KEY_PATH is set but the file is empty."
      fi
   else
      echo "Warning: PWB_PPM_API_KEY_PATH does not point to a valid file."
   fi
fi


# initialize log file path
: ${RSESSION_DIAGNOSTICS_FILE:=/tmp/rsession-diagnostics.log}
RSESSION_STRACE_FILE="$RSESSION_DIAGNOSTICS_FILE.strace"
RSESSION_SEGFAULT_FILE="$RSESSION_DIAGNOSTICS_FILE.segfault"

# When rserver is local, the diag dir has been created already. 
if [[ -n "${RSESSION_DIAGNOSTICS_ENABLED}" ]]; then
   # When rserver is local, the diag dir has been created already.
   diagDirName=`dirname $RSESSION_DIAGNOSTICS_FILE`
   if [[ -n "${diagDirName}" && ! -d ${diagDirName} ]]; then
      if ! mkdir $diagDirName ; then
         echo "Warning - diagnostics dir: ${diagDirName} not writable by rsession-run - turning off session diagnostics"
         RSESSION_DIAGNOSTICS_ENABLED=
      fi
   fi
fi

findLibSegFault

# dump user information
emitDiagnostic "---- user ---- "
emitDiagnostic "$(whoami)"

# dump initial environment
emitDiagnostic "---- init env ----"
emitDiagnostic "$(printenv)"

# load rsession-profile script from all XDG dirs
if [ "$RSTUDIO_CONFIG_DIR" != "" ] ; then
   configDirs="$RSTUDIO_CONFIG_DIR"
else
   configDirs="$XDG_CONFIG_DIRS:/etc"
fi

origIFS=$IFS
IFS=':'; read -ra configDirList <<< "$configDirs"
for configDir in "${configDirList[@]}"
do
   if test -e "$configDir/rstudio/rsession-profile"
   then
      . "$configDir/rstudio/rsession-profile"
      emitDiagnostic "---- sourced $configDir/rstudio/rsession-profile ----"
      break
   fi
done
IFS=$origIFS

# load rsession-profile script from RStudio-specific config dir
if test -e "$RSTUDIO_CONFIG_DIR/rsession-profile"
then
    . "$RSTUDIO_CONFIG_DIR/rsession-profile"
    emitDiagnostic "---- sourced $RSTUDIO_CONFIG_DIR/rsession-profile ----"
fi

# load module if specified
if [[ -n "${RSTUDIO_R_MODULE}" ]]; then
   # on some systems, /etc/profile contains important path munging utilities
   # that are required for modules to work, so they need to be imported
   if test -e /etc/profile
   then
      source /etc/profile
      emitDiagnostic "---- sourced /etc/profile ----"
   fi

   # load the modules init script - this defaults to ~/.bashrc if no modules-bin-path
   # was specified in rserver.conf - it is assumed that modules are loaded into the shell
   # via ~/.bashrc in most cases - note that on some systems (like Ubuntu), bashrc is a
   # no-op if run non-interactively, which this script is
   #
   # expand the module script path as it can contain ~ paths
   RSTUDIO_R_MODULE_SCRIPT="${RSTUDIO_R_MODULE_SCRIPT/#\~/$HOME}"
   if test -e "${RSTUDIO_R_MODULE_SCRIPT}"
   then
      source "${RSTUDIO_R_MODULE_SCRIPT}"
      emitDiagnostic "---- sourced ${RSTUDIO_R_MODULE_SCRIPT} ----"
   fi

   module load "${RSTUDIO_R_MODULE}"
   emitDiagnostic "---- loaded module $RSTUDIO_R_MODULE ----"
fi

# source prelaunch script if specified
if [[ -n "${RSTUDIO_R_PRELAUNCH_SCRIPT}" ]]; then
   # expand the prelaunch script as it is possible it could point
   # to an aliased home path
   RSTUDIO_R_PRELAUNCH_SCRIPT="${RSTUDIO_R_PRELAUNCH_SCRIPT/#\~/$HOME}"
   if test -e "${RSTUDIO_R_PRELAUNCH_SCRIPT}"
   then
      source "${RSTUDIO_R_PRELAUNCH_SCRIPT}"
      emitDiagnostic "---- sourced prelaunch script $RSTUDIO_R_PRELAUNCH_SCRIPT ----"
   fi
fi

# session arguments

if [[ -n "${RS_SESSION_SSL_CERT}" ]]; then
   if [[ -z "${RS_SESSION_SSL_CERT_KEY}" ]]; then
      echo "Error - public key without private key"
      exit 10
   fi

   CERT_DIR="/tmp/pwb-${USER}"
   mkdir -m 700 -p "${CERT_DIR}"

   CERT_ID=$(date +%s.%N)
   CERT_FILE="${CERT_DIR}/cert-${CERT_ID}.pem"
   KEY_FILE="${CERT_DIR}/key-${CERT_ID}.pem"
   rm -f ${CERT_FILE} ${KEY_FILE}
   (umask 077;
    echo "${RS_SESSION_SSL_CERT}" > "${CERT_FILE}";
    echo "${RS_SESSION_SSL_CERT_KEY}" > "${KEY_FILE}")
   RSESSION_ARGS="${RSESSION_ARGS} --cert ${CERT_FILE} --cert-key ${KEY_FILE}"
elif [[ -n "${RS_SESSION_SSL_CERT_PATH}" ]] ; then
   # eval variables like $HOME and $USER in RS_SESSION_CERT_PATH
   eval CERT_PATH=`echo ${RS_SESSION_SSL_CERT_PATH}`
   eval KEY_PATH=`echo ${RS_SESSION_SSL_CERT_KEY_PATH}`
   RSESSION_ARGS="${RSESSION_ARGS} --cert ${CERT_PATH} --cert-key ${KEY_PATH}"
fi

emitDiagnostic "---- session args ----"
emitDiagnostic "$RSESSION_EXEC_COMMAND + $RSESSION_ARGS"

# dump boot env
emitDiagnostic "---- boot env ----"
emitDiagnostic "$(printenv)"

emitDiagnostic "---- strace ----"
emitDiagnostic "$(getDiagnosticSessionStraceEnabled)"

WWW_REUSE_PORTS="--www-reuse-ports 0"

while true
do
   # run the session
  /bin/bash --login $RSESSION_PROFILE_OPTIONS -c "$(getDiagnosticsPrintenv) $RSESSION_EXEC_COMMAND $(getDiagnosticSessionLdd) $(getDiagnosticSessionOptions) $RSESSION_EXEC_COMMAND $(getDiagnosticSessionStrace) $(dirname ${BASH_SOURCE[0]})/rsession $RSESSION_ARGS $WWW_REUSE_PORTS" $(getDiagnosticSessionRedirection)

  # keep running the session if we are told to continue for suspend and restart
  RSESSION_EXIT_CODE=$?
  if [ "$RSESSION_EXIT_CODE" -ne "102" ]; then
     break
  fi

  emitDiagnostic "---- Launcher session exited with code 102 - restarting ----"

  # make sure when the session restarts it grabs the same bound ports
  WWW_REUSE_PORTS="--www-reuse-ports 1"
done

# dump the session return code and end time
emitDiagnostic "---- exit code ----"
emitDiagnostic "${RSESSION_EXIT_CODE}"

runHooks "stop"

if [[ -n "${CERT_DIR}" ]]; then
   rm -f "${CERT_FILE}"
   rm -f "${KEY_FILE}"
fi

exit ${RSESSION_EXIT_CODE}
