#! /bin/sh

# 
# Copyright 1999-2006 University of Chicago
# 
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# 
# http://www.apache.org/licenses/LICENSE-2.0
# 
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# 


#
# globus-job-run and globus-job-submit (same code base)
#

prefix="${GLOBUS_LOCATION-/cvmfs/dirac.egi.eu/dirac/v9.0.0a31/Linux-x86_64}"
exec_prefix="${prefix}"
sbindir="${exec_prefix}/sbin"
bindir="${exec_prefix}/bin"
includedir="/cvmfs/dirac.egi.eu/dirac/v9.0.0a31/Linux-x86_64/include/globus"
datarootdir="${prefix}/share"
datadir="${datarootdir}"
libexecdir="/cvmfs/dirac.egi.eu/dirac/v9.0.0a31/Linux-x86_64/share/globus"
sysconfdir="${prefix}/etc"
sharedstatedir="${prefix}/com"
localstatedir="${prefix}/var"

PATH="${bindir}:${sbindir}:${libexecdir}:${PATH}"

PROGRAM_NAME="${0##*/}"
PROGRAM_VERSION='12.1'
PACKAGE='globus_gram_client_tools'
VERSION='12.1'
DIRT_TIMESTAMP='1629915172'
DIRT_BRANCH_ID='0'


case "$PROGRAM_NAME" in
*-run)
   short_usage="$PROGRAM_NAME <contact string> [-np N] <executable> [<arg>...]"
   std_file_statement="default to stdout/stdout of $PROGRAM_NAME."
   ;;
*-submit)
   short_usage="$PROGRAM_NAME [-help] <contact string> [-np N] <executable> [<arg>...]"
   std_file_statement="is retrieved later using globus-job-get-output."
   ;;
esac

long_usage ()
{
cat >&2 <<EOF

Usage: $PROGRAM_NAME
      [-help|-usage]                     print usage and exit
      [-version[s]]                      print version and exit
Usage: $PROGRAM_NAME
      [-dumprsl]                         output RSL, don't run job
      [-dryrun]                          verify RSL, don't run job
      [-verify]                          perform dryrun, then run job
      [-file             file]           read rest of arguments from file
      contact string                     only the hostname is required.
        [-np             N]              number of processing elements
        [-count          N]              same as -np
        [-host-count     nodes]          number of SMP nodes (IBM SP)
        [-m[axtime]      minutes]        time to allocate for the job
        [-p[roject]      projectID]      scheduling/accounting project ID
        [-q[ueue]        queueID]        scheduling/accounting queue ID
        [-d[ir]          directory]      working directory
        [-env            name=value]...  environment binding
        [-stdin  [-l|-s] file]           input
        [-stdout [-l|-s] file]           output
        [-stderr [-l|-s] file]           error output
        [-x rsl-clause]                  RSL extension capability
        [-l|-s] executable [args...]     executable and clause arguments

   Job stdin defaults to /dev/null.
   Job stdout/stderr $std_file_statement

   The file modifiers -l and -s specify different filespaces:
      -l[ocal]  file is relative to working directory of job      (DEFAULT)
      -s[tage]  file relative to job request is staged to job host

   The working directory of the submitted job defaults to \$HOME.

   Valid forms of the contact string are:
       hostname
       hostname:port
       hostname:port/service
       hostname/service
       hostname:/service
       hostname::subject
       hostname:port:subject
       hostname/service:subject
       hostname:/service:subject
       hostname:port/service:subject

   If more than one setting is defined (such as two -stdin definitions),
   the setting defined last on the command line will be used.

EOF
}

rslfile=${TMPDIR:-/tmp}/globus_job_run.`whoami`.rsl.$$
tmpfile=${TMPDIR:-/tmp}/globus_job_run.`whoami`.tmp.$$


remove_files ()
{
    rm -f ${rslfile} ${tmpfile}
}


error_exit ()
{
    globus_args_short_usage
    exit $1
}


stringify ()
{
    #
    # NOTE: if the argument contains the pattern $(...), it is most likely
    # an RSL substitution: if so, suppress quoting.
    #
    arg="`cat -`"

    if [ "X`printf "%s" "${arg}" | sed -n '/\$(.*)/p'`" = "X" ]; then
        printf "%s" "${arg}" | sed -e 's/"/""/g' -e 's/^.*$/"&"/g'
    else
        printf "%s" "${arg}"
    fi
}


