#! /bin/sh
EXECER=$EXECER_DIR/execer
MEIKO_RUN=prun
SP1_RUN=/usr/lpp/euih/eui/cotb0
jobid=$$
progname=
fake_progname=
np=1
arch=
default_arch="$DEFAULT_ARCH"
machine=$DEFAULT_MACHINE
default_device=$DEFAULT_DEVICE
execer_machine=1
cmdLineArgs=""
cmdLineArgsExecer=
cmdline=
CAC=
#use_execer=0
mpirun_verbose=0
nolocal=0
#leavePGFile=0
#p4pgfile=
globuspgfile=
globusdbfile=
globusargs=
#p4ssport=
just_testing=0
machineFile=
debugger=""
rshcmd="$RSHCOMMAND"
mvhome=0
mvback=
narch=0
stdinfile=
stdoutfile=
stderrfile=
batch=
#p4wd=""
easy_jid=
keepsendQ=
Show=eval
Machines="paragon p4 sp1 ibmspx anlspx sgi_mp ipsc860 inteldelta cray_t3d execer smp symm_ptx"
# Usage limits
max_time=15
max_mem=32
#
# Setup device's default variables
if [ -x $MPIRUN_HOME/mpirun.$default_device.args ] ; then
    arg="--INIT"
    . $MPIRUN_HOME/mpirun.$default_device.args
fi

#
# polling_mode is for systems that can select between polling and 
# interrupt-driven operation.  Currently, only IBM POE is so supported
# (TMC CMMD has some support for this choice of mode, but we do not use it.)
polling_mode=1
#
PrintHelp() {
#
# If you change this, make the corresponding changes in README so that
# the man pages are updated.
#
if test "$0" = "PrintHelp" ; then
    mpirun_arg=$MPIRUN_HOME/mpirun
else
    mpirun_arg=$0
fi
#
cat <<EOF
mpirun [mpirun_options...] <progname> [options...]

  mpirun_options:
    -arch <architecture>
            specify the architecture (must have matching machines.<arch>
            file in ${MPIR_HOME}/bin/machines) if using the execer
    -h      This help
    -machine <machine name>
            use startup procedure for <machine name>
            Currently supported:
EOF
for machine in $Machines ; do
  echo "              $machine"
done
cat <<EOF

    -machinefile <machine-file name>
            Take the list of possible machines to run on from the
            file <machine-file name>.  This is a list of all available
            machines; use -np <np> to request a specific number of machines.
    -np <np>
            specify the number of processors to run on
    -nodes <nodes>
            specify the number of nodes to run on (for SMP systems, 
	    currently only ch_mpl device supports this)
    -nolocal
            don't run on the local machine (only works for 
            p4 and ch_p4 jobs)
    -stdin filename
            Use filename as the standard input for the program.  This
            is needed for programs that must be run as batch jobs, such
            as some IBM SP systems and Intel Paragons using NQS (see 
            -paragontype below).
	    use 
                  -stdin /dev/null 
            if there is no input and you intend to run the program in the
            background.  An alternate is to redirect standard input from
            /dev/null, as in 
                  mpirun -np 4 a.out < /dev/null
    -t      Testing - do not actually run, just print what would be
            executed
    -v      Verbose - throw in some comments
    -dbg    The option '-dbg' may be used to select a debugger.  For example,
            -dbg=gdb invokes the mpirun_dbg.gdb script located in the 
	    'mpich/bin' directory.  This script captures the correct arguments,
            invokes the gdb debugger, and starts the first process under gdb 
            where possible.  There are 4 debugger scripts; gdb, xxgdb, ddd, 
	    totalview.  These may need to be edited depending on your system.
            There is another debugger script for dbx, but this one will always
            need to be edited as the debugger commands for dbx varies between 
            versions.  You can also use this option to call another debugger; 
            for example, -dbg=mydebug.  All you need to do is write a script 
            file, 'mpirun_dbg.mydebug', which follows the format of the 
            included debugger scripts, and place it in the mpich/bin directory.
    -ksq    Keep the send queue. This is useful if you expect later
            to attach totalview to the running (or deadlocked) job, and
	    want to see the send queues. (Normally they are not maintained 
            in a way which is visible to the debugger).

      Special Options for NEC - CENJU-3:

    -batch  Excecute program as a batch job (using cjbr)

    -stdout filename
            Use filename as the standard output for the program.
    -stderr filename
            Use filename as the standard error  for the program.
    -jid    Jobid from Job-Scheduler EASY.
            If this option is specified, mpirun directly executes
            the parallel program using this jobid.
            Otherwise, mpirun requests np nodes from the Job-Scheduler
                       in interactive or batch mode.
                       In interactive mode (i.e. option -batch is not
                       specified), mpirun waits until the processors are
                       allocated, executes the parallel program and
                       releases the processors.
	          
	          
    Special Options for Batch Environments:

    -mvhome Move the executable to the home directory.  This 
            is needed when all file systems are not cross-mounted
            Currently only used by anlspx
    -mvback files
            Move the indicated files back to the current directory.
            Needed only when using -mvhome; has no effect otherwise.
    -maxtime min
            Maximum job run time in minutes.  Currently used only
            by anlspx.  Default value is $max_time minutes.
    -nopoll Do not use a polling-mode communication.
            Available only on IBM SPx.
    -mem value
	   This is the per node memory request (in Mbytes).  Needed for some
           CM-5s. ( Default $max_mem. )
    -cpu time
	   This is the the hard cpu limit used for some CM-5s in
	   minutes. (Default $max_time minutes.)

    Special Options for IBM SP2:

    -cac name
            CAC for ANL scheduler.  Currently used only by anlspx.
            If not provided will choose some valid CAC.

