Blame src/pm/remshell/mpiexec.c

Packit Service c5cf8c
/* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil ; -*- */
Packit Service c5cf8c
/*
Packit Service c5cf8c
 *  (C) 2004 by Argonne National Laboratory.
Packit Service c5cf8c
 *      See COPYRIGHT in top-level directory.
Packit Service c5cf8c
 */
Packit Service c5cf8c
Packit Service c5cf8c
/* OWNER=gropp */
Packit Service c5cf8c
Packit Service c5cf8c
/* An example mpiexec program that uses a remote shell program to create
Packit Service c5cf8c
   new processes on the selected hosts.
Packit Service c5cf8c
Packit Service c5cf8c
   This code also shows how to use the pmutil routines (in ../util)
Packit Service c5cf8c
   to provide many of the services required by mpiexec
Packit Service c5cf8c
Packit Service c5cf8c
   Steps:
Packit Service c5cf8c
   1. Read and process that command line.  Build a ProcessList.  (A ProcessList
Packit Service c5cf8c
   may have one entry for a request to create n separate processes)
Packit Service c5cf8c
Packit Service c5cf8c
   2. Convert the ProcessList into a ProcessTable.  In the forker mpiexec,
Packit Service c5cf8c
   this simply expands the requested number of processes into an
Packit Service c5cf8c
   array with one entry per process.  These entries contain information
Packit Service c5cf8c
   on both the setup of the processes and the file descriptors used for
Packit Service c5cf8c
   stdin,out,err, and for the PMI calls.
Packit Service c5cf8c
Packit Service c5cf8c
   3. (Optionally) allow the forked processes to use a host:port to
Packit Service c5cf8c
   contact this program, rather than just sharing a pipe.  This allows the
Packit Service c5cf8c
   forker to start other programs, such as debuggers.
Packit Service c5cf8c
Packit Service c5cf8c
   4. Establish a signal handler for SIGCHLD.  This will allow us to
Packit Service c5cf8c
   get information about process termination; in particular, the exit
Packit Service c5cf8c
   status.
Packit Service c5cf8c
Packit Service c5cf8c
   5. Start the programs.
Packit Service c5cf8c
Packit Service c5cf8c
   6. Process input from the programs; send stdin given to this process
Packit Service c5cf8c
   to the selected processes (usually rank 0 or everyone).  Handle all
Packit Service c5cf8c
   PMI commands, including spawn.  Another "input" is the expiration of the
Packit Service c5cf8c
   specified timelimit for the run, if any.
Packit Service c5cf8c
Packit Service c5cf8c
   7. Process rundown commands and handle any abnormal termination.
Packit Service c5cf8c
Packit Service c5cf8c
   8. Wait for any processes to exit; gather the exit status and reason
Packit Service c5cf8c
   for exit (if abnormal, such as signaled with SEGV or BUS)
Packit Service c5cf8c
Packit Service c5cf8c
   9. Release all resources and compute the exit status for this program
Packit Service c5cf8c
   (using one of several approaches, such as taking the maximum of the
Packit Service c5cf8c
   exit statuses).
Packit Service c5cf8c
Packit Service c5cf8c
  Special Case to support Singleton Init:
Packit Service c5cf8c
  To support a singleton init of a process that then wants to
Packit Service c5cf8c
  create processes with MPI_Comm_spawn(_multiple), a special form of
Packit Service c5cf8c
  mpiexec is supported:
Packit Service c5cf8c
Packit Service c5cf8c
     mpiexec -pmi_args <port> <interfacename> <securitykey> <pid>
Packit Service c5cf8c
Packit Service c5cf8c
  The singleton process (in a routine in simple_pmi.c) forks a process and
Packit Service c5cf8c
  execs mpiexe with these arguments, where port is the port to which
Packit Service c5cf8c
  mpiexec should connect, interfacename is the name of the network interface, securitykey
Packit Service c5cf8c
  is a place-holder for a key used by the singleton init process to verify
Packit Service c5cf8c
  that the process connecting on the port is the one that was intended, and
Packit Service c5cf8c
  pid is the pid of the singleton init process.
Packit Service c5cf8c
Packit Service c5cf8c
  FIXME: The above has not been implemented yet.
Packit Service c5cf8c
*/
Packit Service c5cf8c
Packit Service c5cf8c
#include "mpichconf.h"
Packit Service c5cf8c
#include <stdio.h>
Packit Service c5cf8c
#include <string.h>
Packit Service c5cf8c
#ifdef HAVE_UNISTD_H
Packit Service c5cf8c
#include <unistd.h>
Packit Service c5cf8c
#endif
Packit Service c5cf8c
#include <stdlib.h>
Packit Service c5cf8c
Packit Service c5cf8c
#include "pmutil.h"
Packit Service c5cf8c
#include "process.h"
Packit Service c5cf8c
#include "cmnargs.h"
Packit Service c5cf8c
#include "pmiserv.h"
Packit Service c5cf8c
#include "ioloop.h"
Packit Service c5cf8c
#include "labelout.h"
Packit Service c5cf8c
#include "rm.h"
Packit Service c5cf8c
#include "simple_pmiutil.h"
Packit Service c5cf8c
#include "env.h"        /* MPIE_Putenv */
Packit Service c5cf8c
/* mpir_mem.h contains prototypes for MPL_strncpy etc. */
Packit Service c5cf8c
/* We no longer can use these because they are MPI device specific */
Packit Service c5cf8c
/* #include "mpir_mem.h" */
Packit Service c5cf8c
Packit Service c5cf8c
typedef struct {
Packit Service c5cf8c
    PMISetup pmiinfo;
Packit Service c5cf8c
    IOLabelSetup labelinfo;
Packit Service c5cf8c
} SetupInfo;
Packit Service c5cf8c
Packit Service c5cf8c
/* Forward declarations */
Packit Service c5cf8c
int mypreamble(void *, ProcessState *);
Packit Service c5cf8c
int mypostfork(void *, void *, ProcessState *);
Packit Service c5cf8c
int mypostamble(void *, void *, ProcessState *);
Packit Service c5cf8c
int myspawn(ProcessWorld *, void *);
Packit Service c5cf8c
Packit Service c5cf8c
static int AddEnvSetToCmdLine(const char *, const char *, const char **);
Packit Service c5cf8c
Packit Service c5cf8c
/* Set printFailure to 1 to get an explanation of the failure reason
Packit Service c5cf8c
   for each process when a process fails */