parse_integer ()
{
    if expr "$2" + 1 - 1 \> 0 > /dev/null 2>&1 ; then
	result="$2"
    else
	globus_args_option_error "$1" "\"$2\" is not an integer"
    fi
}


get_real_pwd ()
{
    file="$1"
    #
    # Try to figure out PWD (the real PWD, not an automounted PWD or an
    # incorrect env.var...)
    # This code is at large based on code from the MPICH mpirun command,
    # where they try to solve the same problem.
    #
    PWDtest="`pwd | sed 's@/tmp_mnt/@/@g'`"
    if [ ! -d "$PWDtest" ] ; then
        PWDtest="`pwd`"
    fi
    if [ -n "$PWD" ] ; then
        PWDtest2=`echo $PWD | sed 's@/tmp_mnt/@/@g'`
	if [ ! -r "${PWDtest2}/file" ]; then
            PWD=$PWDtest2
	else
            PWD=$PWDtest
        fi
    else
        PWD=$PWDtest
    fi
    echo "$PWD/$file"
}


normalize_filename()
{
   case "$1" in
     /* )
        file="$1"
        ;;
     * )
        file=`get_real_pwd "$1"`
        ;;
   esac

   # collapse all sequences of '/'s to a single '/'
   #
   file=`echo $file | sed -e 's://*:/:g'`

   # collapse all instances of "/./" to a single '/'
   # note we must loop for all series forms "/././" because
   # the patterns overlap and only half match in each iteration
   #
   while echo $file | grep '/\./' > /dev/null 2>&1
   do
      # this goes faster when we inline a couple iterations
      # into one sed command
      file=`echo $file | sed -e 's:/\./:/:g' \
                             -e 's:/\./:/:g'`
   done

   # reduce all '/../' expressions
   #    root is its own parent
   #    foo/bar/../ is foo iff bar!=..
   while echo $file | fgrep '/\.\./' > /dev/null 2>&1
   do
      file=`echo $file | sed -e 's:^/\.\./:/:g' \
                             -e 's:/[^./][^/]*/\.\./:/:g' \
                             -e 's:/\.[^./][^/]*/\.\./:/:g' \
                             -e 's:/\.\.[^/][^/]*/\.\./:/:g'`
   done

   # remove trailing '/.' or '/'
   #
   file=`echo $file | sed -e 's:/$::g' \
                          -e 's:\(/\.\)*$::g'`

   echo $file
}


read_argument_file()
{
    cat $1 |
    (
	arglist=
	while read inp ; do
	    arglist="${arglist} ${inp}"
	done
	echo "${arglist}"
    )
}

emit_subjob_rsl ()
{
    #
    # extra RSL should take precedence: therefore, it needs to come first
    #
    if [ ${job_is_multi} = "true" ]; then
	echo "( &${subjob_extra_rsl}(resourceManagerContact=\"${job_contact}\")"
	echo "   (subjobStartType=strict-barrier)"
	echo "   (label=\"subjob ${job_hostclause_count}\")"
	echo "   (executable=$executable)"
    else
        if [ -n "${subjob_extra_rsl}" ] ; then
           echo " &${subjob_extra_rsl}"
           echo "   (executable=$executable)"
        else
           echo "  &(executable=$executable)"
        fi
    fi
    if [ ! "X$project" = "X" ] ; then
	echo "   (project=$project)"
    fi
    if [ ! "X$queue" = "X" ] ; then
	echo "   (queue=$queue)"
    fi
    if [ ! "X$maxtime" = "X" ] ; then
	echo "   (maxtime=$maxtime)"
    fi
    if [ ! "X$directory" = "X" ] ; then
	echo "   (directory=$directory)"
    fi
    if [ ! "X$job_environment" = "X" ] || \
       [ ! "X$subjob_environment" = "X" ] ; then
	echo "   (environment=${job_environment}${subjob_environment})"
    fi
    if [ ! "X$count" = "X" ] ; then
	echo "   (count=$count)"
    fi
    if [ ! "X$host_count" = "X" ] ; then
	echo "   (host_count=$host_count)"
    fi
    if [ ! "X$subjob_args" = "X" ] ; then
	echo "   (arguments=$subjob_args)"
    elif [ ! "X$arguments" = "X" ] ; then
	echo "   (arguments=$arguments)"
    fi
    if [ ! "X$subjob_stdin" = "X" ] ; then
	echo "   (stdin=$subjob_stdin)"
    elif [ ! "X$job_stdin" = "X" ] ; then
	echo "   (stdin=$job_stdin)"
    fi
    if [ ! "X$subjob_stdout" = "X" ] ; then
	echo "   (stdout=$subjob_stdout)"
    elif [ ! "X$job_stdout" = "X" ] ; then
	echo "   (stdout=$job_stdout)"
    fi
    if [ ! "X$subjob_stderr" = "X" ] ; then
	echo "   (stderr=$subjob_stderr)"
    elif [ ! "X$job_stderr" = "X" ] ; then
	echo "   (stderr=$job_stderr)"
    fi
    if [ ${job_is_multi} = "true" ]; then
	echo ")"
    fi
}