On exit, mpirun returns a status of zero unless mpirun detected a problem, in
which case it returns a non-zero status (currently, all are one, but this 
may change in the future).

Multiple architectures may be handled by giving multiple -arch and -np 
arguments.  For example, to run a program on 2 sun4s and 3 rs6000s, with 
the local machine being a sun4, use

    $mpirun_arg -arch sun4 -np 2 -arch rs6000 -np 3 program

This assumes that program will run on both architectures.  If different
executables are needed, the string '%a' will be replaced with the arch name.
For example, if the programs are program.sun4 and program.rs6000, then the
command is

    $mpirun_arg -arch sun4 -np 2 -arch rs6000 -np 3 program.%a

If instead the executables are in different directories; for example, 
/tmp/me/sun4 and /tmp/me/rs6000, then the command is

    $mpirun_arg -arch sun4 -np 2 -arch rs6000 -np 3 /tmp/me/%a/program

It is important to specify the architecture with -arch BEFORE specifying
the number of processors.  Also, the FIRST -arch command must refer to the
processor on which the job will be started.  Specifically, if -nolocal is 
NOT specified, then the first -arch must refer to the processor from which
mpirun is running.

EOF

if [ -x $MPIRUN_HOME/mpirun.$default_device.args ] ; then
    # Here is the easiest way to set the arg list to -usage
    set "--usage"
    . $MPIRUN_HOME/mpirun.$default_device.args
fi
}