Packit Service c5cf8c
static int printFailure = 0;
Packit Service c5cf8c
Packit Service c5cf8c
#ifndef MAX_PORT_STRING
Packit Service c5cf8c
#define MAX_PORT_STRING 1024
Packit Service c5cf8c
#endif
Packit Service c5cf8c
Packit Service c5cf8c
/* Note that envp is common but not standard */
Packit Service c5cf8c
int main(int argc, char *argv[], char *envp[])
Packit Service c5cf8c
{
Packit Service c5cf8c
    int rc;
Packit Service c5cf8c
    int erc = 0;                /* Other (exceptional) return codes */
Packit Service c5cf8c
    int reason, signaled = 0;
Packit Service c5cf8c
    SetupInfo s;
Packit Service c5cf8c
    char portString[MAX_PORT_STRING];
Packit Service c5cf8c
Packit Service c5cf8c
    /* MPIE_ProcessInit initializes the global pUniv */
Packit Service c5cf8c
    MPIE_ProcessInit();
Packit Service c5cf8c
    /* Set a default for the universe size */
Packit Service c5cf8c
    pUniv.size = 64;
Packit Service c5cf8c
Packit Service c5cf8c
    /* Set defaults for any arguments that are options.  Also check the
Packit Service c5cf8c
     * environment for special options, such as debugging.  Set
Packit Service c5cf8c
     * some defaults in pUniv */
Packit Service c5cf8c
    MPIE_CheckEnv(&pUniv, 0, 0);
Packit Service c5cf8c
    IOLabelCheckEnv();
Packit Service c5cf8c
Packit Service c5cf8c
    /* Handle the command line arguments.  Use the routine from util/cmnargs.c
Packit Service c5cf8c
     * to fill in the universe */
Packit Service c5cf8c
    MPIE_Args(argc, argv, &pUniv, 0, 0);
Packit Service c5cf8c
    /* If there were any soft arguments, we need to handle them now */
Packit Service c5cf8c
    rc = MPIE_InitWorldWithSoft(&pUniv.worlds[0], pUniv.size);
Packit Service c5cf8c
    if (!rc) {
Packit Service c5cf8c
        MPL_error_printf("Unable to process soft arguments\n");
Packit Service c5cf8c
        exit(1);
Packit Service c5cf8c
    }
Packit Service c5cf8c
Packit Service c5cf8c
    if (pUniv.fromSingleton) {
Packit Service c5cf8c
        /* The MPI process is already running.  We create a simple entry
Packit Service c5cf8c
         * for a single process rather than creating the process */
Packit Service c5cf8c
        MPIE_SetupSingleton(&pUniv);
Packit Service c5cf8c
    }
Packit Service c5cf8c
Packit Service c5cf8c
Packit Service c5cf8c
    rc = MPIE_ChooseHosts(&pUniv.worlds[0], MPIE_ReadMachines, 0);
Packit Service c5cf8c
    if (rc) {
Packit Service c5cf8c
        MPL_error_printf("Unable to assign hosts to processes\n");
Packit Service c5cf8c
        exit(1);
Packit Service c5cf8c
    }
Packit Service c5cf8c
Packit Service c5cf8c
    if (MPIE_Debug)
Packit Service c5cf8c
        MPIE_PrintProcessUniverse(stdout, &pUniv);
Packit Service c5cf8c
Packit Service c5cf8c
    DBG_PRINTF(("timeout_seconds = %d\n", pUniv.timeout));
Packit Service c5cf8c
Packit Service c5cf8c
    /* Get the common port for creating PMI connections to the created
Packit Service c5cf8c
     * processes */
Packit Service c5cf8c
    rc = PMIServSetupPort(&pUniv, portString, sizeof(portString));
Packit Service c5cf8c
    if (rc) {
Packit Service c5cf8c
        MPL_error_printf("Unable to setup port for listener\n");
Packit Service c5cf8c
        exit(1);
Packit Service c5cf8c
    }
Packit Service c5cf8c
    s.pmiinfo.portName = portString;
Packit Service c5cf8c
Packit Service c5cf8c
#ifdef USE_MPI_STAGE_EXECUTABLES
Packit Service c5cf8c
    /* Hook for later use in staging executables */
Packit Service c5cf8c
    if (? stageExes) {
Packit Service c5cf8c
        rc = MPIE_StageExecutables(&pUniv.worlds[0]);
Packit Service c5cf8c
        if (!rc)
Packit Service c5cf8c
            ...;
Packit Service c5cf8c
    }
Packit Service c5cf8c
#endif
Packit Service c5cf8c
Packit Service c5cf8c
    PMIServInit(myspawn, &s);
Packit Service c5cf8c
    s.pmiinfo.pWorld = &pUniv.worlds[0];
Packit Service c5cf8c
    PMISetupNewGroup(pUniv.worlds[0].nProcess, 0);
Packit Service c5cf8c
    MPIE_ForwardCommonSignals();
Packit Service c5cf8c
    if (!pUniv.fromSingleton) {
Packit Service c5cf8c
        MPIE_ForkProcesses(&pUniv.worlds[0], envp, mypreamble, &s, mypostfork, 0, mypostamble, 0);
Packit Service c5cf8c
    } else {
Packit Service c5cf8c
        /* FIXME: The singleton code goes here */
Packit Service c5cf8c
        MPL_error_printf("Singleton init not supported\n");
Packit Service c5cf8c
        exit(1);
Packit Service c5cf8c
    }
Packit Service c5cf8c
    reason = MPIE_IOLoop(pUniv.timeout);
Packit Service c5cf8c
Packit Service c5cf8c
    if (reason == IOLOOP_TIMEOUT) {
Packit Service c5cf8c
        /* Exited due to timeout.  Generate an error message and
Packit Service c5cf8c
         * terminate the children */
Packit Service c5cf8c
        if (pUniv.timeout > 60) {
Packit Service c5cf8c
            MPL_error_printf("Timeout of %d minutes expired; job aborted\n", pUniv.timeout / 60);
Packit Service c5cf8c
        } else {
Packit Service c5cf8c
            MPL_error_printf("Timeout of %d seconds expired; job aborted\n", pUniv.timeout);
Packit Service c5cf8c
        }
Packit Service c5cf8c
        erc = 1;
Packit Service c5cf8c
        MPIE_KillUniverse(&pUniv);
Packit Service c5cf8c
    }
Packit Service c5cf8c
Packit Service c5cf8c
    /* Wait for all processes to exit and gather information on them.
Packit Service c5cf8c
     * We do this through the SIGCHLD handler. We also bound the length
Packit Service c5cf8c
     * of time that we wait to 2 seconds.
Packit Service c5cf8c
     */
Packit Service c5cf8c
    MPIE_WaitForProcesses(&pUniv, 2);
Packit Service c5cf8c
Packit Service c5cf8c
    /* Compute the return code (max for now) */
Packit Service c5cf8c
    rc = MPIE_ProcessGetExitStatus(&signaled);
Packit Service c5cf8c
Packit Service c5cf8c
    /* Optionally provide detailed information about failed processes */
Packit Service c5cf8c
    if ((rc && printFailure) || signaled)
Packit Service c5cf8c
        MPIE_PrintFailureReasons(stderr);
Packit Service c5cf8c
Packit Service c5cf8c
    /* If the processes exited normally (or were already gone) but we
Packit Service c5cf8c
     * had an exceptional exit, such as a timeout, use the erc value */
Packit Service c5cf8c
    if (!rc && erc)
Packit Service c5cf8c
        rc = erc;
Packit Service c5cf8c
Packit Service c5cf8c
    return (rc);
Packit Service c5cf8c
}
Packit Service c5cf8c
Packit Service c5cf8c
void mpiexec_usage(const char *msg)
Packit Service c5cf8c
{
Packit Service c5cf8c
    if (msg) {
Packit Service c5cf8c
        MPL_error_printf("%s", msg);
Packit Service c5cf8c
        if (msg[strlen(msg) - 1] != '\n') {
Packit Service c5cf8c
            MPL_error_printf("\n");
Packit Service c5cf8c
        }
Packit Service c5cf8c
    }
Packit Service c5cf8c
    MPL_usage_printf("Usage: mpiexec %s\n", MPIE_ArgDescription());
Packit Service c5cf8c
    exit(-1);
Packit Service c5cf8c
}
Packit Service c5cf8c
Packit Service c5cf8c
/* Redirect stdout and stderr to a handler */
Packit Service c5cf8c
int mypreamble(void *data, ProcessState * pState)
Packit Service c5cf8c
{
Packit Service c5cf8c
    SetupInfo *s = (SetupInfo *) data;
Packit Service c5cf8c
    int rc;
Packit Service c5cf8c
Packit Service c5cf8c
    IOLabelSetupFDs(&s->labelinfo);
Packit Service c5cf8c
    rc = PMISetupSockets(1, &s->pmiinfo);
Packit Service c5cf8c
    /* We must use communication over the socket, rather than the
Packit Service c5cf8c
     * environment, to pass initialization data */
Packit Service c5cf8c
    pState->initWithEnv = 0;
Packit Service c5cf8c
Packit Service c5cf8c
    return rc;
Packit Service c5cf8c
}
Packit Service c5cf8c
Packit Service c5cf8c
/* Close one side of each pipe pair and replace stdout/err with the pipes */
Packit Service c5cf8c
int mypostfork(void *predata, void *data, ProcessState * pState)
Packit Service c5cf8c
{
Packit Service c5cf8c
    SetupInfo *s = (SetupInfo *) predata;
Packit Service c5cf8c
    int curarg = 0;
Packit Service c5cf8c
Packit Service c5cf8c
    IOLabelSetupInClient(&s->labelinfo);
Packit Service c5cf8c
    PMISetupInClient(1, &s->pmiinfo);
Packit Service c5cf8c
Packit Service c5cf8c
    /* Now, we *also* change the process state to insert the
Packit Service c5cf8c
     * interposed remote shell routine.  This is probably not
Packit Service c5cf8c
     * where we want this in the final version (because MPIE_ExecProgram
Packit Service c5cf8c
     * does a lot under the assumption that the started program will
Packit Service c5cf8c
     * know what to do with new environment variables), but this
Packit Service c5cf8c
     * will allow us to start. */
Packit Service c5cf8c
    {
Packit Service c5cf8c
        ProcessApp *app = pState->app;
Packit Service c5cf8c
        const char **newargs = 0;
Packit Service c5cf8c
        char *pmiDebugStr = 0;
Packit Service c5cf8c
        int j;
Packit Service c5cf8c
        char rankStr[12];
Packit Service c5cf8c
Packit Service c5cf8c
        /* Insert into app->args */
Packit Service c5cf8c
        newargs = (const char **) MPL_malloc((app->nArgs + 14 + 1) * sizeof(char *), MPL_MEM_PM);
Packit Service c5cf8c
        if (!pState->hostname) {
Packit Service c5cf8c
            MPL_error_printf("No hostname avaliable for %s\n", app->exename);
Packit Service c5cf8c
            exit(1);
Packit Service c5cf8c
        }
Packit Service c5cf8c
Packit Service c5cf8c
        snprintf(rankStr, sizeof(rankStr) - 1, "%d", pState->id);
Packit Service c5cf8c
        rankStr[12 - 1] = 0;
Packit Service c5cf8c
        curarg = 0;
Packit Service c5cf8c
        newargs[curarg++] = MPL_strdup("-Y");
Packit Service c5cf8c
Packit Service c5cf8c
        newargs[curarg++] = pState->hostname;
Packit Service c5cf8c
        curarg += AddEnvSetToCmdLine("PMI_PORT", s->pmiinfo.portName, newargs + curarg);
Packit Service c5cf8c
        curarg += AddEnvSetToCmdLine("PMI_ID", rankStr, newargs + curarg);
Packit Service c5cf8c
        pmiDebugStr = getenv("PMI_DEBUG");
Packit Service c5cf8c
        if (pmiDebugStr) {
Packit Service c5cf8c
            /* Use this to help debug the connection process */
Packit Service c5cf8c
            curarg += AddEnvSetToCmdLine("PMI_DEBUG", pmiDebugStr, newargs + curarg);
Packit Service c5cf8c
        }
Packit Service c5cf8c
Packit Service c5cf8c
        newargs[curarg++] = app->exename;
Packit Service c5cf8c
        for (j = 0; j < app->nArgs; j++) {
Packit Service c5cf8c
            newargs[j + curarg] = app->args[j];
Packit Service c5cf8c
        }
Packit Service c5cf8c
        newargs[j + curarg] = 0;
Packit Service c5cf8c
        app->exename = MPL_strdup("/usr/bin/ssh");
Packit Service c5cf8c
Packit Service c5cf8c
        app->args = newargs;
Packit Service c5cf8c
        app->nArgs += curarg;
Packit Service c5cf8c
Packit Service c5cf8c
        if (MPIE_Debug) {
Packit Service c5cf8c
            printf("cmd = %s\n", app->exename);
Packit Service c5cf8c
            fflush(stdout);
Packit Service c5cf8c
            printf("Number of args = %d\n", app->nArgs);
Packit Service c5cf8c
            for (j = 0; j < app->nArgs; j++) {
Packit Service c5cf8c
                printf("argv[%d] = %s\n", j, app->args[j]);
Packit Service c5cf8c
                fflush(stdout);
Packit Service c5cf8c
            }
Packit Service c5cf8c
        }
Packit Service c5cf8c
    }
Packit Service c5cf8c
Packit Service c5cf8c
    return 0;
Packit Service c5cf8c
}
Packit Service c5cf8c
Packit Service c5cf8c
/* Close one side of the pipe pair and register a handler for the I/O */
Packit Service c5cf8c
int mypostamble(void *predata, void *data, ProcessState * pState)
Packit Service c5cf8c
{
Packit Service c5cf8c
    SetupInfo *s = (SetupInfo *) predata;
Packit Service c5cf8c
Packit Service c5cf8c
    IOLabelSetupFinishInServer(&s->labelinfo, pState);
Packit Service c5cf8c
    PMISetupFinishInServer(1, &s->pmiinfo, pState);
Packit Service c5cf8c
Packit Service c5cf8c
    return 0;
Packit Service c5cf8c
}
Packit Service c5cf8c
Packit Service c5cf8c
int myspawn(ProcessWorld * pWorld, void *data)
Packit Service c5cf8c
{
Packit Service c5cf8c
    SetupInfo *s = (SetupInfo *) data;
Packit Service c5cf8c
    ProcessWorld *p, **pPtr;
Packit Service c5cf8c
Packit Service c5cf8c
    p = pUniv.worlds;
Packit Service c5cf8c
    pPtr = &(pUniv.worlds);
Packit Service c5cf8c
    while (p) {
Packit Service c5cf8c
        pPtr = &p->nextWorld;
Packit Service c5cf8c
        p = *pPtr;
Packit Service c5cf8c
    }
Packit Service c5cf8c
    *pPtr = pWorld;
Packit Service c5cf8c
Packit Service c5cf8c
    /* Fork Processes may call a routine that is passed s but not pWorld;
Packit Service c5cf8c
     * this makes sure that all routines can access the current world */
Packit Service c5cf8c
    s->pmiinfo.pWorld = pWorld;
Packit Service c5cf8c
Packit Service c5cf8c
    /* FIXME: This should be part of the PMI initialization in the clients */
Packit Service c5cf8c
    MPIE_Putenv(pWorld, "PMI_SPAWNED=1");
Packit Service c5cf8c
Packit Service c5cf8c
    MPIE_ForkProcesses(pWorld, 0, mypreamble, s, mypostfork, 0, mypostamble, 0);
Packit Service c5cf8c
    return 0;
Packit Service c5cf8c
}
Packit Service c5cf8c
Packit Service c5cf8c
/* Temp test for the replacement for the simple "spawn == fork" */
Packit Service c5cf8c
Packit Service c5cf8c
/*
Packit Service c5cf8c
 * Approach:
Packit Service c5cf8c
 * Processes are created using a remote shell program. This requires
Packit Service c5cf8c
 * changing the command line from
Packit Service c5cf8c
 *
Packit Service c5cf8c
 *  a.out args ...
Packit Service c5cf8c
 *
Packit Service c5cf8c
 * to
Packit Service c5cf8c
 *
Packit Service c5cf8c
 * remshell-program remshell-args /bin/sh -c PMI_PORT=string &&
Packit Service c5cf8c
 *            export PMI_PORT && PMI_ID=rank-in-world && export PMI_ID &&
Packit Service c5cf8c
 *            a.out args
Packit Service c5cf8c
 *
Packit Service c5cf8c
 * (the export PMI_PORT=string syntax is not valid in all versions of sh)
Packit Service c5cf8c
 *
Packit Service c5cf8c
 * Using PMI_ID ensures that we correctly identify each process (this was
Packit Service c5cf8c
 * a major problem in the setup used by the p4 device in MPICH1).
Packit Service c5cf8c
 * Using environment variables instead of command line arguments keeps
Packit Service c5cf8c
 * the commaand line clean.
Packit Service c5cf8c
 *
Packit Service c5cf8c
 * Two alternatives should be considered
Packit Service c5cf8c
 * 1) Use an intermediate manager.  This would allow us to set up the
Packit Service c5cf8c
 *    environment as well:
Packit Service c5cf8c
 *    remshell-program remshell-args manager -port string
Packit Service c5cf8c
 * 2) Use the secure server (even the same one as in MPICH1); then
Packit Service c5cf8c
 *    there is no remote shell command.
Packit Service c5cf8c
 *
Packit Service c5cf8c
 * We can handle the transformation of the command line by adding a
Packit Service c5cf8c
 * to the postfork routine; this is called after the fork but before the
Packit Service c5cf8c
 * exec, and it can change the command line by making a copy of the app
Packit Service c5cf8c
 * structure, changing the command line, and setting the pState structure
Packit Service c5cf8c
 * to point to this new app (after the fork, these changes are visable only
Packit Service c5cf8c
 * to the forked process).
Packit Service c5cf8c
 *
Packit Service c5cf8c
 * Enhancements:
Packit Service c5cf8c
 * Allow the code to avoid the remote shell if the process is being created
Packit Service c5cf8c
 * on the local host.
Packit Service c5cf8c
 *
Packit Service c5cf8c
 * Handle the user of -l username and -n options to remshell
Packit Service c5cf8c
 * (-n makes stdin /dev/null, necessary for backgrounding).
Packit Service c5cf8c
 * (-l username allows login to hosts where the user's username is
Packit Service c5cf8c
 * different)
Packit Service c5cf8c
 *
Packit Service c5cf8c
 * Provide an option to add a backslash before any - to deal with the
Packit Service c5cf8c
 * serious bug in the GNU inetutils remote shell programs that process
Packit Service c5cf8c
 * *all* arguments on the remote shell command line, even those for the
Packit Service c5cf8c
 * *program*!
Packit Service c5cf8c
 *
Packit Service c5cf8c
 * To best support the errcodes return from MPI_Comm_spawn,
Packit Service c5cf8c
 * we need a way to communicate the array of error codes back to the
Packit Service c5cf8c
 * spawn and spawn multiple commands.  Query: how is that done in
Packit Service c5cf8c
 * PMI?
Packit Service c5cf8c
 *
Packit Service c5cf8c
 */