parse_fileopt ()
{
    #
    # given input: <option> [-l[ocal]|-s[tage]] filename ...
    #
    # where <option> is one of -std{in,out,err}, -file, executable
    # if -l or -s is not defined, -l is assumed.
    #
    # The following settings are triggered:
    #
    # PROGRAM_NAME    option             flag   action
    # -----------------------------------------------------------
    # run         -stdin,executable  -s     enable GASS read
    # run         -stdout,-stderr    -s     enable GASS write
    # submit      -stdout,-stderr    all    disable GASS caching
    #

    opt="$1"
    if [ $# -eq 1 ]; then
	globus_args_option_error "${opt}" "needs a filename argument"
    fi

    stagevar=dummy
    dont_stringify=0
    case "${PROGRAM_NAME}:${opt}" in
	*-run:-stdin | *-run:executable)
	    stagevar=job_needs_gass_read
	    ;;
	*-run:-stdout | *-run:-stderr)
	    stagevar=job_needs_gass_write
	    ;;
	*-submit:-stdout)
	    job_cache_stdout=false
	    ;;
	*-submit:-stderr)
	    job_cache_stderr=false
	    ;;
        *:-file)
            dont_stringify=1
            ;;
    esac

    shift

    case "$1" in
    -s | -stage)
	if [ $# -eq 1 ]; then
	    globus_args_option_error "${opt} $1" "needs a filename argument"
	fi
	file=`normalize_filename "$2" | stringify`
	result=`echo '$(GLOBUSRUN_GASS_URL)' '#' "${file}"`
	eval ${stagevar}=true
	stage_executable=true
	return 3
	;;
    -l | -local)
	if [ $# -eq 1 ]; then
	    globus_args_option_error "${opt} $1" "needs a filename argument"
	    return 0
	fi
	result=`echo "$2" | stringify`
	return 3
	;;
    -*)
	globus_args_option_error "${opt}" "needs a filename argument"
	return 0
	;;
    *)
        if test "$dont_stringify" = 1; then
            result=$1
        else
            result=`echo "$1" | stringify`
        fi
	return 2
	;;
    esac
}


parse_environment ()
{
    shift 1
    if [ ! "$#" = "0" ] ; then
	case "$1" in
	?*=*)
	    name=`echo "$1" | sed -e 's/^\([^=]*\)=\(.*\)/\1/g'`
	    value=`echo "$1" | sed -e 's/^\([^=]*\)=\(.*\)/\2/g'`
	    echo "("`echo "$name" | stringify` `echo "$value" | stringify`")"
	    return 2
	    ;;
	*)
	    globus_args_option_error "-env" "invalid environment string \"$1\""
	    ;;
	esac
    else
	globus_args_option_error "-env" "missing environment string!"
    fi
}

parse_user_args ()
{
    shift_count=0
    temp_arguments=""
    while expr "$#" \> 0 > /dev/null 2> /dev/null ; do
        arg=`printf "%s" "$1" | stringify`
        temp_arguments="${temp_arguments} ${arg}"
        shift
        shift_count=`expr "$shift_count" + 1 2> /dev/null`
    done

    echo "$temp_arguments"
    return $shift_count
}

