#!/bin/sh ################################################################################ # # This file is part of Reflex # Copyright (C) 2015 European Southern Observatory # # This program 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 2 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, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # ################################################################################ # This is the launch script for the Reflex Java binary. # The command options and help can be seen by running "esoreflex -help". # # Author: Artur Szostak # To report bugs or for further help you can contact: usd-help@eso.org ################################################################################ # This part of the script handles parsing of the command line arguments and # placing any arguments that were not handled into the UNHANDLED_OPTIONS pseudo # array to pass on to the Java binary. # Function to save the $@ array to a variable that can later be restored with # the eval command. We need to do this to support POSIX shells where only the # $@ array is available for use. # See: http://www.etalabs.net/sh_tricks.html # # Usage: # ARRAY=$(save_array "$@") # save all positional parameters # eval "set -- $ARRAY" # load the array in 'ARRAY' # save_array () { for N ; do printf %s\\n "$N" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" done echo " " } # Function to append an item to an array type string. # $1 - An array string variable produced by save_array, or the empty string. # $2 - The item to add. # # Usage: # ARRAY="" # ARRAY=$(append_array "$ARRAY" "$1") # append_array () { TMP_APPEND_VAR=$(save_array "$2") printf "%s%s" "$1" "$TMP_APPEND_VAR" } # Flag indicating if the help message should be printed. PRINT_HELP_MESSAGE=no # Flag indicating if the version information should be printed. PRINT_VERSION_INFO=no # Flag indicating if the workflows should be listed. PRINT_WORKFLOW_LIST=no # Flag indicating if the environment and command line parameters that will be # passed to Reflex should be printed. PRINT_DEBUG_INFO=no # Flag indicating if the non-interactive arguments should be added. ADD_NON_INTERACTIVE_ARGS=no # The user selected configuration file. USER_ESOREFLEX_RC_FILE="" # The name of the new configuration file to create when the # -create-config option is used. NEW_ESOREFLEX_RC_FILE="" # Checks to see if the config file name is valid and then stores the value in # USER_ESOREFLEX_RC_FILE. If invalid then an error message is generated and this # script exits. # $1 - The name of the configuration file. # set_config () { if test -z "$1" ; then echo "ERROR: No file name given for '-config'." 1>&2 exit 1 fi if test ! -f "$1" ; then echo "ERROR: Could not find the configuration file: $1" 1>&2 exit 1 fi USER_ESOREFLEX_RC_FILE="$1" } # Checks the parameter passed to the -create-config option and sets the name # of the configuration file appropriately. Note this will set the variable # EXTRA_PARAMETERS_HANDLED to 1 if the argument really was a file name. # $1 - The value of the -create-config option argument. # handle_create_config_arg () { if test -z "$1" ; then # Empty strings are treated as TRUE. NEW_ESOREFLEX_RC_FILE="$HOME/.esoreflex/esoreflex.rc" else case "$1" in -*) # Anything that starts with a minus sign is treated as # another parameter. NEW_ESOREFLEX_RC_FILE="$HOME/.esoreflex/esoreflex.rc";; TRUE) NEW_ESOREFLEX_RC_FILE="$HOME/.esoreflex/esoreflex.rc" ; EXTRA_PARAMETERS_HANDLED=1 ;; *) NEW_ESOREFLEX_RC_FILE="$1" ; EXTRA_PARAMETERS_HANDLED=1 ;; esac fi } # Handles a single command line argument with optional parameter. # $1 - The command line argument to handle. # $2 - Optional parameter for the argument. # A non-zero value is returned if the option could not be handled. # In addition, the global variable PARAMETERS_HANDLED will be set to indicate # the number of additional argument that were processed besides $1. # handle_argument () { EXTRA_PARAMETERS_HANDLED=0 case "$1" in -h | -help) PRINT_HELP_MESSAGE=yes ;; -v | -version) PRINT_VERSION_INFO=yes ;; -l | -list-workflows) PRINT_WORKFLOW_LIST=yes ;; -n | -non-interactive) ADD_NON_INTERACTIVE_ARGS=yes ;; -config) set_config "$2" ; EXTRA_PARAMETERS_HANDLED=1 ;; -create-config) handle_create_config_arg "$2" ;; -debug) PRINT_DEBUG_INFO=yes ;; *) return 1 ;; esac return 0 } # This function is used to split a group of short options into a list that can # be processed by handle_argument. For example, splits "-vh" into "-v", "-h". # If any characters are not valid options in the group, then the whole argument # is ignored and a non-zero value is returned. # split_and_parse_subargs () { OPTIND=1 SUB_ARGS_LIST="" ALL_VALID=yes while getopts ":hvln" OPTNAME ; do case "$OPTNAME" in h|v|l|n) SUB_ARGS_LIST=$(append_array "$SUB_ARGS_LIST" "-$OPTNAME") ;; *) ALL_VALID=no ; break ;; esac done if test "$OPTIND" -gt 1 -a "$ALL_VALID" = yes ; then eval "set -- $SUB_ARGS_LIST" # Load array for N ; do handle_argument "$N" done return 0 else return 1 fi } # This parses the command line options passed to it. Any options that are not # recognised are saved in the variable UNHANDLED_OPTIONS. # parse_args () { UNHANDLED_OPTIONS="" while test "$#" -gt 0 ; do EXTRA_PARAMETERS_HANDLED=0 # reset for handle_argument. split_and_parse_subargs "$1" || handle_argument "$1" "$2" || \ UNHANDLED_OPTIONS=$(append_array "$UNHANDLED_OPTIONS" "$1") shift # Apply extra shift if extra parameters were handled. if test "$EXTRA_PARAMETERS_HANDLED" -gt 0 ; then shift "$EXTRA_PARAMETERS_HANDLED" fi done } parse_args "$@" ################################################################################ # The following section of this script will relaunch this script with a cleaned # environment in the following conditions: # * The ESOREFLEX_CLEAN_ENVIRONMENT environment variable is TRUE. # * INHERIT_ENVIRONMENT loaded from the .rc file is FALSE. # Note: the very first thing we have to do after parsing the command line in # this script is to find the .rc file, check if we need to inherit the user's # environment and then relaunch with the "env -i ..." command if we are not # supposed to inherit the environment. # Figure out the location of the base path of the Reflex software from the # location of this launch script, if not hardcoded. Use perl if we can to # resolve any symbolic links in the path. The base path is used to identify the # default location of the Reflex .jar and Reflex python files. ESOREFLEX_BASE=$(dirname $0)/../.. if command -v perl > /dev/null ; then ESOREFLEX_BASE=$(perl -MCwd=realpath -e "print realpath '$ESOREFLEX_BASE'") else ESOREFLEX_BASE=$(cd "$ESOREFLEX_BASE" ; pwd) fi ESOREFLEX_JAR="$ESOREFLEX_BASE/esoreflex.jar" # This utility function will escape all special characters of a string so that # it can be used in a sed or grep command without interpreting the special # characters as regular expression commands. The result will be printed to # standard output and should be captured. # $1 - The string to escape. escape_for_regex () { printf %s "$1" | \ sed 's|\([[\|\*?!@#$%^&{}():;,."'\''[:space:]]\)|\\\1|g' | \ sed 's|\]|\\\]|g' } ESCAPED_ESOREFLEX_BASE=$(escape_for_regex "$ESOREFLEX_BASE") # Load a variable of the form name=value from a file. Taking care to escape any # special characters properly. If we don't then code might be injected into the # script when the 'eval' command is called. If the variable could not be found # in the file then nothing is done. # $1 - The name of the file to load the variable from. # $2 - The name of the variable in the file. # $3 - The name of the environment variable to assign the value to. # load_variable () { if grep "^[[:space:]]*${2}[[:space:]]*=.*$" "$1" > /dev/null ; then # Note: the first sed command is for removing the variable name prefix. # The second last performs the macro substitution for ${esoreflex_base}. # The last is for escaping any special characters when used in the eval. TMP_VAR=$(grep "^[[:space:]]*${2}[[:space:]]*=.*$" "$1" | tail -n 1 | \ sed "s|^[[:space:]]*${2}[[:space:]]*=||" | \ sed "s|\${esoreflex_base}|$ESCAPED_ESOREFLEX_BASE|" | \ sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/'/") eval "$3"="$TMP_VAR" fi } # Supply a default value for INHERIT_ENVIRONMENT in esoreflex.rc INHERIT_ENVIRONMENT=TRUE # The name of the "inherit" parameter in the esoreflex.rc file. INHERIT_PARAM_NAME='esoreflex.inherit-environment' # The location of the default system configuration file. ESOREFLEX_SYSTEM_RC="/etc/esoreflex.rc" # The location of the esoreflex.rc file that was loaded. ESOREFLEX_RC_FILE="" # If we were not given a configuration file, then try find the esoreflex.rc file # that should be loaded in standard locations and load INHERIT_ENVIRONMENT. if test -z "$USER_ESOREFLEX_RC_FILE" ; then for N in ~/.esoreflex/esoreflex.rc "$ESOREFLEX_SYSTEM_RC" ; do if test -f "$N" ; then ESOREFLEX_RC_FILE="$N" break fi done else ESOREFLEX_RC_FILE="$USER_ESOREFLEX_RC_FILE" fi if test -n "$ESOREFLEX_RC_FILE" ; then load_variable "$ESOREFLEX_RC_FILE" "$INHERIT_PARAM_NAME" 'INHERIT_ENVIRONMENT' fi # Convert boolean values to upper case, but remember the original string in case # we need to print an error or warning message. ORIGINAL_INHERIT_VALUE="$INHERIT_ENVIRONMENT" INHERIT_ENVIRONMENT=$(printf %s "$INHERIT_ENVIRONMENT" | tr '[:lower:]' '[:upper:]') ORIGINAL_CLEAN_VALUE="$ESOREFLEX_CLEAN_ENVIRONMENT" ESOREFLEX_CLEAN_ENVIRONMENT=$(printf %s "$ESOREFLEX_CLEAN_ENVIRONMENT" | tr '[:lower:]' '[:upper:]') if test "$INHERIT_ENVIRONMENT" = FALSE ; then # Set the flag to clean the environment if we are not supposed to inherit the # user's environment. However, use any existing value for the variable # ESOREFLEX_CLEAN_ENVIRONMENT if it was set and not null. i.e. environment # variables take precedence to parameters in the esoreflex.rc file. ESOREFLEX_CLEAN_ENVIRONMENT=${ESOREFLEX_CLEAN_ENVIRONMENT:-TRUE} elif test "$INHERIT_ENVIRONMENT" != TRUE ; then MSG="ERROR: Found an invalid value '$ORIGINAL_INHERIT_VALUE' for\ $INHERIT_PARAM_NAME in file '$ESOREFLEX_RC_FILE'. Must be one of TRUE or\ FALSE." echo "$MSG" 1>&2 exit 1 fi # Restart this script in a clean environment if so requested. Make sure the # ESOREFLEX_CLEAN_ENVIRONMENT variable is FALSE so we don't get into a loop. if test "$ESOREFLEX_CLEAN_ENVIRONMENT" = TRUE ; then exec env -i ESOREFLEX_CLEAN_ENVIRONMENT=FALSE \ HOME="$(cd ~; pwd)" \ ${LANG+LANG="$LANG"} \ ${USER+USER="$USER"} \ ${LOGNAME+LOGNAME="$LOGNAME"} \ ${HOSTNAME+HOSTNAME="$HOSTNAME"} \ ${DISPLAY+DISPLAY="$DISPLAY"} \ ${XAUTHORITY+XAUTHORITY="$XAUTHORITY"} \ PATH="$(getconf PATH)" \ "$0" "$@" elif test -n "$ESOREFLEX_CLEAN_ENVIRONMENT" -a \ "$ESOREFLEX_CLEAN_ENVIRONMENT" != FALSE then MSG="Warning: Found an invalid value '$ORIGINAL_CLEAN_VALUE' for\ ESOREFLEX_CLEAN_ENVIRONMENT. Must be one of TRUE or FALSE. Assuming it to\ be FALSE." echo "$MSG" 1>&2 ESOREFLEX_CLEAN_ENVIRONMENT=FALSE fi # Make sure we do not export ESOREFLEX_CLEAN_ENVIRONMENT to the Reflex binary. TMP_ESOREFLEX_CLEAN_ENVIRONMENT="$ESOREFLEX_CLEAN_ENVIRONMENT" unset ESOREFLEX_CLEAN_ENVIRONMENT ESOREFLEX_CLEAN_ENVIRONMENT="$TMP_ESOREFLEX_CLEAN_ENVIRONMENT" ################################################################################ # From here on we perform the normal loading of the esoreflex.rc configuration # file and launch the Reflex Java binary. # Supply default values for various parameters in esoreflex.rc JAVA_COMMAND=java ESOREFLEX_WORKFLOW_PATH=~/KeplerData/workflows/MyWorkflows ESOREX_COMMAND=esorex LOAD_ESOREX_CONFIG="" LOAD_ESOREX_RECIPE_CONFIG="" PYTHON_COMMAND=python EXTRA_PYTHONPATH="$ESOREFLEX_BASE/esoreflex/python" EXTRA_PATH="" EXTRA_LIBRARY_PATH="" # Load the various parameters from a file and override the default ones that # were set before. # $1 - The name of the .rc file to load from. # load_esoreflex_rc () { if test -f "$1" ; then load_variable "$1" 'esoreflex.java-command' 'JAVA_COMMAND' load_variable "$1" 'esoreflex.workflow-path' 'ESOREFLEX_WORKFLOW_PATH' load_variable "$1" 'esoreflex.esorex-command' 'ESOREX_COMMAND' load_variable "$1" 'esoreflex.esorex-config' 'LOAD_ESOREX_CONFIG' load_variable "$1" 'esoreflex.esorex-recipe-config' 'LOAD_ESOREX_RECIPE_CONFIG' load_variable "$1" 'esoreflex.python-command' 'PYTHON_COMMAND' load_variable "$1" 'esoreflex.python-path' 'EXTRA_PYTHONPATH' load_variable "$1" 'esoreflex.path' 'EXTRA_PATH' load_variable "$1" 'esoreflex.library-path' 'EXTRA_LIBRARY_PATH' fi } # This function checks if a command configured in the esoreflex.rc file appears # valid. If it does not then an error message is produced and the script exits. # $1 - The command name being checked. # $2 - The command as configured in esoreflex.rc to check. # $3 - The expected response string indicating the command is valid. # $4 - The path of the esoreflex.rc file in which the command was configured. # check_command () { if command -v "$1" > /dev/null ; then TMP_RESULT=$($2 2>&1) if test "$?" -ne 0 || ! (echo "$TMP_RESULT" | grep -i "$3" > /dev/null) ; then echo "$TMP_RESULT" 1>&2 echo "ERROR: $1 command configured in $4 is invalid or an unsupported version." 1>&2 exit 1 fi fi } # Checks if the commands configured in the esoreflex.rc file are found and usable. # $1 - The name of the .rc file from which the commands were loaded. # check_configured_commands () { check_command java "$JAVA_COMMAND -version" "java\|jdk" "$1" check_command esorex "$ESOREX_COMMAND --version" "ESO Recipe Execution Tool" "$1" check_command python "$PYTHON_COMMAND --version" "python" "$1" } # Merges one or more paths together separated by a colon. # $@ - The list of paths to merge. # merge_path_lists () { RESULT="" for N in "$@" ; do test -n "$N" || continue if test -z "$RESULT" ; then RESULT="$N" else RESULT="$RESULT:$N" fi done printf %s "$RESULT" } # Prepare a script file for a customised command invocation. This will write a # script that contains the command to invoke under ~/.esoreflex/bin// # and makes the script executable. Then the PATH is updated to make sure the new # command invocation will take precedence. # $1 - The command's check-sum. # $2 - The command to invoke. # $3 - The original PATH variable. # $4 - The script file name to generate. # prepare_command_script () { mkdir -p ~/.esoreflex/bin/"$1" cat > ~/.esoreflex/bin/"$1"/"$4" <&1) if test "$?" -ne 0 ; then echo "$TMP_RESULT" 1>&2 MSG="ERROR: Java was not found. Please check if the PATH environment\ variable is configured correctly. Otherwise install Java\ $MIN_JAVA_VERSION_MAJOR.$MIN_JAVA_VERSION_MINOR or newer." echo "$MSG" 1>&2 exit 1 fi JAVA_VERSION=$(echo "$TMP_RESULT" | head -n 1 | sed -n 's|[^[:digit:]]*\([[:digit:]][[:digit:]]*\.[[:digit:]][[:digit:]]*\).*|\1| p') test -z "$JAVA_VERSION" && JAVA_VERSION="0.0" JAVA_VERSION_MAJOR=$(echo $JAVA_VERSION | sed 's|\([[:digit:]]*\)\.\([[:digit:]]*\)|\1|') JAVA_VERSION_MINOR=$(echo $JAVA_VERSION | sed 's|\([[:digit:]]*\)\.\([[:digit:]]*\)|\2|') VALID_JAVA_VERSION=yes if test "$JAVA_VERSION_MAJOR" -lt "$MIN_JAVA_VERSION_MAJOR" ; then VALID_JAVA_VERSION=no elif test "$JAVA_VERSION_MAJOR" -eq "$MIN_JAVA_VERSION_MAJOR" ; then if test "$JAVA_VERSION_MINOR" -lt "$MIN_JAVA_VERSION_MINOR" ; then VALID_JAVA_VERSION=no fi fi if test "$VALID_JAVA_VERSION" = no ; then echo "$TMP_RESULT" 1>&2 MSG="ERROR: The detected Java version is not compatible with this Reflex version.\ Please check if the PATH environment variable is configured correctly.\ Otherwise install Java $MIN_JAVA_VERSION_MAJOR.$MIN_JAVA_VERSION_MINOR or newer." echo "$MSG" 1>&2 exit 1 fi } # Adds the path used by xorg-server for log files if it is missing, otherwise it # will fail to start. This is only done on the OSX platform if xorg-server is # installed with macports. # setup_X11_path () { test "$(uname)" = "Darwin" || return test -e ~/Library/Logs/X11 && return if port installed xorg-server 2>&1 | grep xorg-server > /dev/null ; then mkdir -p ~/Library/Logs/X11 fi } # Fetches the Reflex version from the 'version' file found under the Reflex # installation path. If that cannot be found then the name of the parent # directory is used as long as it contains the string "reflex". Otherwise # "unknown" is returned. The output needs to be captured into a shell variable. # get_reflex_version () { ESOREFLEX_BASENAME=$(basename "$ESOREFLEX_BASE") if test -f "$ESOREFLEX_BASE/esoreflex/version" ; then cat "$ESOREFLEX_BASE/esoreflex/version" elif test -n "$ESOREFLEX_BASENAME" && \ printf %s "$ESOREFLEX_BASENAME" | grep -i reflex > /dev/null then printf %s "$ESOREFLEX_BASENAME" else printf %s "unknown" fi } # Returns just the Reflex version string in a normalised format. # get_normalised_reflex_version () { REFLEX_VERSION=$(get_reflex_version) REFLEX_VERSION=$(printf %s "$REFLEX_VERSION" | \ sed -n 's|\([^[:digit:]]*-\)*\([[:digit:]].*\)$|\2| p' | \ sed 's|-linux$||' | sed 's|-osx$||' | sed 's|-||g') test -z "$REFLEX_VERSION" && REFLEX_VERSION="0.0" printf %s "$REFLEX_VERSION" } # Writes a new configuration file to the file given and exits. Note that this # function should be called after load_esoreflex_rc to take into account # settings coming from an existing .rc file. # $1 - The name of the new configuration file to create. # write_config_file () { # Make a backup of any existing file or directory. if test -e "$1" ; then BACKUP_FILE="$1.bak" N=1 while test -e "$BACKUP_FILE" ; do BACKUP_FILE="$1.bak$N" if test "$N" -gt 999999 ; then # Prevent infinite loops. echo "ERROR: Could not find a unique name to backup: $1" 1>&2 exit 2 fi N=$(($N+1)) done mv "$1" "$BACKUP_FILE" || exit 1 fi # Get the reflex version string for the config file, in a normalised format. REFLEX_VERSION=$(get_normalised_reflex_version) # Write the new configuration file. # NOTE: if you change the structure of this file here you should also update # the .rc files shipped with or created by the install_esoreflex, MacPorts # and RPM packages etc. mkdir -p $(dirname "$1") || exit 1 cat < "$1" # This file has been produced with esoreflex ${REFLEX_VERSION}. # Warning: if you do modify this file or use it with a different esoreflex # version, it is possible it may not work properly. You will have to know what # you are doing. # TRUE/FALSE indicating if the user's environment should be inherited. esoreflex.inherit-environment=${INHERIT_ENVIRONMENT} # The binary or command used to start Java. esoreflex.java-command=${JAVA_COMMAND} # The search paths for workflows used by the launch script. esoreflex.workflow-path=${ESOREFLEX_WORKFLOW_PATH} # The command used to launch esorex. esoreflex.esorex-command=${ESOREX_COMMAND} # The path to the esorex configuration file. esoreflex.esorex-config=${LOAD_ESOREX_CONFIG} # The path to the dummy configuration file for recipes. esoreflex.esorex-recipe-config=${LOAD_ESOREX_RECIPE_CONFIG} # The command used to launch python. esoreflex.python-command=${PYTHON_COMMAND} # Additional search paths for python modules. PYTHONPATH will be set to this # value if esoreflex.inherit-environment=FALSE. However, the contents of # esoreflex.python-path will be appended to any existing PYTHONPATH from the # user's environment if esoreflex.inherit-environment=TRUE. Note that removing # any paths that were added during the installation may break esoreflex. esoreflex.python-path=${EXTRA_PYTHONPATH} # Additional search paths for binaries. This will be prepended to the default # system PATH as returned by getconf if esoreflex.inherit-environment=FALSE. # Otherwise the contents is appended to the user's PATH environment variable if # esoreflex.inherit-environment=TRUE. Note that removing any paths that were # added during the installation may break esoreflex. esoreflex.path=${EXTRA_PATH} # Additional search paths for shared libraries. (DY)LD_LIBRARY_PATH will be set # to this if esoreflex.inherit-environment=FALSE. However, the contents of # esoreflex.library-path will be appended to any existing (DY)LD_LIBRARY_PATH # variables from the user's environment if esoreflex.inherit-environment=TRUE. # Note that removing any paths that were added during the installation may break # esoreflex. esoreflex.library-path=${EXTRA_LIBRARY_PATH} EOF test "$?" -ne 0 && exit 1 exit } # This function is used to split a string of paths separated by the colon ':' # character into a pseudo array. The function should be used like save_array, # i.e. the generated output should be captured to a variable. # $1 - The paths string to split. # split_paths () { # We have to escape any special characters in the home path before using it # in the sed command. ESCAPED_HOME=$(escape_for_regex "$HOME") # Save the field splitting characters and set it to a colon to split the # paths in $1. OLDIFS="$IFS" IFS=":" for N in $1 ; do # We perform a tilde expansion, taking care of leaving '\~' as '~', and # then convert to an array element. printf %s\\n "$N" | sed "s|[\]~|$(printf '\001')|g" | \ sed "s|~|$ESCAPED_HOME|g" | tr '\001' '~' | \ sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" done IFS="$OLDIFS" # Restore field splitting character. echo " " } # The following function splits input lines into array elements, producing a # pseudo array format compatible with save_array. The result should be captured # to a variable. # split_lines () { # Note: set IFS to empty to prevent splitting into fields. while IFS="" read -r N ; do printf %s\\n "$N" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" done echo " " } # Prints a help/usage message, including the parameters supported by Kepler. # print_help () { echo "Usage: esoreflex [-h] [-v] [-l] [-n] [other workflow or Kepler arguments]" echo "" echo "-h | -help print this help message and exit." echo "-v | -version show installed Reflex version and pipelines and exit." echo "-l | -list-workflows list available installed workflows and from ~/KeplerData/workflows." echo "-n | -non-interactive enable non-interactive features." echo "-config allows to specify a custom esoreflex.rc configuration file." echo "-create-config if is TRUE then a new configuration file is created in" echo " ~/.esoreflex/esoreflex.rc. Alternatively a configuration file name" echo " can be given to write to. Any existing file is backed up to a file" echo " with a '.bak' extension, or '.bakN' where N is an integer." echo "-debug prints the environment and actual Reflex launch command used." # The following generates the Kepler part of the help message. TMP_RESULT=$($JAVA_COMMAND -jar "$ESOREFLEX_JAR" -h 2>&1) if test "$?" -ne 0 ; then echo "" echo "(Warning: Kepler options are not available because there is a problem with Java." echo " Run again with only the -debug option for details.)" else printf %s "$TMP_RESULT" | \ grep '\[null\]' | grep -v '\[null\] USAGE:' | \ sed 's|[[:space:]]*\[null\][[:space:]]\{0,1\}||' | \ sed 's|[Kk][Ee][Pp][Ll][Ee][Rr]|esoreflex|' fi echo "" echo "Reflex behaviour can also be customised with a ~/.esoreflex/esoreflex.rc configuration file." echo "This must be a text file that contains 'name=value' pairs. Comments start with #." echo "If a configuration file is given with the '-config' option then it takes precedence." echo "The following are valid configuration parameters that can be set:" echo " esoreflex.inherit-environment - TRUE/FALSE indicating if the user's environment should be inherited." echo " esoreflex.java-command - the Java binary or command to use to start Java." echo " esoreflex.workflow-path - colon separated list of paths in which to search for workflows." echo " esoreflex.esorex-command - the esorex command to invoke, e.g. 'likwid-pin -c N:0-3 esorex'." echo " esoreflex.esorex-config - the location of the esorex configuration file to use." echo " esoreflex.esorex-recipe-config - the location of the default recipe configuration file to use." echo " esoreflex.python-command - the python command to invoke, e.g. '/usr/bin/python27'." echo " esoreflex.python-path - colon separated list of additional python paths to use." echo " esoreflex.path - colon separated list of additional binary search paths added to PATH." echo " esoreflex.library-path - colon separated list of additional paths added to (DY)LD_LIBRARY_PATH." echo "Note that one can use the macro \${esoreflex_base} in the above values as an alternative to" echo "hardcoding the base installation path." echo "" echo "In addition, the environment variable ESOREFLEX_CLEAN_ENVIRONMENT can be used" echo "to force Reflex to start in a clean environment. Valid values are TRUE or FALSE." echo "By default the environment is always cleaned. If esoreflex.inherit-environment" echo "is set then ESOREFLEX_CLEAN_ENVIRONMENT will be set to its boolean inverse." exit } # Prints the version of Reflex and of any pipelines detected by esorex. # print_version () { # Get the recipe-dir paths from esorex. Note, by the time we call esorex we # expect the PATH to be updated appropriately to pick up the correct version. RECIPE_PATHS=$(esorex --params 2> /dev/null | grep recipe-dir | \ sed 's|^[[:space:]]*recipe-dir[[:space:]]*:[[:space:]]*||') PATHS_ARRAY=$(split_paths "$RECIPE_PATHS") eval "set -- $PATHS_ARRAY" # Load array # For each recipe-dir path, find shared objects (".so" files) and use the # parent path name as the pipeline version string. This works for standard # pipelines which install to a named path. PIPE_VERSIONS="" for N in "$@" ; do # Note: we use the 'tr' and 'sed' commands to change any end of line # characters in the file names to the '\n' character string, so that we # can handle the names correctly with 'read' and 'sort'. TMP_ARRAY=$(find -L "$N" -iname "*.so" -exec printf '%s\000' {} \; 2> /dev/null | \ tr '\n\000' '\001\n' | sed "s|[$(printf '\001')]|\\\\n|g" | \ while IFS="" read -r M ; do \ dirname "$M" ; \ done | \ sort -u | \ while IFS="" read -r M ; do \ basename "$M" ; \ done | \ sort -u | split_lines) PIPE_VERSIONS="$PIPE_VERSIONS $TMP_ARRAY" # merge arrays done # Print the versions. REFLEX_VERSION=$(get_reflex_version) echo "Reflex version:" echo " $REFLEX_VERSION" echo "Installed pipelines:" PIPELINES_DETECTED=no eval "set -- $PIPE_VERSIONS" # Load array for N in "$@" ; do echo " $N" PIPELINES_DETECTED=yes done if test "$PIPELINES_DETECTED" = no ; then echo "(None detected)" fi exit } # Checks if an item exists inside an array. Returns zero if it was found and a # non-zero return code otherwise. # $1 - The item to find. # $2 - The pseudo array to search in. # exists_in_array () { TMP_VALUE_TO_CHECK="$1" eval "set -- $2" # Load array # Note: we use shifting to go through the array since anything else would # clobber global variables. while test "$#" -gt 0 ; do test "$TMP_VALUE_TO_CHECK" = "$1" && return 0 shift done return 1 } # Global pseudo array of workflow short names generated by get_short_name. USED_SHORT_NAMES="" # This function is used to create a list of short names for workflow paths. # Short names are made unique by keeping track of the ones already used in the # pseudo array USED_SHORT_NAMES. # $1 - The path name to a workflow file. # The following variables are updated: # SHORT_NAME - The resulting short name to use for the workflow path. # USED_SHORT_NAMES - The array of short names already generated by this function. # get_short_name () { SHORT_NAME=$(basename "$1" | sed 's|\.[[:alnum:]][[:alnum:]]*$||') if exists_in_array "$SHORT_NAME" "$USED_SHORT_NAMES" ; then SHORT_NAME=$(basename "$1") TMP_REMAINDER=$(dirname "$1") while exists_in_array "$SHORT_NAME" "$USED_SHORT_NAMES" ; do SHORT_NAME="$(basename "$TMP_REMAINDER")/$SHORT_NAME" TMP_REMAINDER=$(dirname "$TMP_REMAINDER") done fi USED_SHORT_NAMES=$(append_array "$USED_SHORT_NAMES" "$SHORT_NAME") } # Global map of short workflow names and their full path names. # The map is a pseudo array of consecutive pairs of items. Counting from 1, the # item in position 2*n-1 is the short name and its corresponding path is at 2*n. WORKFLOW_MAP="" # Create a short-name to path mapping for workflow files found in one of the # paths given by ESOREFLEX_WORKFLOW_PATH. The mapping is stored in WORKFLOW_MAP. # make_workflow_map () { PATHS_ARRAY=$(split_paths "$ESOREFLEX_WORKFLOW_PATH") eval "set -- $PATHS_ARRAY" # Load array # Find the XML and KAR files found under each path. TMP_WORKFLOWS="" for N in "$@" ; do # Note: we use the 'tr' and 'sed' commands to change any end of line # characters in the file names to the '\n' character string for sane # printing. However, we do not sort the output of find. We rely on the # order that find returns its values when we try to replace short # workflow names with their full paths in expand_workflow_path. TMP_ARRAY=$(find -L "$N" \( -iname "*.xml" -o -iname "*.kar" \) -exec printf '%s\000' {} \; 2> /dev/null | \ tr '\n\000' '\001\n' | sed "s|[$(printf '\001')]|\\\\n|g" | \ split_lines) TMP_WORKFLOWS="$TMP_WORKFLOWS $TMP_ARRAY" # merge arrays done eval "set -- $TMP_WORKFLOWS" # Load array USED_SHORT_NAMES="" # reset global variable used by get_short_name. WORKFLOW_MAP="" for N in "$@" ; do get_short_name "$N" TMP_MAP_ENTRY=$(save_array "$SHORT_NAME" "$N") WORKFLOW_MAP="$WORKFLOW_MAP $TMP_MAP_ENTRY" done } # Find the list of standard workflows installed and those under the KeplerData # directory in the home path. # list_workflows () { HEADING="Workflow name" # Prepare the underline string for the heading. UNDERLINE=$(printf %s "$HEADING" | sed 's|.|-|g') # Figure out the shortest field width necessary to align all the short names. MIN_WIDTH=$(printf %s "$HEADING" | wc -m) eval "set -- $WORKFLOW_MAP" # Load array while test "$#" -gt 0 ; do VAL=$(printf %s "$1" | wc -m) test "$VAL" -gt "$MIN_WIDTH" && MIN_WIDTH="$VAL" shift 2 done # Now print the table of workflows. PRINT_FORMAT="%${MIN_WIDTH}s %s\n" printf "$PRINT_FORMAT" "$HEADING" "Full path" printf "$PRINT_FORMAT" "$UNDERLINE" "---------" WORKFLOWS_DETECTED=no eval "set -- $WORKFLOW_MAP" # Load array while test "$#" -gt 0 ; do printf "$PRINT_FORMAT" "$1" "$2" WORKFLOWS_DETECTED=yes shift 2 done if test "$WORKFLOWS_DETECTED" = no ; then echo "(None found)" fi exit } # Global variable indicating the location of the temporary directory used when # non-interactive mode is chosen for Reflex. ESOREFLEX_TMP_DIR="/tmp/reflex_no_gui" # Global flag indicating if the temporary directory was created by this script # or not. Updated by the prepare_non_interactive function. ESOREFLEX_TMP_DIR_CREATED=no # Global flag indicating if Reflex should be run in non-interactive mode or not. # This is set by the prepare_non_interactive function. RUN_NON_INTERACTIVE=no # Called when the '-n' command line option is chosen. Will create the temporary # directory needed for non-interactive mode and updates the global flags # indicating that appropriate arguments for non-interactive mode should be # passed to the Reflex binary. # prepare_non_interactive () { if test ! -e "$ESOREFLEX_TMP_DIR" ; then mkdir -p /tmp/reflex_no_gui ESOREFLEX_TMP_DIR_CREATED=yes fi RUN_NON_INTERACTIVE=yes } # Expands a short workflow name into its full path if it can. The WORKFLOW_MAP # variable must be setup before hand with a call to make_workflow_map. # The output must be captured into a shell variable. # $1 - The short name of the workflow. # # Usage: # FULLPATH=$(expand_workflow_path "$SHORT_NAME") # expand_workflow_path () { SHORT_NAME="$1" eval "set -- $WORKFLOW_MAP" # Load array while test "$#" -gt 0 ; do if test "$SHORT_NAME" = "$1" ; then printf %s "$2" return fi shift 2 done printf %s "$SHORT_NAME" } # Expand a relative path into a full path by pre-pending the current working # directory. If the path is already absolute then just return it. # $1 - The path to expand. # expand_relative_path () { if printf %s "$1" | grep '^/' > /dev/null ; then printf %s "$1" else printf %s "$(pwd)/$1" fi } # The following are arguments used to build up the non-interactive options # passed to the Reflex binary when the '-n' option was given to this script. KEPLER_XML_ARGS=$(save_array "-runwf" "-nogui" "-nocache" "-redirectgui" "$ESOREFLEX_TMP_DIR") KEPLER_KAR_ARGS=$(save_array "-runkar" "-nogui" "-redirectgui" "$ESOREFLEX_TMP_DIR") # non-interactive arguments for Reflex versions 2.7 and older: KEPLER_EXTRA_ARGS_27=$(save_array "-SelectDatasetMethod" "Complete" \ "-RecipeFailureMode" "Continue" \ "-GlobalPlotInteractivity" "false" \ "-ProvenanceExplorerEnabled" "false") # non-interactive arguments for Reflex versions newer than 2.8: KEPLER_EXTRA_ARGS_28=$(save_array "-SelectDatasetMethod" "New" \ "-RecipeFailureMode" "Continue" \ "-GlobalPlotInteractivity" "false" \ "-ProductExplorerMode" "Disabled") # Selects and sets the KEPLER_EXTRA_ARGS variable to the appropriate values for # Kepler, based on the Reflex version being launched. # select_kepler_args () { REFLEX_VERSION=$(get_reflex_version) REFLEX_VERSION=$(printf %s "$REFLEX_VERSION" | sed -n 's|[^[:digit:]]*\([[:digit:]][[:digit:]]*\.[[:digit:]][[:digit:]]*\).*|\1| p') test -z "$REFLEX_VERSION" && REFLEX_VERSION="0.0" REFLEX_VERSION_MAJOR=$(printf %s $REFLEX_VERSION | sed 's|\([[:digit:]]*\)\.\([[:digit:]]*\)|\1|') REFLEX_VERSION_MINOR=$(printf %s $REFLEX_VERSION | sed 's|\([[:digit:]]*\)\.\([[:digit:]]*\)|\2|') if test "$REFLEX_VERSION_MAJOR" -lt 2 ; then KEPLER_EXTRA_ARGS="$KEPLER_EXTRA_ARGS_27" elif test "$REFLEX_VERSION_MAJOR" -lt 3 ; then if test "$REFLEX_VERSION_MINOR" -lt 8 ; then KEPLER_EXTRA_ARGS="$KEPLER_EXTRA_ARGS_27" else KEPLER_EXTRA_ARGS="$KEPLER_EXTRA_ARGS_28" fi else KEPLER_EXTRA_ARGS="$KEPLER_EXTRA_ARGS_28" fi } # Expands an array of arguments (in the format returned by save_array) and returns # the another array with the expanded arguments, which must be captured to a shell # variable. The following argument expansions are performed: # * Expand short workflow names to their full paths. # * Expand any relative paths for workflow names to absolute paths relative to # the current working directory. # * Add the appropriate options for loading XML versus KAR files. # * Add the appropriate options if the non-interactive mode was selected. # Parameters: # $1 - The pseudo array of arguments. # expand_args () { select_kepler_args eval "set -- $1" # Load array NUMBER_XML_FOUND=0 NUMBER_KAR_FOUND=0 UPDATED_ARGS="" for N in "$@" ; do # We try perform a lookup of the argument N in the workflow mapping. If # it is found then we assume its a short workflow name and replace N # with the full file path. Otherwise if the argument N ends in .xml or # .kar, we assume it is a workflow file name with a relative or absolute # path. We expand the path and make sure the file exists. If none of # these path expansions can be done then we have to assume that N is # actually an argument for Kepler and simply pass it on. TMP_WORKFLOW_PATH=$(expand_workflow_path "$N") if test -e "$TMP_WORKFLOW_PATH" ; then N="$TMP_WORKFLOW_PATH" elif printf %s "$N" | grep -q -E -i '\.(xml$)|(kar$)' ; then TMP_WORKFLOW_PATH=$(expand_relative_path "$N") if test -e "$TMP_WORKFLOW_PATH" ; then N="$TMP_WORKFLOW_PATH" else echo "ERROR: Could not find file: $N" 1>&2 exit 1 fi fi # Count the number of .xml and .kar workflow file paths that were found. if printf %s "$N" | grep -q -E -i '\.xml$' ; then NUMBER_XML_FOUND=$(($NUMBER_XML_FOUND + 1)) elif printf %s "$N" | grep -q -E -i '\.kar$' ; then NUMBER_KAR_FOUND=$(($NUMBER_KAR_FOUND + 1)) fi UPDATED_ARGS=$(append_array "$UPDATED_ARGS" "$N") done if test "$NUMBER_XML_FOUND" -gt 0 -a "$NUMBER_KAR_FOUND" -gt 0 ; then echo "ERROR: Cannot have both a .xml and .kar specified." 1>&2 exit 1 fi EXTRA_ARGS="" if test "$NUMBER_XML_FOUND" -gt 0 -a "$RUN_NON_INTERACTIVE" = yes ; then EXTRA_ARGS="$EXTRA_ARGS $KEPLER_XML_ARGS $KEPLER_EXTRA_ARGS" elif test "$NUMBER_KAR_FOUND" -gt 0 -a "$RUN_NON_INTERACTIVE" = yes ; then EXTRA_ARGS="$EXTRA_ARGS $KEPLER_KAR_ARGS $KEPLER_EXTRA_ARGS" fi UPDATED_ARGS="$EXTRA_ARGS $UPDATED_ARGS" } # Tries to prints the locations of command binaries given in the list of input # arguments. The input command strings are broken up if they comprise a list of # commands, e.g. "nice esorex" will try find "nice" and "esorex" individually. # $@ - The list of commands or command strings. # print_binary_locations () { # Get a unique list of individual commands from all input command strings. COMMANDS=$(for N in $@ ; do echo "$N" ; done | sort -u) ANY_MISSING=no for N in $COMMANDS ; do case "$N" in -*) # Skip fields that start with '-'. Assume these are arguments. ;; *) command -v "$N" && continue test -e "$N" && continue # At this point the command either could not be found and it is # not an existing file or directory. Thus it may really be a # missing command or a parameter for an argument we dont know # how to interpret. Thus, we just print a notice about the # possibility of missing commands rather than trying to print # the supposedly missing command name, since otherwise this might # show misleading information to the user if it turned out that # $N is actually an argument parameter. ANY_MISSING=yes ;; esac done if test "$ANY_MISSING" = yes ; then echo "Note: Any commands not listed above may be missing." fi } # Prints all arguments forming the launch command. But we escape any special # characters so that the command should work if copying and pasting it into # another terminal. # print_launch_command () { while test "$#" -gt 0 ; do # Note: change the newlines before calling sed since on OSX the sed # command adds new lines to the end of the output. Changing these first # will allow us to reconstruct a correctly formated output. ESCAPED_ARG=$(printf %s "$1" | \ tr '\n' '\001' | \ sed 's|\([[\|\*?!@#$%^&{}():;,"'\''[:space:]]\)|\\\1|g' | \ sed 's|\]|\\\]|g' | \ sed "s|\([$(printf '\001')]\)|'\\1'|g" | \ tr '\001' '\n') printf "%s " "$ESCAPED_ARG" shift done printf "\n" } load_esoreflex_rc "$ESOREFLEX_RC_FILE" test -n "$NEW_ESOREFLEX_RC_FILE" && write_config_file "$NEW_ESOREFLEX_RC_FILE" update_paths_and_env # Must check the configured commands only after the PATH, LD_LIBRARY_PATH etc # were updated in update_paths_and_env. Otherwise might not find the binaries. check_configured_commands "$ESOREFLEX_RC_FILE" setup_X11_path make_workflow_map # Printing of version information, workflows and help messages must be done # after we have loaded the esoreflex.rc file and the environment is updated. test "$PRINT_HELP_MESSAGE" = yes && print_help test "$PRINT_VERSION_INFO" = yes && print_version test "$PRINT_WORKFLOW_LIST" = yes && list_workflows test "$ADD_NON_INTERACTIVE_ARGS" = yes && prepare_non_interactive # Delay checking of the Java version until after printing the help messages. # Otherwise we can never see the at least part of the help message if there is # a problem with Java, since check_java_version will exit on error. check_java_version expand_args "$UNHANDLED_OPTIONS" eval "set -- $UPDATED_ARGS" # Load arguments that will be passed to Reflex. if test "$PRINT_DEBUG_INFO" = yes ; then if test -n "$ESOREFLEX_RC_FILE" ; then echo "Using configuration file: $ESOREFLEX_RC_FILE" REFLEX_VERSION=$(get_normalised_reflex_version) ESCAPED_VERSION=$(escape_for_regex "$REFLEX_VERSION") if ! grep "esoreflex $ESCAPED_VERSION" "$ESOREFLEX_RC_FILE" > /dev/null ; then echo "Warning: It appears the configuration file was created by a different esoreflex version than $REFLEX_VERSION." 1>&2 fi else echo "Using configuration file: (none)" fi echo "=============================== Environment used ===============================" env echo "=============================== Binary locations ===============================" # Adding the esorex and python commands explicitly in case they are missing # in the command variables. print_binary_locations "$JAVA_COMMAND" esorex python "$ESOREX_COMMAND" "$PYTHON_COMMAND" echo "================================ Launch command ================================" print_launch_command $JAVA_COMMAND -jar "$ESOREFLEX_JAR" "$@" echo "================================================================================" fi # Now execute the esoreflex Java application. Save the exit code to return it later. $JAVA_COMMAND -jar "$ESOREFLEX_JAR" "$@" RESULT="$?" # Cleanup the temporary directory if it was created by prepare_non_interactive. test "$ESOREFLEX_TMP_DIR_CREATED" = "yes" && rm -rf $ESOREFLEX_TMP_DIR exit "$RESULT"