#    -xdbx   Start the first process under xdbx where possible
while [ 1 -le $# ] ; do
  arg=$1
  #echo $arg
  #echo $#
  shift
  case $arg in 
     -arch) 
     	arch=$1
	narch=`expr $narch + 1`
	eval arch$narch=$1
	shift
	;;
    -np)
	np="$1"
        nodigits=`echo $np | sed 's/^[0-9]*$//'`
        if [ "$nodigits" != "" -o $np -lt 1 ] ; then
           echo np: $np is an invalid number of processors.  Exiting.
           exit 1
	fi
	eval np$narch=$1
	shift
	;;
    -nodes)
	nodes="$1"
        nodigits=`echo $np | sed 's/^[0-9]*$//'`
        if [ "$nodigits" != "" -o $np -lt 1 ] ; then
           echo nodes: $nodes is an invalid number of nodes.  Exiting.
           exit 1
	fi
	eval nodes$narch=$1
	shift
	;;
    -machine)
	machine="$1"
	shift
	;;
    -machinefile)
        machineFile="$1"
	eval machineFile$narch="$1"
	shift
	;;
    -randommachines)
        MPIRUN_RANDOM="yes"
	export MPIRUN_RANDOM
        ;;
    -device)
 	default_device="$1"
	shift
	;;
    -stdin)
	# Name of a file to use for standard input.
	stdinfile="$1"
	shift
	;;
    -stdout)
        # Name of a file to use for standard output.
        stdoutfile="$1"
        shift
        ;;
    -stderr)
        # Name of a file to use for standard error.
        stderrfile="$1"
        shift
        ;;
    -nolocal)
        nolocal=1
	;;	
    -h)
	if [ "$hasprinthelp" = 1 ] ; then
	   PrintHelp
        fi
      	exit 1
	;;	
#    -e)
#      	use_execer=1
#	;;
#    -pg)
#      	use_execer=0
#      	;;
#    -leave_pg|-keep_pg)
#      	leavePGFile=1
#	;;
    -batch)
        batch=1
        ;;
    -jid)
        easy_jid="$1"
        shift
        ;;
#    -p4pg)
#      	if [ $np -gt  1 ] ; then
#	    echo "-p4pg and -np are mutually exclusive; -np $np being ignored."
#            np=1
#	fi
#	p4pgfile="$1"
#	shift
#      	leavePGFile=1
#	;;
#    -p4wd)
#	p4workdir="$1"
#	shift
#	;;
#    -p4ssport)
#      	p4ssport="$1"
#	shift
#        if [ $p4ssport = "0" -a "$MPI_P4SSPORT" != "" ] ; then
#	    p4ssport=$MPI_P4SSPORT
#      	fi
#	;;
    -v)
       	mpirun_verbose=1
	;;
    -t|-show)
      	just_testing=1
	Show=echo
	;;
#   This option is deprecated
    -tv|-totalview)
        debugger="totalview"
	;;
    -ksq)
        cmdLineArgs="$cmdLineArgs -mpichksq"
        ;;
    -dbg=* | --dbg=*)
        debugger=`echo $arg|sed 's/-*dbg=//'`
	;;
#   This option is deprecated
    -dbx)
      	debugger="dbx"
      	;;
#   This option is deprecated
    -gdb)
      	debugger="gdb"
	;;
#   This option is deprecated
    -xxgdb)
      	debugger="xxgdb"
	;;
#   This option is deprecated
    -ddd)
	debugger="ddd"
	;;
#   This option is deprecated
    -pedb)
	# For IBM SP
	debugger="pedb"
	commandfile=""
	;;
    -nopoll)
	polling_mode=0
	;;
    -maxtime|-cpu)
        max_time="$1"
	shift
	;;
    -mem)
	mem="$1"
	if [ $mem -gt $max_mem ];then