parse_hostclause ()
{
    count=""
    host_count=""
    maxtime=""
    executable=""
    directory=""
    project=""
    queue=""
    subjob_environment=""
    subjob_stdin=""
    subjob_stdout=""
    subjob_stderr=""
    subjob_args=""
    clause_done="false"

    if [ $# -eq 0 ]; then
	echo "ERROR: empty host-clause" >& 2
	return 0
    fi

    # "$1" should be the contact string
    #
    case "$1" in
	-*)
	    globus_args_option_error "$1" \
			       "contact string must be first in host-clause"
	    ;;
	*)
	    job_contact="$1"
	    ;;
    esac

    shift 1
    shift_count=1

    while [ $# -gt 0 -a "${clause_done}" = "false" ]; do
	case "$1" in
	-np | -count | \
	-host-count | \
	-m  | -maxtime | \
	-p  | -project | \
	-q  | -queue | \
	-d  | -dir | \
	-x  | -extrarsl)
	    if [ $# -lt 2 ]; then
		globus_args_option_error "$1" "missing argument"
	    fi
	    case "$1" in
		-np | -count)
		    parse_integer "$@"
		    count=${result}
		    ;;
		-host-count)
		    parse_integer "$@"
		    host_count=${result}
		    ;;
		-m | -maxtime)
		    parse_integer "$@"
		    maxtime=${result}
		    if [ ${maxtime} -gt ${job_maxtime} ]; then
			job_maxtime=${maxtime}
		    fi
		    ;;
		-p | -project)
		    project=`echo "$2" | stringify`
		    ;;
		-q | -queue)
		    queue=`echo "$2" | stringify`
		    ;;
		-d | -dir)
		    directory=`echo "$2" | stringify`
		    ;;
		-x | -extrarsl)
		    case "$2" in
		    \&*)
			subjob_extra_rsl=`echo "$2" | \
				    sed 's/^&//'`
			;;
		    *)
			subjob_extra_rsl="$2"
			;;
		    esac
		    globusrun -p "&${subjob_extra_rsl}" \
			1>/dev/null 2>/dev/null
		    if [ $? -ne 0 ]; then
			globus_args_option_error "$1" "cannot parse RSL stub"
		    fi
		    ;;
	    esac
	    shift 2
	    shift_count=`expr $shift_count + 2`
	    ;;
	-stdin | -stdout | -stderr)
	    parse_fileopt "$@"
	    stdio_shift=$?
	    if [ ${stdio_shift} -eq 0 ]; then
		return 0
	    fi
	    case "$1" in
		-stdin)
		    subjob_stdin=${result}
		    ;;
		-stdout)
		    subjob_stdout=${result}
		    ;;
		-stderr)
		    subjob_stderr=${result}
		    ;;
	    esac
	    shift $stdio_shift
	    shift_count=`expr $shift_count + $stdio_shift`
	    ;;
	-env)
	    subjob_environment=${subjob_environment}\ `parse_environment "$@"`
	    env_shift=$?
	    if [ ${env_shift} -eq 0 ] ; then
		return 0
	    else
		shift $env_shift
		shift_count=`expr $shift_count + $env_shift`
	    fi
	    ;;
	*)
	    case "$1" in
		-l* | -s*)
		    dummy=xyz
		    ;;
		-*)
		    globus_args_unrecognized_option "$1"
		    ;;
	    esac
	    #
	    # this should be  [-l|-s] executable [args...]
	    # where [args...] is terminated by EOL
	    #
	    # fileopt() needs a "executable" option first though.
	    #
	    parse_fileopt "executable" "$@"
	    exec_shift=$?
	    if [ ${exec_shift} -eq 0 ]; then
		return 0
	    fi
	    executable=${result}
	    #
	    # added a phantom "executable" argument above: adjust for that
	    exec_shift=`expr $exec_shift - 1`
	    shift $exec_shift
	    shift_count=`expr $shift_count + $exec_shift`

	    # and now the arguments...
	    #
	    while [ ! "$#" = "0" ]; do
                if [ "$1" = "--" ]; then
                    shift_count=`expr $shift_count + 1`
                    if [ $# -ge 2 -a "$2" = "--" ]; then
                        shift
                    else
                        shift
                        continue
                    fi
		fi
		subjob_args="$subjob_args `printf "%s" "$1" | stringify`"
		shift_count=`expr $shift_count + 1`
		shift
	    done
	    clause_done=true
	    ;;
	esac
    done

    if [ -z "${executable}" ]; then
	echo "ERROR: no executable in host-clause" >&2
	return 0
    fi

    emit_subjob_rsl
    return $shift_count
}