Packit Service c5cf8c
Packit Service c5cf8c
static int AddEnvSetToCmdLine(const char *envName, const char *envValue, const char **args)
Packit Service c5cf8c
{
Packit Service c5cf8c
    int nArgs = 0;
Packit Service c5cf8c
    static int useCSHFormat = -1;
Packit Service c5cf8c
Packit Service c5cf8c
    /* Determine the Shell type the first time */
Packit Service c5cf8c
    if (useCSHFormat == -1) {
Packit Service c5cf8c
        char *shell = getenv("SHELL"), *sname;
Packit Service c5cf8c
        if (shell) {
Packit Service c5cf8c
            /* printf("Shell is %s\n", shell); */
Packit Service c5cf8c
            sname = strrchr(shell, '/');
Packit Service c5cf8c
            if (!sname)
Packit Service c5cf8c
                sname = shell;
Packit Service c5cf8c
            else
Packit Service c5cf8c
                sname++;
Packit Service c5cf8c
            /* printf("Sname is %s\n", sname); */
Packit Service c5cf8c
            if (strcmp(sname, "bash") == 0 || strcmp(sname, "sh") || strcmp(sname, "ash") == 0)
Packit Service c5cf8c
                useCSHFormat = 0;
Packit Service c5cf8c
            else
Packit Service c5cf8c
                useCSHFormat = 1;
Packit Service c5cf8c
        } else {
Packit Service c5cf8c
            /* Default is to assume csh (setenv) format */
Packit Service c5cf8c
            useCSHFormat = 1;
Packit Service c5cf8c
        }
Packit Service c5cf8c
    }
Packit Service c5cf8c
Packit Service c5cf8c
    if (useCSHFormat) {
Packit Service c5cf8c
        args[nArgs++] = MPL_strdup("setenv");
Packit Service c5cf8c
        args[nArgs++] = MPL_strdup(envName);
Packit Service c5cf8c
        args[nArgs++] = MPL_strdup(envValue);
Packit Service c5cf8c
        args[nArgs++] = MPL_strdup(";");
Packit Service c5cf8c
    } else {
Packit Service c5cf8c
        char tmpBuf[1024];
Packit Service c5cf8c
        args[nArgs++] = MPL_strdup("export");
Packit Service c5cf8c
        MPL_strncpy(tmpBuf, envName, sizeof(tmpBuf));
Packit Service c5cf8c
        MPL_strnapp(tmpBuf, "=", sizeof(tmpBuf));
Packit Service c5cf8c
        MPL_strnapp(tmpBuf, envValue, sizeof(tmpBuf));
Packit Service c5cf8c
        args[nArgs++] = MPL_strdup(tmpBuf);
Packit Service c5cf8c
        args[nArgs++] = MPL_strdup(";");
Packit Service c5cf8c
    }
Packit Service c5cf8c
    return nArgs;
Packit Service c5cf8c
}