echo "mem: Memory requested ($mem MB/node) greater than maximum available ($max_mem MB/node).  Exiting."
	    exit 1
	fi
	shift
	;;
    -mvhome)
        mvhome=1
	;;
    -mvback)
	mvback="$1"
	shift
	;;
    -cac)
        CAC="$1"
	shift
	;;
    -echo)
	# this is for debugging
	set -x
	echo "default_arch   = $default_arch"
	echo "default_device = $default_device"
	echo "machine	     = $machine"
	;;
    -usage|-help|-\?)
      	# Accept these for help until the program name is provided.
      	if [ "$progname" = "" ] ; then
	   if [ "$hasprinthelp" = "1" ] ; then
	      PrintHelp
           fi
	   exit 1
      	fi
	;;
    *)
        # First see if the device knows about this argument:
        if [ -x $MPIRUN_HOME/mpirun.$default_device.args ] ; then
            device_knows_arg=0
	    . $MPIRUN_HOME/mpirun.$default_device.args
            # If the device handled it, skip to the top of the while loop
            # on arguments.  ? Does this need to do a shift?  If so, 
	    # should the number of shifts be the value of the arg?
            if [ $device_knows_arg != 0 ] ; then continue ; fi
        fi
	# The first unrecognized argument is assumed to be the name of
      	# the program, but only if it is executable
	proginstance=`echo $arg | sed "s/%a/$arch/g"`
      	if [ "$progname" = "" -a "$fake_progname" = "" -a \
		! -x "$proginstance" ] ; then
	    fake_progname="$arg"
      	elif [ "$progname" = "" -a -x "$proginstance" ] ; then 
	    progname="$arg"
            # any following unrecognized arguments are assumed to be arguments
            # to be passed to the program, as well as all future args
	    # We can't use use_execer here to decide how to store the
	    # arguments, since we may later change it (see the switch on arch)
	    # Thus we store the args in two forms:
	    # cmdLineArgsExecer and cmdLineArgs.  Notes that we need
	    # to preserve strings in cmdLineArgs, so 
	    while [ 1 -le $# ] ; do
	        arg="$1"
	        case $arg in 
	           *\'*) 
                   cmdLineArgsExecer="$cmdLineArgsExecer -arg=\"$arg\""
                   cmdLineArgs="$cmdLineArgs \"$arg\""
		   ;;
	           *\"*)
                   cmdLineArgsExecer="$cmdLineArgsExecer -arg='$arg'"
                   cmdLineArgs="$cmdLineArgs '$arg'"
		   ;;
	           *)
                   cmdLineArgsExecer="$cmdLineArgsExecer -arg=\"$arg\""
                   cmdLineArgs="$cmdLineArgs \"$arg\""
		   ;;
	       esac
	       shift
            done
         else
	    echo "Warning: Command line arguments for program should be given"
            echo "after the program name.  Assuming that $arg is a"
            echo "command line argument for the program."
            case $arg in 
	        *\'*) 
                cmdLineArgsExecer="$cmdLineArgsExecer -arg=\"$arg\""
                cmdLineArgs="$cmdLineArgs \"$arg\""
	        ;;
	        *\"*)
                cmdLineArgsExecer="$cmdLineArgsExecer -arg='$arg'"
                cmdLineArgs="$cmdLineArgs '$arg'"
		;;
	        *)
                cmdLineArgsExecer="$cmdLineArgsExecer -arg=\"$arg\""
                cmdLineArgs="$cmdLineArgs \"$arg\""
		;;
	    esac
         fi
	 # note that the commandline args should get one more eval to 
	 # strip off any quotes
      ;;
  esac
done

# We need at least the program name

if [ "$globuspgfile" = "" -a "$progname" = "" ] ; then
  echo "Missing: program name"
  if [ "$fake_progname" != "" ] ; then
      echo "Program $fake_progname either does not exist, is not "
      echo "executable, or is an erroneous argument to mpirun."
  fi
  exit 1
fi
#
if [ -n "$fake_progname" -a -n "$progname" ] ; then
    # found an unrecognized argument, followed by a program
    echo "Unrecognized argument $fake_progname ignored."
    # Should this exit?