globus_args_short_usage()
{
    cat 1>&2 <<EOF

Syntax : ${short_usage}

Use -help to display full usage.

EOF
}

globus_args_option_error()
{
    cat 1>&2 <<EOF

ERROR: option $1 : $2
EOF
    globus_args_short_usage
    exit 1
}

globus_args_unrecognized_option()
{
    globus_args_option_error $1 "unrecognized option"
    exit 1
}	


parse_global_opts ()
{
    while [ ! "$#" = "0" ] ; do
	case "$1" in
        -help | -h | --help | -usage | --usage)
            long_usage
            exit 0
            ;;
        -version|--version)
            if [ "X${PROGRAM_NAME}" != "X" -a \
                  "X${PROGRAM_VERSION}" != "X" ]; then
                echo "${PROGRAM_NAME}: ${PROGRAM_VERSION}"
            elif [ "X${PACKAGE}" != "X" -a \
                   "X${VERSION}" != "X" ]; then
                echo "${PACKAGE}: ${VERSION}"
            else
                echo "No version information available."
            fi
            exit 0
            ;;
        -versions|--versions)
            AT=@
            if [ -n "${PACKAGE}" -a -n "${VERSION}" -a \
                 -n "${DIRT_TIMESTAMP}" -a -n "${DIRT_BRANCH_ID}" -a \
                 "X${DIRT_TIMESTAMP}" != "X${AT}DIRT_TIMESTAMP${AT}" -a \
                 "X${DIRT_BRANCH_ID}" != "X${AT}DIRT_BRANCH_ID${AT}" ];
            then
                echo "${PACKAGE}: ${VERSION} (${DIRT_TIMESTAMP}-${DIRT_BRANCH_ID})"
            else
                echo "No DiRT information available."
            fi
            exit 0;
            ;;
	-dump | -dumprsl | --dump | --dumprsl)
	    shift
	    job_print_rsl=true
	    job_submit=false
	    ;;
	-dryrun | --dryrun)
	    shift
	    job_verify=true
	    job_submit=false
	    ;;
	-verify | --verify)
	    shift
	    job_verify=true
	    job_submit=true
	    ;;
	-- )
	    shift 1
	    arguments=`parse_user_args "$@"`
	    shift $?
	    ;;
	*)
	    single_flag=`expr ${single_flag} + 1`
	    if [ ${job_is_multi} = true -o ${single_flag} -gt 1 ]; then
		globus_args_option_error "$1" \
				      "Cannot mix single and multi-req syntax"
	    fi
	    parse_hostclause "$@"
	    shift_count=$?
	    if [ ${shift_count} -eq 0 ]; then
		return 1
	    fi
	    shift $shift_count
	    job_hostclause_count=`expr $job_hostclause_count + 1`
	    ;;
	esac
    done

    if [ "$job_hostclause_count" = 0 ] ; then
	echo "ERROR: Every job requires at least one host-clause!" >&2
	return 1
    fi

}


parse_top ()
{
    # Default global settings
    #
    mds_search_options=""
    job_stdin=""
    job_stdout=""
    job_stderr=""
    job_environment=""
    job_contact=""
    job_maxtime=120             # two hours
    job_is_multi=false

    job_print_rsl=false
    job_verify=false
    job_submit=true

    job_needs_gass_read=false
    job_needs_gass_write=false
    job_needs_gass_output=true

    stage_executable=false
    job_cache_stdout=false
    job_cache_stderr=false
    job_gass_cache_tag="x-gass-cache://\$(GLOBUS_GRAM_JOB_CONTACT)"

    # there are some differences in default settings for job-run and
    # job-submit....
    #
    case "${PROGRAM_NAME}" in
	*-submit)
	    job_needs_gass_output=false
	    job_cache_stdout=true
	    job_cache_stderr=true
	    job_stdout="${job_gass_cache_tag}stdout anExtraTag"
	    job_stderr="${job_gass_cache_tag}stderr anExtraTag"
	    ;;
    esac

    single_flag=0
    job_hostclause_count=0

    parse_global_opts "$@"
}


#################################################
## validation pass gathers job argument list

trap remove_files 0 1 2 9 13 15

if [ $# -eq 0 ]; then
    globus_args_short_usage
    exit 1
fi

for file in ${rslfile} ${tmpfile} ; do
    rm -f ${file}
    touch ${file}
    if [ $? -ne 0 ] ; then
	echo "ERROR: Could not create temporary file \"${file}\"!" >&2
	error_exit 1
    fi
done


if parse_top "$@" > ${rslfile}
then

    if [ "${job_print_rsl}" = "true" ]; then
	cat ${rslfile}
    fi

    gassflag=""
    batchflag="-fast-batch"
    if [ ${job_needs_gass_output} = true ]; then
	gassflag="-o"
	batchflag="-b"
    fi
    if [ ${job_needs_gass_read}   = true ] || \
       [ ${stage_executable}      = true ] ; then
	gassflag="-s"
	batchflag="-b"
    fi
    if [ ${job_needs_gass_write}  = true ]; then
	gassflag="-w"
	batchflag="-b"
    fi

    case "${PROGRAM_NAME}:${job_is_multi}" in
	*submit:true)
	    echo "this statement should never have been executed" >&2
	    error_exit 1
	    ;;
	*submit:false)
	    globusrun_args="${gassflag} ${batchflag} -r \"${job_contact}\" -f ${rslfile}"
	    ;;
	*run:true)
	    globusrun_args="${gassflag} -f ${rslfile}"
	    ;;
	*run:false)
	    globusrun_args="${gassflag} -r \"${job_contact}\" -f ${rslfile}"
	    ;;
    esac

    # Check if proxy exists and is valid long enough.
    #
    if [ ${job_verify} = true -o ${job_submit} = true ]; then
	grid-proxy-info -exists
	if [ $? -ne 0 ]; then
	    echo "ERROR: proxy does not exist" >&2
	    error_exit 1
	fi
	hours=`expr ${job_maxtime} / 60 + 1`
	grid-proxy-info -exists -valid "${hours}:0"
	if [ $? -ne 0 ]; then
	    echo "ERROR: proxy is not valid long enough " \
		 "(${hours} hours)" >&2
	    error_exit 1
	fi
    fi

    # Dryrun to see if things will be OK.
    #
    if [ "${job_verify}" = "true" ]; then
	eval globusrun "-d ${globusrun_args}"  1>${tmpfile} 2>&1
	if [ $? -ne 0 ]; then
	    echo "ERROR: verify failed:" >&2
	    cat ${tmpfile} >&2
	    error_exit 1
	else
	    echo "Dryrun successful" >&2
	fi
	if [ ${job_cache_stdout} = true -o ${job_cache_stderr} = true ]; then
	    jobid=`grep x-nexus ${tmpfile}`
	    basecmd="\$bindir/globus-gass-cache -cleanup-url x-gass-cache://"
	    command=
	    if [ ${job_cache_stdout} = true ]; then
		command="${basecmd}${jobid}stdout"
	    fi
	    if [ ${job_cache_stderr} = true ]; then
		command="${command} ; ${basecmd}${jobid}stderr"
	    fi
	    if [ -n "${command}" ]; then
		echo globusrun -q -b -r "${job_contact}" \
                   "&(executable=\${bindir}//globus-sh-exec) \
		     (arguments=-e \"${command}\")"
	    fi
	fi
    fi

    # Just do it (tm).
    #
    if [ "${job_submit}" = "true" ]; then
	eval globusrun "-q ${globusrun_args}"
	return_status=$?
	exit $return_status
    fi

    exit 0
else
    error_exit 1
fi