fi
# 
# Warning: Some systems (OSF 1 at least) have a command called dbx
# with different command-line arguments.  In particular, it needs
# -c here (and that does not implement the remove part of the -sr).
#
# Turns out that this doesn't matter so much, because the dbx is terribly,
# terribly crippled (on Alpha).  
#
larch=$arch
if [ -z "$larch" ] ; then larch=$default_arch ; fi
#
# Fill out relative program pathnames
#
# Get the current directory
# Note that PWD may NOT be `pwd` if an explicit "cd", outside of the csh
# shell, is used.  A downside of this is that PWD may have a more reasonable
# version of the path.  To try and fix this, we create a test file and see
# if it is accessible from PWD and pwd; if so, we take the PWD value
# 
# Why can't automounters work!???!!
#
# For those unfamiliar with the problem, the automounter creates
# paths like /tmp_mnt/.... .  But if you try to open a file with that path, 
# and the ACTUAL path has not been accessed before, the automounter fails
# to mount the partition.  In otherwords, the automounter is so mind-bogglingly
# stupid as to not recognize its OWN MOUNTS.  Network computing indeed.
#
# Note that PWD may ALSO be damaged, so we need to sed PWD as well...
#
# We should move this to the point in the code where it needs to know a
# particular filename, since this will fail if the directory is not
# writable by the user.
# 
if [ -n "$AUTOMOUNTFIX" ] ; then
    PWDtest=`pwd | $AUTOMOUNTFIX`
    if [ ! -d $PWDtest ] ; then
        PWDtest=`pwd`
    fi
    if [ -n "$PWD" ] ; then
	# Use a process-specific filename
	PWDtest2=`echo $PWD | $AUTOMOUNTFIX`
        /bin/rm -f $PWDtest/.mpirtmp$$ $PWDtest2/.mpirtmp$$
        if `eval "echo test > $PWDtest2/.mpirtmp$$" 2>/dev/null` ; then
            if [ ! -s $PWDtest/.mpirtmp$$ ] ; then
	        /bin/rm -f $PWDtest2/.mpirtmp$$
                PWD=$PWDtest
	    else
		PWD=$PWDtest2
	    fi
	else
	    # Current directory is not writable.  Hope for the best
            PWD=$PWDtest        
        fi
        /bin/rm -f $PWDtest/.mpirtmp$$ $PWDtest2/.mpirtmp$$
    else 
        PWD=$PWDtest
    fi
else
    PWD=`pwd`
fi
#
if [ -n "$PWD" ] ; then
    PWD_TRIAL=$PWD
else
    PWD_TRIAL=$PWDtest
fi
# The test for PWD_TRIAL usable is within the p4/execer test (since that
# is where it matters)
#
tail=`echo $progname | sed 's/\/.*//'` 
if  [ "$tail" = "" ] ; then
  #echo Absolute path, don't need to change anything
  true
else
  #echo Relative path
  progname="$PWD_TRIAL/$progname"
fi

#
# Get the architecture and the device
# Note that some architecture/device combinations have their own, private
# devices
#
if [ "$arch" = "" -a -x $bindir/tarch ] ; then
  # This would be ok for systems that had a unique architecture.
  # For IRIX, this isn't good enough
  arch=`$bindir/tarch`
  if [ "$arch" = "IRIX64" -a \( "$default_arch" = "IRIX" -o \
			       "$default_arch" = "IRIXN32" \) ] ; then
      arch=$default_arch			       
  fi
fi

archlist="$arch"
case $arch in 
    HP-UX) archlist="hpux hp9000s700 hp9000s735 HP-UX" 
	;;
    IRIX|sgi|SGI) archlist="IRIX sgi"
	;;
    i386|symm_ptx) archlist="i386 symm_ptx"
	;;
    IP19|IP21) archlist="IRIX IP19 IP21"
	arch="IRIX"
	# Was sgi_mp instead of IRIX
	;;
    ipsc860|i860|ipsc)
      	arch="ipsc860"
      	archlist="ipsc860 i860 ipsc"
	;;
    freebsd|386BSD)
	arch="386BSD"
	archlist="386BSD freebsd"
	;;
    hpux|hp9000s700)
	arch="hpux"
	archlist="hpux hp9000s700"
	;;
esac

#
# Try to find the machine
if [ "$machine" = "" ] ; then 
  machine=$arch
  case $arch in 
    sun4|solaris)
      # This device should only exits on sun4s that are actually
      # MEIKO machines.
      if [ -r /dev/elan ] ; then
	machine="meiko"
      elif [ "$default_device" = "ch_p4" -a $use_execer = 0 ] ; then
	machine="p4"
      elif [ "$default_device" = "globus" ] ; then
	machine="globus"
      else
        machine="execer"
      fi
      ;;
    
    rs6000)
      # This only works on the ANL sp system
      # -e for "exists" isn't available on all systems
      echo `hostname` | grep spnode > /dev/null
      if [ $? = 0 ] ; then
        # This is the correct version to use once we're on a node
	machine="ibmspx"
      elif [ "$default_device" = "globus" ] ; then
        # We need this before the anlspx check, because the Globus device
        # can be used on the ANL SP, as well, but the mpirun script for
        # it does not use Globus by default.
	machine="globus"
      elif [ -d /etc/FRAMES -a -d /mcs ] ; then
        machine="anlspx"
      elif [ "$default_device" = "ch_p4" -a $use_execer = 0 ] ; then
	machine="p4"
      elif [ "$default_device" = "ch_p4" -a $use_execer = 1 ] ; then
	machine="execer"
      elif [ -d /usr/lpp/euih ] ; then
        machine="sp1"
      elif [ -x /usr/bin/poe -o -x /usr/lpp/poe/bin/poe ] ; then
        # should work for other users
        machine="ibmspx"
      else
	 echo "Can't determine the type of the machine this is."
         echo "Specifiy it with -machine <machine_type>."
      fi
      ;;
    ipsc860|i860|ipsc)
        machine="ipsc860"
	;;
    symm_ptx|i386)
        machine="symm_ptx"
	;;
    *)
      if [ "$default_device" = "ch_p4" -a $use_execer = 0 ] ; then
	machine="p4"
      elif [ "$default_device" = "globus" ] ; then
	machine="globus"
      else
        machine="execer"
      fi
      #echo "Can't determine the type of the machine this is."
      #echo "it with -machine <machine_type>."
      ;;
  esac
fi

# Get value of host if necessary
if [ "$machine" = p4 -o "$machine" = execer -o \
	"$machine" = sgi_mp -o "$machine" = ch_p4 -o \
	"$machine" = ch_p4-2 -o "$machine" = globus -o \
        "$machine" = globus ] ; then
  if [ "$MPI_HOST" = "" ] ;  then
    MPI_HOST=""
    if [ "$arch" = "ipsc860" ] ; then
	MPI_HOST=`hostname`
    else
        # Which is not supported in many sh's.  head isn't on some as well.
#	HOSTNAMEPGM="`which hostname | head -1`"
#        if [ -x "$HOSTNAMEPGM" ] ; then MPI_HOST="`hostname`" ; fi
	MPI_HOST=`hostname`
    fi
    # Note that uname -n may not produce a usable hostname.  Any suggestions?
    if [ "$MPI_HOST" = "" ] ; then MPI_HOST="`uname -n`" ; fi
  fi
  #
  # We don't need this for most systems; for the moment, we'll suppress the
  # message about the automounter unless we're using ch_p4
  #
  if [ "$PWD_TRIAL" != "" ] ; then
    PWD_TRIAL=`pwd | sed -e 's%/tmp_mnt/%/%g'`
    if [ ! -d $PWD_TRIAL ] ; then 
        echo "Warning: your default path uses the automounter; this may"
        echo "cause some problems if you use other NFS-connected systems."
        PWD_TRIAL=`pwd`
    fi
  fi
fi


if [ $mpirun_verbose = 1 ] ; then
  cnt=1
  if [ $narch -gt 1 ] ; then
      while [ $cnt -le $narch ] ; do
        eval mp=\$"np$cnt"
        eval archm=\$"arch$cnt"
	proginstance=`echo $progname | sed "s/%a/$archm/"`
	echo "running $proginstance on $mp $archm $machine processors"
	cnt=`expr $cnt + 1`
      done
  else
      echo "running $progname on $np $arch $machine processors"
  fi
fi

