From 92eb3d448a4485f2a2f87043772d5edabdee9126 Mon Sep 17 00:00:00 2001
From: Piotr Lesnicki <piotr.lesnicki@ext.bull.net>
Date: Mon, 17 Jun 2013 11:30:55 -0700
Subject: [PATCH] Commit the PMI2 client library.

---
 contribs/pmi2/Makefile       |   21 +
 contribs/pmi2/pmi2_api.c     | 1903 ++++++++++++++++++++++++++++++++++
 contribs/pmi2/pmi2_api.h     |  704 +++++++++++++
 contribs/pmi2/pmi2_util.c    |  223 ++++
 contribs/pmi2/pmi2_util.h    |  118 +++
 contribs/pmi2/testpmi2.c     |   12 +
 contribs/pmi2/testpmi2_put.c |   40 +
 7 files changed, 3021 insertions(+)
 create mode 100644 contribs/pmi2/Makefile
 create mode 100644 contribs/pmi2/pmi2_api.c
 create mode 100644 contribs/pmi2/pmi2_api.h
 create mode 100644 contribs/pmi2/pmi2_util.c
 create mode 100644 contribs/pmi2/pmi2_util.h
 create mode 100644 contribs/pmi2/testpmi2.c
 create mode 100644 contribs/pmi2/testpmi2_put.c

diff --git a/contribs/pmi2/Makefile b/contribs/pmi2/Makefile
new file mode 100644
index 00000000000..77bd0d0f87e
--- /dev/null
+++ b/contribs/pmi2/Makefile
@@ -0,0 +1,21 @@
+CFLAGS=-Wall -Wextra -g
+
+all: libpmi2.so testpmi2 testpmi2_put
+
+testpmi2: LDLIBS+=-L. -lpmi2
+testpmi2: CFLAGS+=-I.
+
+testpmi2_put: LDLIBS+=-L. -lpmi2
+testpmi2_put: CFLAGS+=-I.
+
+
+%.o: CFLAGS=-fPIC
+
+libpmi2.so: LDFLAGS+= -shared -Wl,-soname,libpmi2.so
+libpmi2.so: pmi2_api.o pmi2_util.o
+	$(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@ $(LDLIBS)
+
+
+clean:
+	rm -f *.so *.o testpmi2 testpmi2_put
+
diff --git a/contribs/pmi2/pmi2_api.c b/contribs/pmi2/pmi2_api.c
new file mode 100644
index 00000000000..34ab6c6d83f
--- /dev/null
+++ b/contribs/pmi2/pmi2_api.c
@@ -0,0 +1,1903 @@
+/* -*- Mode: C; c-basic-offset:4 ; -*- */
+/*
+ *  (C) 2007 by Argonne National Laboratory.
+ *      See COPYRIGHT in top-level directory.
+ */
+
+#include "pmi2_util.h"
+#include "pmi2_api.h"
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <errno.h>
+
+#ifndef MAXHOSTNAME
+#define MAXHOSTNAME 256
+#endif
+
+#define PMII_EXIT_CODE -1
+
+#define PMI_VERSION    2
+#define PMI_SUBVERSION 0
+
+#define MAX_INT_STR_LEN 11 /* number of digits in MAX_UINT + 1 */
+
+typedef enum {
+    PMI2_UNINITIALIZED = 0,
+    SINGLETON_INIT_BUT_NO_PM = 1,
+    NORMAL_INIT_WITH_PM,
+    SINGLETON_INIT_WITH_PM
+} PMI2State;
+
+static PMI2State PMI2_initialized = PMI2_UNINITIALIZED;
+
+static int PMI2_debug = 0;
+static int PMI2_fd = -1;
+static int PMI2_size = 1;
+static int PMI2_rank = 0;
+
+
+/* XXX DJG the "const"s on both of these functions and the Keyvalpair
+ * struct are wrong in the isCopy==TRUE case! */
+/* init_kv_str -- fills in keyvalpair.  val is required to be a
+   null-terminated string.  isCopy is set to FALSE, so caller must
+   free key and val memory, if necessary.
+*/
+static void init_kv_str(PMI2_Keyvalpair *kv, const char key[], const char val[])
+{
+    kv->key = key;
+    kv->value = val;
+    kv->valueLen = strlen(val);
+    kv->isCopy = 0/*FALSE*/;
+}
+
+/* same as init_kv_str, but strdup's the key and val first, and sets isCopy=TRUE */
+static void init_kv_strdup(PMI2_Keyvalpair *kv, const char key[], const char val[])
+{
+    /* XXX DJG could be slightly more efficient */
+    init_kv_str(kv, strdup(key), strdup(val));
+    kv->isCopy = 1/*TRUE*/;
+}
+
+/* same as init_kv_strdup, but converts val into a string first */
+/* XXX DJG could be slightly more efficient */
+static void init_kv_strdup_int(PMI2_Keyvalpair *kv, const char key[], int val)
+{
+    char tmpbuf[32] = {0};
+    int rc = PMI2_SUCCESS;
+
+    rc = snprintf(tmpbuf, sizeof(tmpbuf), "%d", val);
+    PMI2U_Assert(rc >= 0);
+    init_kv_strdup(kv, key, tmpbuf);
+}
+
+/* initializes the key with ("%s%d", key_prefix, suffix), uses a string value */
+/* XXX DJG could be slightly more efficient */
+static void init_kv_strdup_intsuffix(PMI2_Keyvalpair *kv, const char key_prefix[], int suffix, const char val[])
+{
+    char tmpbuf[256/*XXX HACK*/] = {0};
+    int rc = PMI2_SUCCESS;
+
+    rc = snprintf(tmpbuf, sizeof(tmpbuf), "%s%d", key_prefix, suffix);
+    PMI2U_Assert(rc >= 0);
+    init_kv_strdup(kv, tmpbuf, val);
+}
+
+
+static int getPMIFD(void);
+static int PMIi_ReadCommandExp( int fd, PMI2_Command *cmd, const char *exp, int* rc, const char **errmsg );
+static int PMIi_ReadCommand( int fd, PMI2_Command *cmd );
+
+static int PMIi_WriteSimpleCommand( int fd, PMI2_Command *resp, const char cmd[], PMI2_Keyvalpair *pairs[], int npairs);
+static int PMIi_WriteSimpleCommandStr( int fd, PMI2_Command *resp, const char cmd[], ...);
+static int PMIi_InitIfSingleton(void);
+
+static void freepairs(PMI2_Keyvalpair** pairs, int npairs);
+static int getval(PMI2_Keyvalpair *const pairs[], int npairs, const char *key,  const char **value, int *vallen);
+static int getvalint(PMI2_Keyvalpair *const pairs[], int npairs, const char *key, int *val);
+static int getvalptr(PMI2_Keyvalpair *const pairs[], int npairs, const char *key, void *val);
+static int getvalbool(PMI2_Keyvalpair *const pairs[], int npairs, const char *key, int *val);
+
+static int accept_one_connection(int list_sock);
+static int GetResponse(const char request[], const char expectedCmd[], int checkRc);
+
+static void dump_PMI2_Command(PMI2_Command *cmd);
+static void dump_PMI2_Keyvalpair(PMI2_Keyvalpair *kv);
+
+
+typedef struct pending_item
+{
+    struct pending_item *next;
+    PMI2_Command *cmd;
+} pending_item_t;
+
+pending_item_t *pendingq_head = NULL;
+pending_item_t *pendingq_tail = NULL;
+
+static inline void ENQUEUE(PMI2_Command *cmd)
+{
+    pending_item_t *pi = malloc(sizeof(pending_item_t));
+
+    pi->next = NULL;
+    pi->cmd = cmd;
+
+    if (pendingq_head == NULL) {
+        pendingq_head = pendingq_tail = pi;
+    } else {
+        pendingq_tail->next = pi;
+        pendingq_tail = pi;
+    }
+}
+
+static inline int SEARCH_REMOVE(PMI2_Command *cmd)
+{
+    pending_item_t *pi, *prev;
+
+    pi = pendingq_head;
+    if (pi->cmd == cmd) {
+        pendingq_head = pi->next;
+        if (pendingq_head == NULL)
+            pendingq_tail = NULL;
+        free(pi);
+        return 1;
+    }
+    prev = pi;
+    pi = pi->next;
+
+    for ( ; pi ; pi = pi->next) {
+        if (pi->cmd == cmd) {
+            prev->next = pi->next;
+            if (prev->next == NULL)
+                pendingq_tail = prev;
+            free(pi);
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+/* ------------------------------------------------------------------------- */
+/* PMI-2 API Routines                                                        */
+/* ------------------------------------------------------------------------- */
+int PMI2_Init(int *spawned, int *size, int *rank, int *appnum)
+{
+    int pmi2_errno = PMI2_SUCCESS;
+    char *p;
+    char buf[PMI2_MAXLINE], cmdline[PMI2_MAXLINE];
+    char *jobid;
+    char *pmiid;
+    int ret;
+
+    PMI2U_printf("[BEGIN]");
+
+    /* Get the value of PMI2_DEBUG from the environment if possible, since
+       we may have set it to help debug the setup process */
+    p = getenv("PMI2_DEBUG");
+    if (p) PMI2_debug = atoi(p);
+
+    /* Get the fd for PMI commands; if none, we're a singleton */
+    pmi2_errno = getPMIFD();
+    if (pmi2_errno) PMI2U_ERR_POP(pmi2_errno);
+
+    if (PMI2_fd == -1) {
+		/* Singleton init: Process not started with mpiexec,
+     		   so set size to 1, rank to 0 */
+		PMI2_size = 1;
+		PMI2_rank = 0;
+		*spawned = 0;
+
+		PMI2_initialized = SINGLETON_INIT_BUT_NO_PM;
+		goto fn_exit;
+    }
+
+    /* do initial PMI1 init */
+    ret = snprintf(buf, PMI2_MAXLINE, "cmd=init pmi_version=%d pmi_subversion=%d\n", PMI_VERSION, PMI_SUBVERSION);
+    PMI2U_ERR_CHKANDJUMP(ret < 0, pmi2_errno, PMI2_ERR_OTHER, "**intern %s", "failed to generate init line");
+
+    ret = PMI2U_writeline(PMI2_fd, buf);
+    PMI2U_ERR_CHKANDJUMP(ret < 0, pmi2_errno, PMI2_ERR_OTHER, "**pmi2_init_send");
+
+    ret = PMI2U_readline(PMI2_fd, buf, PMI2_MAXLINE);
+    PMI2U_ERR_CHKANDJUMP(ret < 0, pmi2_errno, PMI2_ERR_OTHER, "**pmi2_initack %s", strerror(pmi2_errno));
+
+    PMI2U_parse_keyvals(buf);
+    cmdline[0] = 0;
+    PMI2U_getval("cmd", cmdline, PMI2_MAXLINE);
+    PMI2U_ERR_CHKANDJUMP(strncmp(cmdline, "response_to_init", PMI2_MAXLINE) != 0,  pmi2_errno, PMI2_ERR_OTHER, "**bad_cmd");
+
+    PMI2U_getval("rc", buf, PMI2_MAXLINE);
+    if (strncmp(buf, "0", PMI2_MAXLINE) != 0) {
+        char buf1[PMI2_MAXLINE];
+        PMI2U_getval("pmi_version", buf, PMI2_MAXLINE);
+        PMI2U_getval("pmi_subversion", buf1, PMI2_MAXLINE);
+        PMI2U_ERR_SETANDJUMP(pmi2_errno, PMI2_ERR_OTHER, "**pmi2_version %s %s %d %d", buf, buf1, PMI_VERSION, PMI_SUBVERSION);
+    }
+
+    PMI2U_printf("do full PMI2 init ...");
+    /* do full PMI2 init */
+    {
+        PMI2_Keyvalpair pairs[3];
+        PMI2_Keyvalpair *pairs_p[] = { pairs, pairs+1, pairs+2 };
+        int npairs = 0;
+        int isThreaded = 0;
+        const char *errmsg;
+        int rc;
+        int found;
+        int version, subver;
+        const char *spawner_jobid;
+        int spawner_jobid_len;
+        PMI2_Command cmd = {0};
+        int debugged;
+        int PMI2_pmiverbose;
+
+
+        jobid = getenv("PMI_JOBID");
+        if (jobid) {
+            init_kv_str(&pairs[npairs], PMIJOBID_KEY, jobid);
+            ++npairs;
+        }
+
+        pmiid = getenv("PMI_ID");
+        if (pmiid) {
+            init_kv_str(&pairs[npairs], SRCID_KEY, pmiid);
+            ++npairs;
+        }
+        else {
+            pmiid = getenv("PMI_RANK");
+            if (pmiid) {
+                init_kv_str(&pairs[npairs], PMIRANK_KEY, pmiid);
+                PMI2_rank = strtol(pmiid, NULL, 10);
+                ++npairs;
+            }
+        }
+
+        init_kv_str(&pairs[npairs], THREADED_KEY, isThreaded ? "TRUE" : "FALSE");
+        ++npairs;
+
+        pmi2_errno = PMIi_WriteSimpleCommand(PMI2_fd, 0, FULLINIT_CMD, pairs_p, npairs); /* don't pass in thread id for init */
+        if (pmi2_errno) PMI2U_ERR_SETANDJUMP(1, pmi2_errno, "PMIi_WriteSimpleCommand");
+
+        /* Read auth-response */
+        /* Send auth-response-complete */
+
+        /* Read fullinit-response */
+        pmi2_errno = PMIi_ReadCommandExp(PMI2_fd, &cmd, FULLINITRESP_CMD, &rc, &errmsg);
+        if (pmi2_errno) PMI2U_ERR_SETANDJUMP(1, pmi2_errno, "PMIi_ReadCommandExp");
+        PMI2U_ERR_CHKANDJUMP(rc, pmi2_errno, PMI2_ERR_OTHER, "**pmi2_fullinit %s", errmsg ? errmsg : "unknown");
+
+        found = getvalint(cmd.pairs, cmd.nPairs, PMIVERSION_KEY, &version);
+        PMI2U_ERR_CHKANDJUMP(found != 1, pmi2_errno, PMI2_ERR_OTHER, "**intern");
+
+        found = getvalint(cmd.pairs, cmd.nPairs, PMISUBVER_KEY, &subver);
+        PMI2U_ERR_CHKANDJUMP(found != 1, pmi2_errno, PMI2_ERR_OTHER, "**intern");
+
+        found = getvalint(cmd.pairs, cmd.nPairs, RANK_KEY, rank);
+        PMI2U_ERR_CHKANDJUMP(found != 1, pmi2_errno, PMI2_ERR_OTHER, "**intern");
+
+        found = getvalint(cmd.pairs, cmd.nPairs, SIZE_KEY, size);
+        PMI2U_ERR_CHKANDJUMP(found != 1, pmi2_errno, PMI2_ERR_OTHER, "**intern");
+        PMI2_size = *size;
+
+        found = getvalint(cmd.pairs, cmd.nPairs, APPNUM_KEY, appnum);
+        PMI2U_ERR_CHKANDJUMP(found != 1, pmi2_errno, PMI2_ERR_OTHER, "**intern");
+
+        found = getval(cmd.pairs, cmd.nPairs, SPAWNERJOBID_KEY, &spawner_jobid, &spawner_jobid_len);
+        PMI2U_ERR_CHKANDJUMP(found == -1, pmi2_errno, PMI2_ERR_OTHER, "**intern");
+        if (found)
+            *spawned = TRUE;
+        else
+            *spawned = FALSE;
+
+        debugged = 0;
+        found = getvalbool(cmd.pairs, cmd.nPairs, DEBUGGED_KEY, &debugged);
+        PMI2U_ERR_CHKANDJUMP(found == -1, pmi2_errno, PMI2_ERR_OTHER, "**intern");
+        PMI2_debug |= debugged;
+
+        PMI2_pmiverbose = 0;
+        found = getvalbool(cmd.pairs, cmd.nPairs, PMIVERBOSE_KEY, &PMI2_pmiverbose);
+        PMI2U_ERR_CHKANDJUMP(found == -1, pmi2_errno, PMI2_ERR_OTHER, "**intern");
+
+        free(cmd.command);
+        freepairs(cmd.pairs, cmd.nPairs);
+    }
+
+    if (! PMI2_initialized) {
+        PMI2_initialized = NORMAL_INIT_WITH_PM;
+        pmi2_errno = PMI2_SUCCESS;
+    }
+
+fn_exit:
+    PMI2U_printf("[END]");
+    return pmi2_errno;
+fn_fail:
+    goto fn_exit;
+}
+
+int PMI2_Finalize(void)
+{
+    int pmi2_errno = PMI2_SUCCESS;
+    int rc;
+    const char *errmsg;
+    PMI2_Command cmd = {0};
+
+    PMI2U_printf("[BEGIN]");
+
+    if (PMI2_initialized > SINGLETON_INIT_BUT_NO_PM) {
+        pmi2_errno = PMIi_WriteSimpleCommandStr(PMI2_fd, &cmd, FINALIZE_CMD, NULL);
+        if (pmi2_errno) PMI2U_ERR_SETANDJUMP(1, pmi2_errno, "PMIi_WriteSimpleCommandStr");
+        pmi2_errno = PMIi_ReadCommandExp(PMI2_fd, &cmd, FINALIZERESP_CMD, &rc, &errmsg);
+        if (pmi2_errno) PMI2U_ERR_SETANDJUMP(1, pmi2_errno, "PMIi_ReadCommandExp");
+        PMI2U_ERR_CHKANDJUMP(rc, pmi2_errno, PMI2_ERR_OTHER, "**pmi2_finalize %s", errmsg ? errmsg : "unknown");
+
+        free(cmd.command);
+        freepairs(cmd.pairs, cmd.nPairs);
+
+        shutdown(PMI2_fd, SHUT_RDWR);
+        close(PMI2_fd);
+    }
+
+fn_exit:
+    PMI2U_printf("[END]");
+    return pmi2_errno;
+fn_fail:
+    goto fn_exit;
+}
+
+int PMI2_Initialized(void)
+{
+    /* Turn this into a logical value (1 or 0). This allows us
+       to use PMI2_initialized to distinguish between initialized with
+       an PMI service (e.g., via mpiexec) and the singleton init,
+       which has no PMI service */
+    return (PMI2_initialized != 0);
+}
+
+int PMI2_Abort(int flag, const char msg[])
+{
+    PMI2U_printf("aborting job:\n%s", msg);
+
+    /* ignoring return code, because we're exiting anyway */
+    PMIi_WriteSimpleCommandStr(PMI2_fd, NULL, ABORT_CMD, ISWORLD_KEY, flag ? TRUE_VAL : FALSE_VAL, MSG_KEY, msg, NULL);
+
+    exit(PMII_EXIT_CODE);
+    return PMI2_SUCCESS;
+}
+
+int PMI2_Job_Spawn(int count, const char * cmds[],
+                   int argcs[], const char ** argvs[],
+                   const int maxprocs[],
+                   const int info_keyval_sizes[],
+                   const struct MPID_Info *info_keyval_vectors[],
+                   int preput_keyval_size,
+                   const struct MPID_Info *preput_keyval_vector[],
+                   char jobId[], int jobIdSize,
+                   int errors[])
+{
+    int  i,rc,spawncnt,total_num_processes,num_errcodes_found;
+    int found;
+    const char *jid;
+    int jidlen;
+    char tempbuf[PMI2_MAXLINE];
+    char *lead, *lag;
+    int spawn_rc;
+    const char *errmsg = NULL;
+    PMI2_Command resp_cmd  = {0};
+    int pmi2_errno = 0;
+    PMI2_Keyvalpair **pairs_p = NULL;
+    int npairs = 0;
+    int total_pairs = 0;
+
+    PMI2U_printf("[BEGIN]");
+
+    /* Connect to the PM if we haven't already */
+    if (PMIi_InitIfSingleton() != 0) return -1;
+
+    total_num_processes = 0;
+
+/* XXX DJG from Pavan's email:
+cmd=spawn;thrid=string;ncmds=count;preputcount=n;ppkey0=name;ppval0=string;...;\
+        subcmd=spawn-exe1;maxprocs=n;argc=narg;argv0=name;\
+                argv1=name;...;infokeycount=n;infokey0=key;\
+                infoval0=string;...;\
+(... one subcmd for each executable ...)
+*/
+
+    /* FIXME overall need a better interface for building commands!
+     * Need to be able to append commands, and to easily accept integer
+     * valued arguments.  Memory management should stay completely out
+     * of mind when writing a new PMI command impl like this! */
+
+    /* Calculate the total number of keyval pairs that we need.
+     *
+     * The command writing utility adds "cmd" and "thrid" fields for us,
+     * don't include them in our count. */
+    total_pairs = 2; /* ncmds,preputcount */
+    total_pairs += (3 * count); /* subcmd,maxprocs,argc */
+    total_pairs += (2 * preput_keyval_size); /* ppkeyN,ppvalN */
+    for (spawncnt = 0; spawncnt < count; ++spawncnt) {
+        total_pairs += argcs[spawncnt]; /* argvN */
+        if (info_keyval_sizes) {
+            total_pairs += 1;  /* infokeycount */
+            total_pairs += 2 * info_keyval_sizes[spawncnt]; /* infokeyN,infovalN */
+        }
+    }
+
+    pairs_p = malloc(total_pairs * sizeof(PMI2_Keyvalpair*));
+    /* individiually allocating instead of batch alloc b/c freepairs assumes it */
+    for (i = 0; i < total_pairs; ++i) {
+        /* FIXME we are somehow still leaking some of this memory */
+        pairs_p[i] = malloc(sizeof(PMI2_Keyvalpair));
+        PMI2U_Assert(pairs_p[i]);
+    }
+
+    init_kv_strdup_int(pairs_p[npairs++], "ncmds", count);
+
+    init_kv_strdup_int(pairs_p[npairs++], "preputcount", preput_keyval_size);
+    for (i = 0; i < preput_keyval_size; ++i) {
+        init_kv_strdup_intsuffix(pairs_p[npairs++], "ppkey", i, preput_keyval_vector[i]->key);
+        init_kv_strdup_intsuffix(pairs_p[npairs++], "ppval", i, preput_keyval_vector[i]->value);
+    }
+
+    for (spawncnt = 0; spawncnt < count; ++spawncnt)
+    {
+        total_num_processes += maxprocs[spawncnt];
+
+        init_kv_strdup(pairs_p[npairs++], "subcmd", cmds[spawncnt]);
+        init_kv_strdup_int(pairs_p[npairs++], "maxprocs", maxprocs[spawncnt]);
+
+        init_kv_strdup_int(pairs_p[npairs++], "argc", argcs[spawncnt]);
+        for (i = 0; i < argcs[spawncnt]; ++i) {
+            init_kv_strdup_intsuffix(pairs_p[npairs++], "argv", i, argvs[spawncnt][i]);
+        }
+
+        if (info_keyval_sizes) {
+            init_kv_strdup_int(pairs_p[npairs++], "infokeycount", info_keyval_sizes[spawncnt]);
+            for (i = 0; i < info_keyval_sizes[spawncnt]; ++i) {
+                init_kv_strdup_intsuffix(pairs_p[npairs++], "infokey", i, info_keyval_vectors[spawncnt][i].key);
+                init_kv_strdup_intsuffix(pairs_p[npairs++], "infoval", i, info_keyval_vectors[spawncnt][i].value);
+            }
+        }
+    }
+
+    if (npairs < total_pairs) { PMI2U_printf("about to fail assertion, npairs=%d total_pairs=%d", npairs, total_pairs); }
+    PMI2U_Assert(npairs == total_pairs);
+
+    pmi2_errno = PMIi_WriteSimpleCommand(PMI2_fd, &resp_cmd, "spawn", pairs_p, npairs);
+    if (pmi2_errno) PMI2U_ERR_SETANDJUMP(1, pmi2_errno, "PMIi_WriteSimpleCommand");
+
+    freepairs(pairs_p, npairs);
+    pairs_p = NULL;
+
+    /* XXX DJG TODO release any upper level MPICH2 critical sections */
+    rc = PMIi_ReadCommandExp(PMI2_fd, &resp_cmd, "spawn-response", &spawn_rc, &errmsg);
+    if (rc != 0) { return PMI2_FAIL; }
+
+    /* XXX DJG TODO deal with the response */
+    PMI2U_Assert(errors != NULL);
+
+    if (jobId && jobIdSize) {
+        found = getval(resp_cmd.pairs, resp_cmd.nPairs, JOBID_KEY, &jid, &jidlen);
+        PMI2U_ERR_CHKANDJUMP(found != 1, pmi2_errno, PMI2_ERR_OTHER, "**intern");
+        strncpy(jobId, jid, jobIdSize);
+    }
+
+    if (PMI2U_getval("errcodes", tempbuf, PMI2_MAXLINE)) {
+        num_errcodes_found = 0;
+        lag = &tempbuf[0];
+        do {
+            lead = strchr(lag, ',');
+            if (lead) *lead = '\0';
+            errors[num_errcodes_found++] = atoi(lag);
+            lag = lead + 1; /* move past the null char */
+            PMI2U_Assert(num_errcodes_found <= total_num_processes);
+        } while (lead != NULL);
+        PMI2U_Assert(num_errcodes_found == total_num_processes);
+    }
+    else {
+        /* gforker doesn't return errcodes, so we'll just pretend that means
+           that it was going to send all `0's. */
+        for (i = 0; i < total_num_processes; ++i) {
+            errors[i] = 0;
+        }
+    }
+
+fn_fail:
+    free(resp_cmd.command);
+    freepairs(resp_cmd.pairs, resp_cmd.nPairs);
+    if (pairs_p) freepairs(pairs_p, npairs);
+
+    PMI2U_printf("[END]");
+    return pmi2_errno;
+}
+
+int PMI2_Job_GetId(char jobid[], int jobid_size)
+{
+    int pmi2_errno = PMI2_SUCCESS;
+    int found;
+    const char *jid;
+    int jidlen;
+    int rc;
+    const char *errmsg;
+    PMI2_Command cmd = {0};
+
+    PMI2U_printf("[BEGIN]");
+
+    pmi2_errno = PMIi_WriteSimpleCommandStr(PMI2_fd, &cmd, JOBGETID_CMD, NULL);
+    if (pmi2_errno) PMI2U_ERR_SETANDJUMP(1, pmi2_errno, "PMIi_WriteSimpleCommandStr");
+    pmi2_errno = PMIi_ReadCommandExp(PMI2_fd, &cmd, JOBGETIDRESP_CMD, &rc, &errmsg);
+    if (pmi2_errno) PMI2U_ERR_SETANDJUMP(1, pmi2_errno, "PMIi_ReadCommandExp");
+    PMI2U_ERR_CHKANDJUMP(rc, pmi2_errno, PMI2_ERR_OTHER, "**pmi2_jobgetid %s", errmsg ? errmsg : "unknown");
+
+    found = getval(cmd.pairs, cmd.nPairs, JOBID_KEY, &jid, &jidlen);
+    PMI2U_ERR_CHKANDJUMP(found != 1, pmi2_errno, PMI2_ERR_OTHER, "**intern");
+
+    strncpy(jobid, jid, jobid_size);
+
+fn_exit:
+    free(cmd.command);
+    freepairs(cmd.pairs, cmd.nPairs);
+    PMI2U_printf("[END]");
+    return pmi2_errno;
+fn_fail:
+    goto fn_exit;
+}
+
+int PMI2_Job_GetRank(int* rank)
+{
+    *rank = PMI2_rank;
+    return PMI2_SUCCESS;
+}
+
+int PMI2_Info_GetSize(int* size)
+{
+    *size = PMI2_size;
+    return PMI2_SUCCESS;
+}
+
+#undef FUNCNAME
+#define FUNCNAME PMI2_Job_Connect
+#undef FCNAME
+#define FCNAME PMI2DI_QUOTE(FUNCNAME)
+
+int PMI2_Job_Connect(const char jobid[], PMI2_Connect_comm_t *conn)
+{
+    int pmi2_errno = PMI2_SUCCESS;
+    PMI2_Command cmd = {0};
+    int found;
+    int kvscopy;
+    int rc;
+    const char *errmsg;
+
+    PMI2U_printf("[BEGIN]");
+
+    pmi2_errno = PMIi_WriteSimpleCommandStr(PMI2_fd, &cmd, JOBCONNECT_CMD, JOBID_KEY, jobid, NULL);
+    if (pmi2_errno) PMI2U_ERR_SETANDJUMP(1, pmi2_errno, "PMIi_WriteSimpleCommandStr");
+    pmi2_errno = PMIi_ReadCommandExp(PMI2_fd, &cmd, JOBCONNECTRESP_CMD, &rc, &errmsg);
+    if (pmi2_errno) PMI2U_ERR_SETANDJUMP(1, pmi2_errno, "PMIi_ReadCommandExp");
+    PMI2U_ERR_CHKANDJUMP(rc, pmi2_errno, PMI2_ERR_OTHER, "**pmi2_jobconnect %s", errmsg ? errmsg : "unknown");
+
+    found = getvalbool(cmd.pairs, cmd.nPairs, KVSCOPY_KEY, &kvscopy);
+    PMI2U_ERR_CHKANDJUMP(found != 1, pmi2_errno, PMI2_ERR_OTHER, "**intern");
+
+    PMI2U_ERR_CHKANDJUMP(kvscopy, pmi2_errno, PMI2_ERR_OTHER, "**notimpl");
+
+ fn_exit:
+    free(cmd.command);
+    freepairs(cmd.pairs, cmd.nPairs);
+    PMI2U_printf("[END]");
+    return pmi2_errno;
+ fn_fail:
+    goto fn_exit;
+}
+
+int PMI2_Job_Disconnect(const char jobid[])
+{
+    int pmi2_errno = PMI2_SUCCESS;
+    PMI2_Command cmd = {0};
+    int rc;
+    const char *errmsg;
+
+    PMI2U_printf("[BEGIN]");
+
+    pmi2_errno = PMIi_WriteSimpleCommandStr(PMI2_fd, &cmd, JOBDISCONNECT_CMD, JOBID_KEY, jobid, NULL);
+    if (pmi2_errno) PMI2U_ERR_SETANDJUMP(1, pmi2_errno, "PMIi_WriteSimpleCommandStr");
+    pmi2_errno = PMIi_ReadCommandExp(PMI2_fd, &cmd, JOBDISCONNECTRESP_CMD, &rc, &errmsg);
+    if (pmi2_errno) PMI2U_ERR_SETANDJUMP(1, pmi2_errno, "PMIi_ReadCommandExp");
+    PMI2U_ERR_CHKANDJUMP(rc, pmi2_errno, PMI2_ERR_OTHER, "**pmi2_jobdisconnect %s", errmsg ? errmsg : "unknown");
+
+fn_exit:
+    free(cmd.command);
+    freepairs(cmd.pairs, cmd.nPairs);
+    PMI2U_printf("[END]");
+    return pmi2_errno;
+fn_fail:
+    goto fn_exit;
+}
+
+int PMI2_KVS_Put(const char key[], const char value[])
+{
+    int pmi2_errno = PMI2_SUCCESS;
+    PMI2_Command cmd = {0};
+    int rc;
+    const char *errmsg;
+
+    PMI2U_printf("[BEGIN]");
+
+    pmi2_errno = PMIi_WriteSimpleCommandStr(PMI2_fd, &cmd, KVSPUT_CMD, KEY_KEY, key, VALUE_KEY, value, NULL);
+    if (pmi2_errno) PMI2U_ERR_SETANDJUMP(1, pmi2_errno, "PMIi_WriteSimpleCommandStr");
+    pmi2_errno = PMIi_ReadCommandExp(PMI2_fd, &cmd, KVSPUTRESP_CMD, &rc, &errmsg);
+    if (pmi2_errno) PMI2U_ERR_SETANDJUMP(1, pmi2_errno, "PMIi_ReadCommandExp");
+    PMI2U_ERR_CHKANDJUMP(rc, pmi2_errno, PMI2_ERR_OTHER, "**pmi2_kvsput %s", errmsg ? errmsg : "unknown");
+
+fn_exit:
+    free(cmd.command);
+    freepairs(cmd.pairs, cmd.nPairs);
+    PMI2U_printf("[END]");
+    return pmi2_errno;
+fn_fail:
+    goto fn_exit;
+}
+
+int PMI2_KVS_Fence(void)
+{
+    int pmi2_errno = PMI2_SUCCESS;
+    PMI2_Command cmd = {0};
+    int rc;
+    const char *errmsg;
+
+    PMI2U_printf("[BEGIN]");
+
+    pmi2_errno = PMIi_WriteSimpleCommandStr(PMI2_fd, &cmd, KVSFENCE_CMD, NULL);
+    if (pmi2_errno) PMI2U_ERR_SETANDJUMP(1, pmi2_errno, "PMIi_WriteSimpleCommandStr");
+    pmi2_errno = PMIi_ReadCommandExp(PMI2_fd, &cmd, KVSFENCERESP_CMD, &rc, &errmsg);
+    if (pmi2_errno) PMI2U_ERR_SETANDJUMP(1, pmi2_errno, "PMIi_ReadCommandExp");
+    PMI2U_ERR_CHKANDJUMP(rc, pmi2_errno, PMI2_ERR_OTHER, "**pmi2_kvsfence %s", errmsg ? errmsg : "unknown");
+
+fn_exit:
+    free(cmd.command);
+    freepairs(cmd.pairs, cmd.nPairs);
+    PMI2U_printf("[END]");
+    return pmi2_errno;
+fn_fail:
+    goto fn_exit;
+}
+
+int PMI2_KVS_Get(const char *jobid, int src_pmi_id, const char key[], char value [], int maxValue, int *valLen)
+{
+    int pmi2_errno = PMI2_SUCCESS;
+    int found, keyfound;
+    const char *kvsvalue;
+    int kvsvallen;
+    int ret;
+    PMI2_Command cmd = {0};
+    int rc;
+    char src_pmi_id_str[256];
+    const char *errmsg;
+
+    PMI2U_printf("[BEGIN]");
+
+    snprintf(src_pmi_id_str, sizeof(src_pmi_id_str), "%d", src_pmi_id);
+
+    pmi2_errno = PMIi_InitIfSingleton();
+    if (pmi2_errno) PMI2U_ERR_SETANDJUMP(1, pmi2_errno, "PMIi_InitIfSingleton");
+
+    pmi2_errno = PMIi_WriteSimpleCommandStr(PMI2_fd, &cmd, KVSGET_CMD, JOBID_KEY, jobid, SRCID_KEY, src_pmi_id_str, KEY_KEY, key, NULL);
+    if (pmi2_errno) PMI2U_ERR_SETANDJUMP(1, pmi2_errno, "PMIi_WriteSimpleCommandStr");
+    pmi2_errno = PMIi_ReadCommandExp(PMI2_fd, &cmd, KVSGETRESP_CMD, &rc, &errmsg);
+    if (pmi2_errno) PMI2U_ERR_SETANDJUMP(1, pmi2_errno, "PMIi_ReadCommandExp");
+    PMI2U_ERR_CHKANDJUMP(rc, pmi2_errno, PMI2_ERR_OTHER, "**pmi2_kvsget %s", errmsg ? errmsg : "unknown");
+
+    found = getvalbool(cmd.pairs, cmd.nPairs, FOUND_KEY, &keyfound);
+    PMI2U_ERR_CHKANDJUMP(found != 1, pmi2_errno, PMI2_ERR_OTHER, "**intern");
+    PMI2U_ERR_CHKANDJUMP(!keyfound, pmi2_errno, PMI2_ERR_OTHER, "**pmi2_kvsget_notfound");
+
+    found = getval(cmd.pairs, cmd.nPairs, VALUE_KEY, &kvsvalue, &kvsvallen);
+    PMI2U_ERR_CHKANDJUMP(found != 1, pmi2_errno, PMI2_ERR_OTHER, "**intern");
+
+    ret = strncpy(value, kvsvalue, maxValue);
+    *valLen = ret ? -kvsvallen : kvsvallen;
+
+ fn_exit:
+    free(cmd.command);
+    freepairs(cmd.pairs, cmd.nPairs);
+    PMI2U_printf("[END]");
+    return pmi2_errno;
+ fn_fail:
+    goto fn_exit;
+}
+
+int PMI2_Info_GetNodeAttr(const char name[], char value[], int valuelen, int *flag, int waitfor)
+{
+    int pmi2_errno = PMI2_SUCCESS;
+    int found;
+    const char *kvsvalue;
+    int kvsvallen;
+    PMI2_Command cmd = {0};
+    int rc;
+    const char *errmsg;
+
+    PMI2U_printf("[BEGIN]");
+
+    pmi2_errno = PMIi_InitIfSingleton();
+    if (pmi2_errno) PMI2U_ERR_SETANDJUMP(1, pmi2_errno, "PMIi_InitIfSingleton");
+
+    pmi2_errno = PMIi_WriteSimpleCommandStr(PMI2_fd, &cmd, GETNODEATTR_CMD, KEY_KEY, name, WAIT_KEY, waitfor ? "TRUE" : "FALSE", NULL);
+    if (pmi2_errno) PMI2U_ERR_SETANDJUMP(1, pmi2_errno, "PMIi_WriteSimpleCommandStr");
+    pmi2_errno = PMIi_ReadCommandExp(PMI2_fd, &cmd, GETNODEATTRRESP_CMD, &rc, &errmsg);
+    if (pmi2_errno) PMI2U_ERR_SETANDJUMP(1, pmi2_errno, "PMIi_ReadCommandExp");
+    PMI2U_ERR_CHKANDJUMP(rc, pmi2_errno, PMI2_ERR_OTHER, "**pmi2_getnodeattr %s", errmsg ? errmsg : "unknown");
+
+    found = getvalbool(cmd.pairs, cmd.nPairs, FOUND_KEY, flag);
+    PMI2U_ERR_CHKANDJUMP(found != 1, pmi2_errno, PMI2_ERR_OTHER, "**intern");
+    if (*flag) {
+        found = getval(cmd.pairs, cmd.nPairs, VALUE_KEY, &kvsvalue, &kvsvallen);
+        PMI2U_ERR_CHKANDJUMP(found != 1, pmi2_errno, PMI2_ERR_OTHER, "**intern");
+
+        strncpy(value, kvsvalue, valuelen);
+    }
+
+fn_exit:
+    free(cmd.command);
+    freepairs(cmd.pairs, cmd.nPairs);
+    PMI2U_printf("[END]");
+    return pmi2_errno;
+fn_fail:
+    goto fn_exit;
+}
+
+int PMI2_Info_GetNodeAttrIntArray(const char name[], int array[], int arraylen, int *outlen, int *flag)
+{
+    int pmi2_errno = PMI2_SUCCESS;
+    int found;
+    const char *kvsvalue;
+    int kvsvallen;
+    PMI2_Command cmd = {0};
+    int rc;
+    const char *errmsg;
+    int i;
+    const char *valptr;
+
+    PMI2U_printf("[BEGIN]");
+
+    pmi2_errno = PMIi_InitIfSingleton();
+    if (pmi2_errno) PMI2U_ERR_SETANDJUMP(1, pmi2_errno, "PMIi_InitIfSingleton");
+
+    pmi2_errno = PMIi_WriteSimpleCommandStr(PMI2_fd, &cmd, GETNODEATTR_CMD, KEY_KEY, name, WAIT_KEY, "FALSE", NULL);
+    if (pmi2_errno) PMI2U_ERR_SETANDJUMP(1, pmi2_errno, "PMIi_WriteSimpleCommandStr");
+    pmi2_errno = PMIi_ReadCommandExp(PMI2_fd, &cmd, GETNODEATTRRESP_CMD, &rc, &errmsg);
+    if (pmi2_errno) PMI2U_ERR_SETANDJUMP(1, pmi2_errno, "PMIi_ReadCommandExp");
+    PMI2U_ERR_CHKANDJUMP(rc, pmi2_errno, PMI2_ERR_OTHER, "**pmi2_getnodeattr %s", errmsg ? errmsg : "unknown");
+
+    found = getvalbool(cmd.pairs, cmd.nPairs, FOUND_KEY, flag);
+    PMI2U_ERR_CHKANDJUMP(found != 1, pmi2_errno, PMI2_ERR_OTHER, "**intern");
+    if (*flag) {
+        found = getval(cmd.pairs, cmd.nPairs, VALUE_KEY, &kvsvalue, &kvsvallen);
+        PMI2U_ERR_CHKANDJUMP(found != 1, pmi2_errno, PMI2_ERR_OTHER, "**intern");
+
+        valptr = kvsvalue;
+        i = 0;
+        rc = sscanf(valptr, "%d", &array[i]);
+        PMI2U_ERR_CHKANDJUMP(rc != 1, pmi2_errno, PMI2_ERR_OTHER, "**intern %s", "unable to parse intarray");
+        ++i;
+        while ((valptr = strchr(valptr, ',')) && i < arraylen) {
+            ++valptr; /* skip over the ',' */
+            rc = sscanf(valptr, "%d", &array[i]);
+            PMI2U_ERR_CHKANDJUMP(rc != 1, pmi2_errno, PMI2_ERR_OTHER, "**intern %s", "unable to parse intarray");
+            ++i;
+        }
+
+        *outlen = i;
+    }
+
+fn_exit:
+    free(cmd.command);
+    freepairs(cmd.pairs, cmd.nPairs);
+    PMI2U_printf("[END]");
+    return pmi2_errno;
+fn_fail:
+    goto fn_exit;
+}
+
+int PMI2_Info_PutNodeAttr(const char name[], const char value[])
+{
+    int pmi2_errno = PMI2_SUCCESS;
+    PMI2_Command cmd = {0};
+    int rc;
+    const char *errmsg;
+
+    PMI2U_printf("[BEGIN]");
+
+    pmi2_errno = PMIi_WriteSimpleCommandStr(PMI2_fd, &cmd, PUTNODEATTR_CMD, KEY_KEY, name, VALUE_KEY, value, NULL);
+    if (pmi2_errno) PMI2U_ERR_SETANDJUMP(1, pmi2_errno, "PMIi_WriteSimpleCommandStr");
+    pmi2_errno = PMIi_ReadCommandExp(PMI2_fd, &cmd, PUTNODEATTRRESP_CMD, &rc, &errmsg);
+    if (pmi2_errno) PMI2U_ERR_SETANDJUMP(1, pmi2_errno, "PMIi_ReadCommandExp");
+    PMI2U_ERR_CHKANDJUMP(rc, pmi2_errno, PMI2_ERR_OTHER, "**pmi2_putnodeattr %s", errmsg ? errmsg : "unknown");
+
+fn_exit:
+    free(cmd.command);
+    freepairs(cmd.pairs, cmd.nPairs);
+    PMI2U_printf("[END]");
+    return pmi2_errno;
+fn_fail:
+    goto fn_exit;
+}
+
+int PMI2_Info_GetJobAttr(const char name[], char value[], int valuelen, int *flag)
+{
+    int pmi2_errno = PMI2_SUCCESS;
+    int found;
+    const char *kvsvalue;
+    int kvsvallen;
+    PMI2_Command cmd = {0};
+    int rc;
+    const char *errmsg;
+
+    PMI2U_printf("[BEGIN]");
+
+    pmi2_errno = PMIi_InitIfSingleton();
+    if (pmi2_errno) PMI2U_ERR_SETANDJUMP(1, pmi2_errno, "PMIi_InitIfSingleton");
+
+    pmi2_errno = PMIi_WriteSimpleCommandStr(PMI2_fd, &cmd, GETJOBATTR_CMD, KEY_KEY, name, NULL);
+    if (pmi2_errno) PMI2U_ERR_SETANDJUMP(1, pmi2_errno, "PMIi_WriteSimpleCommandStr");
+    pmi2_errno = PMIi_ReadCommandExp(PMI2_fd, &cmd, GETJOBATTRRESP_CMD, &rc, &errmsg);
+    if (pmi2_errno) PMI2U_ERR_SETANDJUMP(1, pmi2_errno, "PMIi_ReadCommandExp");
+    PMI2U_ERR_CHKANDJUMP(rc, pmi2_errno, PMI2_ERR_OTHER, "**pmi2_getjobattr %s", errmsg ? errmsg : "unknown");
+
+    found = getvalbool(cmd.pairs, cmd.nPairs, FOUND_KEY, flag);
+    PMI2U_ERR_CHKANDJUMP(found != 1, pmi2_errno, PMI2_ERR_OTHER, "**intern");
+
+    if (*flag) {
+        found = getval(cmd.pairs, cmd.nPairs, VALUE_KEY, &kvsvalue, &kvsvallen);
+        PMI2U_ERR_CHKANDJUMP(found != 1, pmi2_errno, PMI2_ERR_OTHER, "**intern");
+
+        strncpy(value, kvsvalue, valuelen);
+    }
+
+fn_exit:
+    free(cmd.command);
+    freepairs(cmd.pairs, cmd.nPairs);
+    PMI2U_printf("[END]");
+    return pmi2_errno;
+fn_fail:
+    goto fn_exit;
+}
+
+int PMI2_Info_GetJobAttrIntArray(const char name[], int array[], int arraylen, int *outlen, int *flag)
+{
+    int pmi2_errno = PMI2_SUCCESS;
+    int found;
+    const char *kvsvalue;
+    int kvsvallen;
+    PMI2_Command cmd = {0};
+    int rc;
+    const char *errmsg;
+    int i;
+    const char *valptr;
+
+    PMI2U_printf("[BEGIN]");
+
+    pmi2_errno = PMIi_InitIfSingleton();
+    if (pmi2_errno) PMI2U_ERR_SETANDJUMP(1, pmi2_errno, "PMIi_InitIfSingleton");
+
+    pmi2_errno = PMIi_WriteSimpleCommandStr(PMI2_fd, &cmd, GETJOBATTR_CMD, KEY_KEY, name, NULL);
+    if (pmi2_errno) PMI2U_ERR_SETANDJUMP(1, pmi2_errno, "PMIi_WriteSimpleCommandStr");
+    pmi2_errno = PMIi_ReadCommandExp(PMI2_fd, &cmd, GETJOBATTRRESP_CMD, &rc, &errmsg);
+    if (pmi2_errno) PMI2U_ERR_SETANDJUMP(1, pmi2_errno, "PMIi_ReadCommandExp");
+    PMI2U_ERR_CHKANDJUMP(rc, pmi2_errno, PMI2_ERR_OTHER, "**pmi2_getjobattr %s", errmsg ? errmsg : "unknown");
+
+    found = getvalbool(cmd.pairs, cmd.nPairs, FOUND_KEY, flag);
+    PMI2U_ERR_CHKANDJUMP(found != 1, pmi2_errno, PMI2_ERR_OTHER, "**intern");
+    if (*flag) {
+        found = getval(cmd.pairs, cmd.nPairs, VALUE_KEY, &kvsvalue, &kvsvallen);
+        PMI2U_ERR_CHKANDJUMP(found != 1, pmi2_errno, PMI2_ERR_OTHER, "**intern");
+
+        valptr = kvsvalue;
+        i = 0;
+        rc = sscanf(valptr, "%d", &array[i]);
+        PMI2U_ERR_CHKANDJUMP(rc != 1, pmi2_errno, PMI2_ERR_OTHER, "**intern %s", "unable to parse intarray");
+        ++i;
+        while ((valptr = strchr(valptr, ',')) && i < arraylen) {
+            ++valptr; /* skip over the ',' */
+            rc = sscanf(valptr, "%d", &array[i]);
+            PMI2U_ERR_CHKANDJUMP(rc != 1, pmi2_errno, PMI2_ERR_OTHER, "**intern %s", "unable to parse intarray");
+            ++i;
+        }
+
+        *outlen = i;
+    }
+
+fn_exit:
+    free(cmd.command);
+    freepairs(cmd.pairs, cmd.nPairs);
+    PMI2U_printf("[END]");
+    return pmi2_errno;
+fn_fail:
+    goto fn_exit;
+}
+
+int PMI2_Nameserv_publish(const char service_name[], const PMI2U_Info *info_ptr, const char port[])
+{
+    int pmi2_errno = PMI2_SUCCESS;
+    PMI2_Command cmd = {0};
+    int rc;
+    const char *errmsg;
+
+    PMI2U_printf("[BEGIN]");
+
+    /* ignoring infokey functionality for now */
+    pmi2_errno = PMIi_WriteSimpleCommandStr(PMI2_fd, &cmd, NAMEPUBLISH_CMD,
+                                            NAME_KEY, service_name, PORT_KEY, port,
+                                            INFOKEYCOUNT_KEY, "0", NULL);
+    if (pmi2_errno) PMI2U_ERR_SETANDJUMP(1, pmi2_errno, "PMIi_WriteSimpleCommandStr");
+    pmi2_errno = PMIi_ReadCommandExp(PMI2_fd, &cmd, NAMEPUBLISHRESP_CMD, &rc, &errmsg);
+    if (pmi2_errno) PMI2U_ERR_SETANDJUMP(1, pmi2_errno, "PMIi_ReadCommandExp");
+    PMI2U_ERR_CHKANDJUMP(rc, pmi2_errno, PMI2_ERR_OTHER, "**pmi2_nameservpublish %s", errmsg ? errmsg : "unknown");
+
+
+fn_exit:
+    free(cmd.command);
+    freepairs(cmd.pairs, cmd.nPairs);
+    PMI2U_printf("[END]");
+    return pmi2_errno;
+fn_fail:
+    goto fn_exit;
+}
+
+
+int PMI2_Nameserv_lookup(const char service_name[], const PMI2U_Info *info_ptr,
+                         char port[], int portLen)
+{
+    int pmi2_errno = PMI2_SUCCESS;
+    int found;
+    int rc;
+    PMI2_Command cmd = {0};
+    int plen;
+    const char *errmsg;
+    const char *found_port;
+
+    PMI2U_printf("[BEGIN]");
+
+    /* ignoring infos for now */
+    pmi2_errno = PMIi_WriteSimpleCommandStr(PMI2_fd, &cmd, NAMELOOKUP_CMD,
+                                            NAME_KEY, service_name, INFOKEYCOUNT_KEY, "0", NULL);
+    if (pmi2_errno) PMI2U_ERR_SETANDJUMP(1, pmi2_errno, "PMIi_WriteSimpleCommandStr");
+    pmi2_errno = PMIi_ReadCommandExp(PMI2_fd, &cmd, NAMELOOKUPRESP_CMD, &rc, &errmsg);
+    if (pmi2_errno) PMI2U_ERR_SETANDJUMP(1, pmi2_errno, "PMIi_ReadCommandExp");
+    PMI2U_ERR_CHKANDJUMP(rc, pmi2_errno, PMI2_ERR_OTHER, "**pmi2_nameservlookup %s", errmsg ? errmsg : "unknown");
+
+    found = getval(cmd.pairs, cmd.nPairs, VALUE_KEY, &found_port, &plen);
+    PMI2U_ERR_CHKANDJUMP(!found, pmi2_errno, PMI2_ERR_OTHER, "**pmi2_nameservlookup %s", "not found");
+    strncpy(port, found_port, portLen);
+
+fn_exit:
+    free(cmd.command);
+    freepairs(cmd.pairs, cmd.nPairs);
+    PMI2U_printf("[END]");
+    return pmi2_errno;
+fn_fail:
+    goto fn_exit;
+}
+
+int PMI2_Nameserv_unpublish(const char service_name[],
+                            const PMI2U_Info *info_ptr)
+{
+    int pmi2_errno = PMI2_SUCCESS;
+    int found;
+    int rc;
+    PMI2_Command cmd = {0};
+    int plen;
+    const char *errmsg;
+    const char *found_port;
+
+    PMI2U_printf("[BEGIN]");
+
+    pmi2_errno = PMIi_WriteSimpleCommandStr(PMI2_fd, &cmd, NAMEUNPUBLISH_CMD,
+                                            NAME_KEY, service_name, INFOKEYCOUNT_KEY, "0", NULL);
+    if (pmi2_errno) PMI2U_ERR_SETANDJUMP(1, pmi2_errno, "PMIi_WriteSimpleCommandStr");
+    pmi2_errno = PMIi_ReadCommandExp(PMI2_fd, &cmd, NAMEUNPUBLISHRESP_CMD, &rc, &errmsg);
+    if (pmi2_errno) PMI2U_ERR_SETANDJUMP(1, pmi2_errno, "PMIi_ReadCommandExp");
+    PMI2U_ERR_CHKANDJUMP(rc, pmi2_errno, PMI2_ERR_OTHER, "**pmi2_nameservunpublish %s", errmsg ? errmsg : "unknown");
+
+fn_exit:
+    free(cmd.command);
+    freepairs(cmd.pairs, cmd.nPairs);
+    PMI2U_printf("[END]");
+    return pmi2_errno;
+fn_fail:
+    goto fn_exit;
+}
+
+/* ------------------------------------------------------------------------- */
+/* Service Routines */
+/* ------------------------------------------------------------------------- */
+
+/* ------------------------------------------------------------------------- */
+/*
+ * PMIi_ReadCommand - Reads an entire command from the PMI socket.  This
+ * routine blocks the thread until the command is read.
+ *
+ * PMIi_WriteSimpleCommand - Write a simple command to the PMI socket; this
+ * allows printf - style arguments.  This blocks the thread until the buffer
+ * has been written (for fault-tolerance, we may want to keep it around
+ * in case of PMI failure).
+ *
+ * PMIi_WaitFor - Wait for a particular PMI command request to complete.
+ */
+/* ------------------------------------------------------------------------- */
+
+/* frees all of the keyvals pointed to by a keyvalpair* array and the array iteself*/
+static void freepairs(PMI2_Keyvalpair** pairs, int npairs)
+{
+    int i;
+
+    if (!pairs)
+        return;
+
+    for (i = 0; i < npairs; ++i)
+        if (pairs[i]->isCopy) {
+            /* FIXME casts are here to suppress legitimate constness warnings */
+            free((void *)pairs[i]->key);
+            free((void *)pairs[i]->value);
+            free(pairs[i]);
+        }
+    free(pairs);
+}
+
+/* getval & friends -- these functions search the pairs list for a
+ * matching key, set val appropriately and return 1.  If no matching
+ * key is found, 0 is returned.  If the value is invalid, -1 is returned */
+
+static int getval(PMI2_Keyvalpair *const pairs[], int npairs, const char *key,  const char **value, int *vallen)
+{
+    int i;
+
+    for (i = 0; i < npairs; ++i)
+        if (strncmp(key, pairs[i]->key, PMI2_MAX_KEYLEN) == 0) {
+            *value = pairs[i]->value;
+            *vallen = pairs[i]->valueLen;
+            return 1;
+        }
+    return 0;
+}
+
+static int getvalint(PMI2_Keyvalpair *const pairs[], int npairs, const char *key, int *val)
+{
+    int found;
+    const char *value;
+    int vallen;
+    int ret;
+    /* char *endptr; */
+
+    found = getval(pairs, npairs, key, &value, &vallen);
+    if (found != 1)
+        return found;
+
+    if (vallen == 0)
+        return -1;
+
+    ret = sscanf(value, "%d", val);
+    if (ret != 1)
+        return -1;
+
+    /* *val = strtoll(value, &endptr, 0); */
+    /* if (endptr - value != vallen) */
+    /*     return -1; */
+
+    return 1;
+}
+
+static int getvalptr(PMI2_Keyvalpair *const pairs[], int npairs, const char *key, void *val)
+{
+    int found;
+    const char *value;
+    int vallen;
+    int ret;
+    void **val_ = val;
+    /* char *endptr; */
+
+    found = getval(pairs, npairs, key, &value, &vallen);
+    if (found != 1)
+        return found;
+
+    if (vallen == 0)
+        return -1;
+
+    ret = sscanf(value, "%p", val_);
+    if (ret != 1)
+        return -1;
+
+    /* *val_ = (void *)(PMI2R_Upint)strtoll(value, &endptr, 0); */
+    /* if (endptr - value != vallen) */
+    /*     return -1; */
+
+    return 1;
+}
+
+
+static int getvalbool(PMI2_Keyvalpair *const pairs[], int npairs, const char *key, int *val)
+{
+    int found;
+    const char *value;
+    int vallen;
+
+
+    found = getval(pairs, npairs, key, &value, &vallen);
+    if (found != 1)
+        return found;
+
+    if (strlen("TRUE") == vallen && !strncmp(value, "TRUE", vallen))
+        *val = 1/*TRUE*/;
+    else if (strlen("FALSE") == vallen && !strncmp(value, "FALSE", vallen))
+        *val = 0/*FALSE*/;
+    else
+        return -1;
+
+    return 1;
+}
+
+
+
+/* parse_keyval(cmdptr, len, key, val, vallen)
+   Scans through buffer specified by cmdptr looking for the first key and value.
+     IN/OUT cmdptr - IN: pointer to buffer; OUT: pointer to byte after the ';' terminating the value
+     IN/OUT len    - IN: length of buffer; OUT: length of buffer not read
+     OUT    key    - pointer to null-terminated string containing the key
+     OUT    val    - pointer to string containing the value
+     OUT    vallen - length of the value string
+
+   This function will modify the buffer passed through cmdptr to
+   insert '\0' following the key, and to replace escaped ';;' with
+   ';'.
+ */
+static int parse_keyval(char **cmdptr, int *len, char **key, char **val, int *vallen)
+{
+    int pmi2_errno = PMI2_SUCCESS;
+    char *c = *cmdptr;
+    char *d;
+
+    /*PMI2U_printf("[BEGIN]");*/
+
+    /* find key */
+    *key = c; /* key is at the start of the buffer */
+    while (*len && *c != '=') {
+        --*len;
+        ++c;
+    }
+    PMI2U_ERR_CHKANDJUMP(*len == 0, pmi2_errno, PMI2_ERR_OTHER, "**bad_keyval");
+    PMI2U_ERR_CHKANDJUMP((c - *key) > PMI2_MAX_KEYLEN, pmi2_errno, PMI2_ERR_OTHER, "**bad_keyval");
+    *c = '\0'; /* terminate the key string */
+
+    /* skip over the '=' */
+    --*len;
+    ++c;
+
+    /* find val */
+    *val = d = c; /* val is next */
+    while (*len) {
+        if (*c == ';') { /* handle escaped ';' */
+            if (*(c+1) != ';')
+                break;
+            else
+            {
+                --*len;
+                ++c;
+            }
+        }
+        --*len;
+        *(d++) = *(c++);
+    }
+    PMI2U_ERR_CHKANDJUMP(*len == 0, pmi2_errno, PMI2_ERR_OTHER, "**bad_keyval");
+    PMI2U_ERR_CHKANDJUMP((d - *val) > PMI2_MAX_VALLEN, pmi2_errno, PMI2_ERR_OTHER, "**bad_keyval");
+    *c = '\0'; /* terminate the val string */
+    *vallen = d - *val;
+
+    *cmdptr = c+1; /* skip over the ';' */
+    --*len;
+
+ fn_exit:
+    /*PMI2U_printf("[END]");*/
+    return pmi2_errno;
+ fn_fail:
+    goto fn_exit;
+}
+
+static int create_keyval(PMI2_Keyvalpair **kv, const char *key, const char *val, int vallen)
+{
+    int pmi2_errno = PMI2_SUCCESS;
+    char *key_p;
+    char *value_p;
+    PMI2U_CHKMEM_DECL(3);
+
+    /*PMI2U_printf("[BEGIN]");*/
+    /*PMI2U_printf("[BEGIN] create_keyval(%p, %s, %s, %d)", kv, key, val, vallen);*/
+
+    PMI2U_CHKMEM_MALLOC(*kv, PMI2_Keyvalpair *, sizeof(PMI2_Keyvalpair), pmi2_errno, "pair");
+
+    PMI2U_CHKMEM_MALLOC(key_p, char *, strlen(key)+1, pmi2_errno, "key");
+    strncpy(key_p, key, strlen(key));
+    key_p[strlen(key)] = '\0';
+
+    PMI2U_CHKMEM_MALLOC(value_p, char *, vallen+1, pmi2_errno, "value");
+    memcpy(value_p, val, vallen);
+    value_p[vallen] = '\0';
+
+    (*kv)->key = key_p;
+    (*kv)->value = value_p;
+    (*kv)->valueLen = vallen;
+    (*kv)->isCopy = 1/*TRUE*/;
+
+fn_exit:
+    PMI2U_CHKMEM_COMMIT();
+    /*PMI2U_printf("[END]");*/
+    return pmi2_errno;
+fn_fail:
+    PMI2U_CHKMEM_REAP();
+    goto fn_exit;
+}
+
+
+/* Note that we fill in the fields in a command that is provided.
+   We may want to share these routines with the PMI version 2 server */
+int PMIi_ReadCommand( int fd, PMI2_Command *cmd )
+{
+    int pmi2_errno = PMI2_SUCCESS;
+    char cmd_len_str[PMII_COMMANDLEN_SIZE+1];
+    int cmd_len, remaining_len, vallen = 0;
+    char *c, *cmd_buf = NULL;
+    char *key, *val = NULL;
+    ssize_t nbytes;
+    ssize_t offset;
+    int num_pairs;
+    int pair_index;
+    char *command = NULL;
+    int nPairs, i;
+    int found;
+    PMI2_Keyvalpair **pairs = NULL;
+    PMI2_Command *target_cmd;
+
+    PMI2U_printf("[BEGIN]");
+
+    memset(cmd_len_str, 0, sizeof(cmd_len_str));
+
+#ifdef MPICH_IS_THREADED
+    MPIU_THREAD_CHECK_BEGIN;
+    {
+        MPID_Thread_mutex_lock(&mutex);
+
+        while (blocked && !cmd->complete)
+            MPID_Thread_cond_wait(&cond, &mutex);
+
+        if (cmd->complete) {
+            MPID_Thread_mutex_unlock(&mutex);
+            goto fn_exit;
+        }
+
+        blocked = 1/*TRUE*/;
+        MPID_Thread_mutex_unlock(&mutex);
+    }
+    MPIU_THREAD_CHECK_END;
+#endif
+
+    do {
+
+        /* get length of cmd */
+        offset = 0;
+        do
+        {
+            do {
+                nbytes = read(fd, &cmd_len_str[offset], PMII_COMMANDLEN_SIZE - offset);
+            } while (nbytes == -1 && errno == EINTR);
+
+            PMI2U_ERR_CHKANDJUMP(nbytes <= 0, pmi2_errno, PMI2_ERR_OTHER, "**read %s", strerror(errno));
+
+            offset += nbytes;
+        }
+        while (offset < PMII_COMMANDLEN_SIZE);
+
+        cmd_len = atoi(cmd_len_str);
+
+        cmd_buf = malloc(cmd_len+1);
+        if (!cmd_buf) PMI2U_CHKMEM_SETERR(pmi2_errno, cmd_len+1, "cmd_buf");
+
+        memset(cmd_buf, 0, cmd_len+1);
+
+        /* get command */
+        offset = 0;
+        do
+        {
+            do {
+                nbytes = read(fd, &cmd_buf[offset], cmd_len - offset);
+            } while (nbytes == -1 && errno == EINTR);
+
+            PMI2U_ERR_CHKANDJUMP(nbytes <= 0, pmi2_errno, PMI2_ERR_OTHER, "**read %s", strerror(errno));
+
+            offset += nbytes;
+        }
+        while (offset < cmd_len);
+
+        PMI2U_printf("PMI received (cmdlen %d):  %s", cmd_len, cmd_buf);
+
+        /* count number of "key=val;" */
+        c = cmd_buf;
+        remaining_len = cmd_len;
+        num_pairs = 0;
+
+        while (remaining_len > 0) {
+            while (remaining_len && *c != ';') {
+                --remaining_len;
+                ++c;
+            }
+            if (*c == ';' && *(c+1) == ';') {
+                remaining_len -= 2;
+                c += 2;
+            } else {
+                ++num_pairs;
+                --remaining_len;
+                ++c;
+            }
+        }
+
+        c = cmd_buf;
+        remaining_len = cmd_len;
+        pmi2_errno = parse_keyval(&c, &remaining_len, &key, &val, &vallen);
+        if (pmi2_errno) PMI2U_ERR_POP(pmi2_errno);
+
+        PMI2U_ERR_CHKANDJUMP(strncmp(key, "cmd", PMI2_MAX_KEYLEN) != 0, pmi2_errno, PMI2_ERR_OTHER, "**bad_cmd");
+
+        command = malloc(vallen+1);
+        if (!command) PMI2U_CHKMEM_SETERR(pmi2_errno, vallen+1, "command");
+        memcpy(command, val, vallen);
+        val[vallen] = '\0';
+
+        nPairs = num_pairs-1;  /* num_pairs-1 because the first pair is the command */
+
+        pairs = malloc(sizeof(PMI2_Keyvalpair *) * nPairs);
+        if (!pairs) PMI2U_CHKMEM_SETERR(pmi2_errno, sizeof(PMI2_Keyvalpair *) * nPairs, "pairs");
+
+        pair_index = 0;
+        while (remaining_len > 0)
+        {
+            PMI2_Keyvalpair *pair;
+
+            pmi2_errno = parse_keyval(&c, &remaining_len, &key, &val, &vallen);
+            if (pmi2_errno) PMI2U_ERR_POP(pmi2_errno);
+
+            pmi2_errno = create_keyval(&pair, key, val, vallen);
+            if (pmi2_errno) PMI2U_ERR_POP(pmi2_errno);
+
+            pairs[pair_index] = pair;
+            ++pair_index;
+        }
+
+        found = getvalptr(pairs, nPairs, THRID_KEY, &target_cmd);
+        if (!found) /* if there's no thrid specified, assume it's for you */
+            target_cmd = cmd;
+        else
+            if (PMI2_debug && SEARCH_REMOVE(target_cmd) == 0) {
+                int i;
+
+                PMI2U_printf("command=%s", command);
+                for (i = 0; i < nPairs; ++i)
+                    dump_PMI2_Keyvalpair(pairs[i]);
+            }
+
+        target_cmd->command = command;
+        target_cmd->nPairs = nPairs;
+        target_cmd->pairs = pairs;
+        target_cmd->complete = 1/*TRUE*/;
+
+#ifdef MPICH_IS_THREADED
+        target_cmd->complete = 1/*TRUE*/;
+#endif
+
+        if (cmd_buf) free(cmd_buf);
+        cmd_buf = NULL;
+    } while (!cmd->complete);
+
+#ifdef MPICH_IS_THREADED
+    MPIU_THREAD_CHECK_BEGIN;
+    {
+        MPID_Thread_mutex_lock(&mutex);
+        blocked = 0/*FALSE*/;
+        MPID_Thread_cond_broadcast(&cond);
+        MPID_Thread_mutex_unlock(&mutex);
+    }
+    MPIU_THREAD_CHECK_END;
+#endif
+
+fn_exit:
+    PMI2U_printf("[END]");
+    return pmi2_errno;
+fn_fail:
+    if (cmd_buf) free(cmd_buf);
+    goto fn_exit;
+}
+
+/* PMIi_ReadCommandExp -- reads a command checks that it matches the
+ * expected command string exp, and parses the return code */
+int PMIi_ReadCommandExp( int fd, PMI2_Command *cmd, const char *exp, int* rc, const char **errmsg )
+{
+    int pmi2_errno = PMI2_SUCCESS;
+    int found, i;
+    int msglen;
+
+    PMI2U_printf("[BEGIN]");
+
+    pmi2_errno = PMIi_ReadCommand(fd, cmd);
+    if (pmi2_errno) PMI2U_ERR_POP(pmi2_errno);
+
+    PMI2U_ERR_CHKANDJUMP(strncmp(cmd->command, exp, strlen(exp)) != 0,  pmi2_errno, PMI2_ERR_OTHER, "**bad_cmd");
+
+    found = getvalint(cmd->pairs, cmd->nPairs, RC_KEY, rc);
+    PMI2U_ERR_CHKANDJUMP(found != 1, pmi2_errno, PMI2_ERR_OTHER, "**intern");
+
+    found = getval(cmd->pairs, cmd->nPairs, ERRMSG_KEY, errmsg, &msglen);
+    PMI2U_ERR_CHKANDJUMP(found == -1, pmi2_errno, PMI2_ERR_OTHER, "**intern");
+
+    if (!found) *errmsg = NULL;
+
+fn_exit:
+    PMI2U_printf("[END]");
+    return pmi2_errno;
+fn_fail:
+    goto fn_exit;
+}
+
+
+int PMIi_WriteSimpleCommand( int fd, PMI2_Command *resp, const char cmd[], PMI2_Keyvalpair *pairs[], int npairs)
+{
+    int pmi2_errno = PMI2_SUCCESS;
+    char cmdbuf[PMII_MAX_COMMAND_LEN];
+    char cmdlenbuf[PMII_COMMANDLEN_SIZE+1];
+    char *c = cmdbuf;
+    int ret;
+    int remaining_len = PMII_MAX_COMMAND_LEN;
+    int cmdlen;
+    int i;
+    ssize_t nbytes;
+    ssize_t offset;
+    int pair_index;
+
+    PMI2U_printf("[BEGIN]");
+
+    /* leave space for length field */
+    memset(c, ' ', PMII_COMMANDLEN_SIZE);
+    c += PMII_COMMANDLEN_SIZE;
+
+    PMI2U_ERR_CHKANDJUMP(strlen(cmd) > PMI2_MAX_VALLEN, pmi2_errno, PMI2_ERR_OTHER, "**cmd_too_long");
+
+    ret = snprintf(c, remaining_len, "cmd=%s;", cmd);
+    PMI2U_ERR_CHKANDJUMP(ret >= remaining_len, pmi2_errno, PMI2_ERR_OTHER, "**intern %s", "Ran out of room for command");
+    c += ret;
+    remaining_len -= ret;
+
+#ifdef MPICH_IS_THREADED
+    MPIU_THREAD_CHECK_BEGIN;
+    if (resp) {
+        ret = snprintf(c, remaining_len, "thrid=%p;", resp);
+        PMI2U_ERR_CHKANDJUMP(ret >= remaining_len, pmi2_errno, PMI2_ERR_OTHER, "**intern %s", "Ran out of room for command");
+        c += ret;
+        remaining_len -= ret;
+    }
+    MPIU_THREAD_CHECK_END;
+#endif
+
+    for (pair_index = 0; pair_index < npairs; ++pair_index) {
+        /* write key= */
+        PMI2U_ERR_CHKANDJUMP(strlen(pairs[pair_index]->key) > PMI2_MAX_KEYLEN, pmi2_errno, PMI2_ERR_OTHER, "**key_too_long");
+        ret = snprintf(c, remaining_len, "%s=", pairs[pair_index]->key);
+        PMI2U_ERR_CHKANDJUMP(ret >= remaining_len, pmi2_errno, PMI2_ERR_OTHER, "**intern %s", "Ran out of room for command");
+        c += ret;
+        remaining_len -= ret;
+
+        /* write value and escape ;'s as ;; */
+        PMI2U_ERR_CHKANDJUMP(pairs[pair_index]->valueLen > PMI2_MAX_VALLEN, pmi2_errno, PMI2_ERR_OTHER, "**val_too_long");
+        for (i = 0; i < pairs[pair_index]->valueLen; ++i) {
+            if (pairs[pair_index]->value[i] == ';') {
+                *c = ';';
+                ++c;
+                --remaining_len;
+            }
+            *c = pairs[pair_index]->value[i];
+            ++c;
+            --remaining_len;
+        }
+
+        /* append ; */
+        *c = ';';
+        ++c;
+        --remaining_len;
+    }
+
+    /* prepend the buffer length stripping off the trailing '\0' */
+    cmdlen = PMII_MAX_COMMAND_LEN - remaining_len;
+    ret = snprintf(cmdlenbuf, sizeof(cmdlenbuf), "%d", cmdlen);
+    PMI2U_ERR_CHKANDJUMP(ret >= PMII_COMMANDLEN_SIZE, pmi2_errno, PMI2_ERR_OTHER, "**intern %s", "Command length won't fit in length buffer");
+
+    memcpy(cmdbuf, cmdlenbuf, ret);
+
+    cmdbuf[cmdlen+PMII_COMMANDLEN_SIZE] = '\0'; /* silence valgrind warnings in PMI2U_printf */
+    PMI2U_printf("PMI sending: %s", cmdbuf);
+
+
+ #ifdef MPICH_IS_THREADED
+    MPIU_THREAD_CHECK_BEGIN;
+    {
+        MPID_Thread_mutex_lock(&mutex);
+
+        while (blocked)
+            MPID_Thread_cond_wait(&cond, &mutex);
+
+        blocked = 1/*TRUE*/;
+        MPID_Thread_mutex_unlock(&mutex);
+    }
+    MPIU_THREAD_CHECK_END;
+#endif
+
+    if (PMI2_debug)
+        ENQUEUE(resp);
+
+    offset = 0;
+    do {
+        do {
+            nbytes = write(fd, &cmdbuf[offset], cmdlen + PMII_COMMANDLEN_SIZE - offset);
+        } while (nbytes == -1 && errno == EINTR);
+
+        PMI2U_ERR_CHKANDJUMP(nbytes <= 0, pmi2_errno, PMI2_ERR_OTHER, "**write %s", strerror(errno));
+
+        offset += nbytes;
+    } while (offset < cmdlen + PMII_COMMANDLEN_SIZE);
+#ifdef MPICH_IS_THREADED
+    MPIU_THREAD_CHECK_BEGIN;
+    {
+        MPID_Thread_mutex_lock(&mutex);
+        blocked = 0/*FALSE*/;
+        MPID_Thread_cond_broadcast(&cond);
+        MPID_Thread_mutex_unlock(&mutex);
+    }
+    MPIU_THREAD_CHECK_END;
+#endif
+
+fn_fail:
+    goto fn_exit;
+fn_exit:
+    PMI2U_printf("[END]");
+    return pmi2_errno;
+}
+
+int PMIi_WriteSimpleCommandStr(int fd, PMI2_Command *resp, const char cmd[], ...)
+{
+    int pmi2_errno = PMI2_SUCCESS;
+    va_list ap;
+    PMI2_Keyvalpair *pairs;
+    PMI2_Keyvalpair **pairs_p;
+    int npairs;
+    int i;
+    const char *key;
+    const char *val;
+    PMI2U_CHKMEM_DECL(2);
+
+    PMI2U_printf("[BEGIN]");
+
+    npairs = 0;
+    va_start(ap, cmd);
+    while ((key = va_arg(ap, const char*))) {
+        val = va_arg(ap, const char*);
+        ++npairs;
+    }
+    va_end(ap);
+
+    /* allocates n+1 pairs in case npairs is 0, avoiding unnecessary warning logs */
+    PMI2U_CHKMEM_MALLOC(pairs, PMI2_Keyvalpair*, (sizeof(PMI2_Keyvalpair) * (npairs+1)), pmi2_errno, "pairs");
+    PMI2U_CHKMEM_MALLOC(pairs_p, PMI2_Keyvalpair**, (sizeof(PMI2_Keyvalpair*) * (npairs+1)), pmi2_errno, "pairs_p");
+
+    i = 0;
+    va_start(ap, cmd);
+    while ((key = va_arg(ap, const char *))) {
+        val = va_arg(ap, const char *);
+        pairs_p[i] = &pairs[i];
+        pairs[i].key = key;
+        pairs[i].value = val;
+        pairs[i].valueLen = strlen(val);
+        pairs[i].isCopy = 0/*FALSE*/;
+        ++i;
+    }
+    va_end(ap);
+
+    pmi2_errno = PMIi_WriteSimpleCommand(fd, resp, cmd, pairs_p, npairs);
+    if (pmi2_errno) PMI2U_ERR_POP(pmi2_errno);
+
+fn_exit:
+    PMI2U_printf("[END]");
+    PMI2U_CHKMEM_FREEALL();
+    return pmi2_errno;
+fn_fail:
+    goto fn_exit;
+}
+
+
+/*
+ * This code allows a program to contact a host/port for the PMI socket.
+ */
+#include <sys/types.h>
+#include <sys/param.h>
+
+/* sockaddr_in (Internet) */
+#include <netinet/in.h>
+/* TCP_NODELAY */
+#include <netinet/tcp.h>
+
+/* sockaddr_un (Unix) */
+#include <sys/un.h>
+
+/* defs of gethostbyname */
+#include <netdb.h>
+
+/* fcntl, F_GET/SETFL */
+#include <fcntl.h>
+
+/* This is really IP!? */
+#ifndef TCP
+#define TCP 0
+#endif
+
+/* stub for connecting to a specified host/port instead of using a
+   specified fd inherited from a parent process */
+static int PMII_Connect_to_pm( char *hostname, int portnum )
+{
+    struct hostent     *hp;
+    struct sockaddr_in sa;
+    int                fd;
+    int                optval = 1;
+    int                q_wait = 1;
+
+    hp = gethostbyname( hostname );
+    if (!hp) {
+    PMI2U_printf("Unable to get host entry for %s", hostname );
+    return -1;
+    }
+
+    memset( (void *)&sa, 0, sizeof(sa) );
+    /* POSIX might define h_addr_list only and node define h_addr */
+#ifdef HAVE_H_ADDR_LIST
+    memcpy( (void *)&sa.sin_addr, (void *)hp->h_addr_list[0], hp->h_length);
+#else
+    memcpy( (void *)&sa.sin_addr, (void *)hp->h_addr, hp->h_length);
+#endif
+    sa.sin_family = hp->h_addrtype;
+    sa.sin_port   = htons( (unsigned short) portnum );
+
+    fd = socket( AF_INET, SOCK_STREAM, TCP );
+    if (fd < 0) {
+    PMI2U_printf("Unable to get AF_INET socket" );
+    return -1;
+    }
+
+    if (setsockopt( fd, IPPROTO_TCP, TCP_NODELAY,
+            (char *)&optval, sizeof(optval) )) {
+    perror( "Error calling setsockopt:" );
+    }
+
+    /* We wait here for the connection to succeed */
+    if (connect( fd, (struct sockaddr *)&sa, sizeof(sa) ) < 0) {
+    switch (errno) {
+    case ECONNREFUSED:
+        PMI2U_printf("connect failed with connection refused" );
+        /* (close socket, get new socket, try again) */
+        if (q_wait)
+        close(fd);
+        return -1;
+
+    case EINPROGRESS: /*  (nonblocking) - select for writing. */
+        break;
+
+    case EISCONN: /*  (already connected) */
+        break;
+
+    case ETIMEDOUT: /* timed out */
+        PMI2U_printf("connect failed with timeout" );
+        return -1;
+
+    default:
+        PMI2U_printf("connect failed with errno %d", errno );
+        return -1;
+    }
+    }
+
+    return fd;
+}
+
+/* ------------------------------------------------------------------------- */
+/*
+ * Singleton Init.
+ *
+ * MPI-2 allows processes to become MPI processes and then make MPI calls,
+ * such as MPI_Comm_spawn, that require a process manager (this is different
+ * than the much simpler case of allowing MPI programs to run with an
+ * MPI_COMM_WORLD of size 1 without an mpiexec or process manager).
+ *
+ * The process starts when either the client or the process manager contacts
+ * the other.  If the client starts, it sends a singinit command and
+ * waits for the server to respond with its own singinit command.
+ * If the server start, it send a singinit command and waits for the
+ * client to respond with its own singinit command
+ *
+ * client sends singinit with these required values
+ *   pmi_version=<value of PMI_VERSION>
+ *   pmi_subversion=<value of PMI_SUBVERSION>
+ *
+ * and these optional values
+ *   stdio=[yes|no]
+ *   authtype=[none|shared|<other-to-be-defined>]
+ *   authstring=<string>
+ *
+ * server sends singinit with the same required and optional values as
+ * above.
+ *
+ * At this point, the protocol is now the same in both cases, and has the
+ * following components:
+ *
+ * server sends singinit_info with these required fields
+ *   versionok=[yes|no]
+ *   stdio=[yes|no]
+ *   kvsname=<string>
+ *
+ * The client then issues the init command (see PMII_getmaxes)
+ *
+ * cmd=init pmi_version=<val> pmi_subversion=<val>
+ *
+ * and expects to receive a
+ *
+ * cmd=response_to_init rc=0 pmi_version=<val> pmi_subversion=<val>
+ *
+ * (This is the usual init sequence).
+ *
+ */
+/* ------------------------------------------------------------------------- */
+/* This is a special routine used to re-initialize PMI when it is in
+   the singleton init case.  That is, the executable was started without
+   mpiexec, and PMI2_Init returned as if there was only one process.
+
+   Note that PMI routines should not call PMII_singinit; they should
+   call PMIi_InitIfSingleton(), which both connects to the process mangager
+   and sets up the initial KVS connection entry.
+*/
+
+static int PMII_singinit(void)
+{
+    return 0;
+}
+
+/* Promote PMI to a fully initialized version if it was started as
+   a singleton init */
+static int PMIi_InitIfSingleton(void)
+{
+    return 0;
+}
+
+static int accept_one_connection(int list_sock)
+{
+    int gotit, new_sock;
+    struct sockaddr_in from;
+    socklen_t len;
+
+    len = sizeof(from);
+    gotit = 0;
+    while ( ! gotit )
+    {
+        new_sock = accept(list_sock, (struct sockaddr *)&from, &len);
+        if (new_sock == -1)
+        {
+            if (errno == EINTR)
+                continue;   /* interrupted? If so, try again */
+            else
+            {
+                PMI2U_printf("accept failed in accept_one_connection");
+                exit (-1);
+            }
+        }
+        else gotit = 1;
+    }
+
+    return new_sock;
+}
+
+
+/* Get the FD to use for PMI operations.  If a port is used, rather than
+   a pre-established FD (i.e., via pipe), this routine will handle the
+   initial handshake.
+*/
+static int getPMIFD(void)
+{
+    int pmi2_errno = PMI2_SUCCESS;
+    char *p;
+
+    /* Set the default */
+    PMI2_fd = -1;
+
+    p = getenv("PMI_FD");
+    if (p) {
+        PMI2_fd = atoi(p);
+        goto fn_exit;
+    }
+
+    p = getenv( "PMI_PORT" );
+    if (p) {
+    int portnum;
+    char hostname[MAXHOSTNAME+1];
+    char *pn, *ph;
+
+    /* Connect to the indicated port (in format hostname:portnumber)
+       and get the fd for the socket */
+
+    /* Split p into host and port */
+    pn = p;
+    ph = hostname;
+    while (*pn && *pn != ':' && (ph - hostname) < MAXHOSTNAME) {
+        *ph++ = *pn++;
+    }
+    *ph = 0;
+
+        PMI2U_ERR_CHKANDJUMP(*pn != ':', pmi2_errno, PMI2_ERR_OTHER, "**pmi2_port %s", p);
+
+        portnum = atoi( pn+1 );
+        /* FIXME: Check for valid integer after : */
+        /* This routine only gets the fd to use to talk to
+           the process manager. The handshake below is used
+           to setup the initial values */
+        PMI2_fd = PMII_Connect_to_pm( hostname, portnum );
+        PMI2U_ERR_CHKANDJUMP(PMI2_fd < 0, pmi2_errno, PMI2_ERR_OTHER, "**connect_to_pm %s %d", hostname, portnum);
+    }
+
+    /* OK to return success for singleton init */
+
+ fn_exit:
+    return pmi2_errno;
+ fn_fail:
+    goto fn_exit;
+}
+
+/* ----------------------------------------------------------------------- */
+/*
+ * This function is used to request information from the server and check
+ * that the response uses the expected command name.  On a successful
+ * return from this routine, additional PMI2U_getval calls may be used
+ * to access information about the returned value.
+ *
+ * If checkRc is true, this routine also checks that the rc value returned
+ * was 0.  If not, it uses the "msg" value to report on the reason for
+ * the failure.
+ */
+static int GetResponse( const char request[], const char expectedCmd[],
+            int checkRc )
+{
+    int err = 0;
+
+    return err;
+}
+
+static void dump_PMI2_Keyvalpair(PMI2_Keyvalpair *kv)
+{
+    PMI2U_printf("key      = %s", kv->key);
+    PMI2U_printf("value    = %s", kv->value);
+    PMI2U_printf("valueLen = %d", kv->valueLen);
+    PMI2U_printf("isCopy   = %s", kv->isCopy ? "TRUE" : "FALSE");
+}
+
+static void dump_PMI2_Command(PMI2_Command *cmd)
+{
+    int i;
+
+    PMI2U_printf("cmd    = %s", cmd->command);
+    PMI2U_printf("nPairs = %d", cmd->nPairs);
+
+    for (i = 0; i < cmd->nPairs; ++i)
+        dump_PMI2_Keyvalpair(cmd->pairs[i]);
+}
+
+
diff --git a/contribs/pmi2/pmi2_api.h b/contribs/pmi2/pmi2_api.h
new file mode 100644
index 00000000000..e72a985ff67
--- /dev/null
+++ b/contribs/pmi2/pmi2_api.h
@@ -0,0 +1,704 @@
+/* -*- Mode: C; c-basic-offset:4 ; -*- */
+/*
+ *  (C) 2007 by Argonne National Laboratory.
+ *      See COPYRIGHT in top-level directory.
+ */
+
+#ifndef PMI2_H_INCLUDED
+#define PMI2_H_INCLUDED
+
+#ifndef USE_PMI2_API
+/*#error This header file defines the PMI2 API, but PMI2 was not selected*/
+#endif
+
+#define PMI2_MAX_KEYLEN 64
+#define PMI2_MAX_VALLEN 1024
+#define PMI2_MAX_ATTRVALUE 1024
+#define PMI2_ID_NULL -1
+
+#define PMII_COMMANDLEN_SIZE 6
+#define PMII_MAX_COMMAND_LEN (64*1024)
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+static const char FULLINIT_CMD[]          = "fullinit";
+static const char FULLINITRESP_CMD[]      = "fullinit-response";
+static const char FINALIZE_CMD[]          = "finalize";
+static const char FINALIZERESP_CMD[]      = "finalize-response";
+static const char ABORT_CMD[]             = "abort";
+static const char JOBGETID_CMD[]          = "job-getid";
+static const char JOBGETIDRESP_CMD[]      = "job-getid-response";
+static const char JOBCONNECT_CMD[]        = "job-connect";
+static const char JOBCONNECTRESP_CMD[]    = "job-connect-response";
+static const char JOBDISCONNECT_CMD[]     = "job-disconnect";
+static const char JOBDISCONNECTRESP_CMD[] = "job-disconnect-response";
+static const char KVSPUT_CMD[]            = "kvs-put";
+static const char KVSPUTRESP_CMD[]        = "kvs-put-response";
+static const char KVSFENCE_CMD[]          = "kvs-fence";
+static const char KVSFENCERESP_CMD[]      = "kvs-fence-response";
+static const char KVSGET_CMD[]            = "kvs-get";
+static const char KVSGETRESP_CMD[]        = "kvs-get-response";
+static const char GETNODEATTR_CMD[]       = "info-getnodeattr";
+static const char GETNODEATTRRESP_CMD[]   = "info-getnodeattr-response";
+static const char PUTNODEATTR_CMD[]       = "info-putnodeattr";
+static const char PUTNODEATTRRESP_CMD[]   = "info-putnodeattr-response";
+static const char GETJOBATTR_CMD[]        = "info-getjobattr";
+static const char GETJOBATTRRESP_CMD[]    = "info-getjobattr-response";
+static const char NAMEPUBLISH_CMD[]       = "name-publish";
+static const char NAMEPUBLISHRESP_CMD[]   = "name-publish-response";
+static const char NAMEUNPUBLISH_CMD[]     = "name-unpublish";
+static const char NAMEUNPUBLISHRESP_CMD[] = "name-unpublish-response";
+static const char NAMELOOKUP_CMD[]        = "name-lookup";
+static const char NAMELOOKUPRESP_CMD[]    = "name-lookup-response";
+
+static const char PMIJOBID_KEY[]          = "pmijobid";
+static const char PMIRANK_KEY[]           = "pmirank";
+static const char SRCID_KEY[]             = "srcid";
+static const char THREADED_KEY[]          = "threaded";
+static const char RC_KEY[]                = "rc";
+static const char ERRMSG_KEY[]            = "errmsg";
+static const char PMIVERSION_KEY[]        = "pmi-version";
+static const char PMISUBVER_KEY[]         = "pmi-subversion";
+static const char RANK_KEY[]              = "rank";
+static const char SIZE_KEY[]              = "size";
+static const char APPNUM_KEY[]            = "appnum";
+static const char SPAWNERJOBID_KEY[]      = "spawner-jobid";
+static const char DEBUGGED_KEY[]          = "debugged";
+static const char PMIVERBOSE_KEY[]        = "pmiverbose";
+static const char ISWORLD_KEY[]           = "isworld";
+static const char MSG_KEY[]               = "msg";
+static const char JOBID_KEY[]             = "jobid";
+static const char KVSCOPY_KEY[]           = "kvscopy";
+static const char KEY_KEY[]               = "key";
+static const char VALUE_KEY[]             = "value";
+static const char FOUND_KEY[]             = "found";
+static const char WAIT_KEY[]              = "wait";
+static const char NAME_KEY[]              = "name";
+static const char PORT_KEY[]              = "port";
+static const char THRID_KEY[]             = "thrid";
+static const char INFOKEYCOUNT_KEY[]      = "infokeycount";
+static const char INFOKEY_KEY[]           = "infokey%d";
+static const char INFOVAL_KEY[]           = "infoval%d";
+
+static const char TRUE_VAL[]              = "TRUE";
+static const char FALSE_VAL[]             = "FALSE";
+
+/* Local types */
+
+/* Parse commands are in this structure.  Fields in this structure are
+   dynamically allocated as necessary */
+typedef struct PMI2_Keyvalpair {
+    const char *key;
+    const char *value;
+    int         valueLen;  /* Length of a value (values may contain nulls, so
+                              we need this) */
+    int         isCopy;    /* The value is a copy (and will need to be freed)
+                              if this is true, otherwise,
+                              it is a null-terminated string in the original
+                              buffer */
+} PMI2_Keyvalpair;
+
+typedef struct PMI2_Command {
+    int               nPairs;   /* Number of key=value pairs */
+    char             *command;  /* Overall command buffer */
+    PMI2_Keyvalpair **pairs;    /* Array of pointers to pairs */
+    int               complete;
+} PMI2_Command;
+
+
+/*D
+PMI2_CONSTANTS - PMI2 definitions
+
+Error Codes:
++ PMI2_SUCCESS - operation completed successfully
+. PMI2_FAIL - operation failed
+. PMI2_ERR_NOMEM - input buffer not large enough
+. PMI2_ERR_INIT - PMI not initialized
+. PMI2_ERR_INVALID_ARG - invalid argument
+. PMI2_ERR_INVALID_KEY - invalid key argument
+. PMI2_ERR_INVALID_KEY_LENGTH - invalid key length argument
+. PMI2_ERR_INVALID_VAL - invalid val argument
+. PMI2_ERR_INVALID_VAL_LENGTH - invalid val length argument
+. PMI2_ERR_INVALID_LENGTH - invalid length argument
+. PMI2_ERR_INVALID_NUM_ARGS - invalid number of arguments
+. PMI2_ERR_INVALID_ARGS - invalid args argument
+. PMI2_ERR_INVALID_NUM_PARSED - invalid num_parsed length argument
+. PMI2_ERR_INVALID_KEYVALP - invalid keyvalp argument
+. PMI2_ERR_INVALID_SIZE - invalid size argument
+- PMI2_ERR_OTHER - other unspecified error
+
+D*/
+#define PMI2_SUCCESS                0
+#define PMI2_FAIL                   -1
+#define PMI2_ERR_INIT               1
+#define PMI2_ERR_NOMEM              2
+#define PMI2_ERR_INVALID_ARG        3
+#define PMI2_ERR_INVALID_KEY        4
+#define PMI2_ERR_INVALID_KEY_LENGTH 5
+#define PMI2_ERR_INVALID_VAL        6
+#define PMI2_ERR_INVALID_VAL_LENGTH 7
+#define PMI2_ERR_INVALID_LENGTH     8
+#define PMI2_ERR_INVALID_NUM_ARGS   9
+#define PMI2_ERR_INVALID_ARGS       10
+#define PMI2_ERR_INVALID_NUM_PARSED 11
+#define PMI2_ERR_INVALID_KEYVALP    12
+#define PMI2_ERR_INVALID_SIZE       13
+#define PMI2_ERR_OTHER              14
+
+/* This is here to allow spawn multiple functions to compile.  This
+   needs to be removed once those functions are fixed for pmi2 */
+/*
+typedef struct PMI_keyval_t
+{
+    char * key;
+    char * val;
+} PMI_keyval_t;
+*/
+
+/*@
+  PMI2_Connect_comm_t - connection structure used when connecting to other jobs
+
+  Fields:
+  + read - Read from a connection to the leader of the job to which
+    this process will be connecting. Returns 0 on success or an MPI
+    error code on failure.
+  . write - Write to a connection to the leader of the job to which
+    this process will be connecting. Returns 0 on success or an MPI
+    error code on failure.
+  . ctx - An anonymous pointer to data that may be used by the read
+    and write members.
+  - isMaster - Indicates which process is the "master"; may have the
+    values 1 (is the master), 0 (is not the master), or -1 (neither is
+    designated as the master). The two processes must agree on which
+    process is the master, or both must select -1 (neither is the
+    master).
+
+  Notes:
+  A typical implementation of these functions will use the read and
+  write calls on a pre-established file descriptor (fd) between the
+  two leading processes. This will be needed only if the PMI server
+  cannot access the KVS spaces of another job (this may happen, for
+  example, if each mpiexec creates the KVS spaces for the processes
+  that it manages).
+  
+@*/
+typedef struct PMI2_Connect_comm {
+    int (*read)( void *buf, int maxlen, void *ctx );
+    int (*write)( const void *buf, int len, void *ctx );
+    void *ctx;
+    int  isMaster;
+} PMI2_Connect_comm_t;
+
+
+/*S
+  MPID_Info - Structure of an MPID info
+
+  Notes:
+  There is no reference count because 'MPI_Info' values, unlike other MPI
+  objects, may be changed after they are passed to a routine without
+  changing the routine''s behavior.  In other words, any routine that uses
+  an 'MPI_Info' object must make a copy or otherwise act on any info value
+  that it needs.
+
+  A linked list is used because the typical 'MPI_Info' list will be short
+  and a simple linked list is easy to implement and to maintain.  Similarly,
+  a single structure rather than separate header and element structures are
+  defined for simplicity.  No separate thread lock is provided because
+  info routines are not performance critical; they may use the single
+  critical section lock in the 'MPIR_Process' structure when they need a
+  thread lock.
+
+  This particular form of linked list (in particular, with this particular
+  choice of the first two members) is used because it allows us to use
+  the same routines to manage this list as are used to manage the
+  list of free objects (in the file 'src/util/mem/handlemem.c').  In
+  particular, if lock-free routines for updating a linked list are
+  provided, they can be used for managing the 'MPID_Info' structure as well.
+
+  The MPI standard requires that keys can be no less that 32 characters and
+  no more than 255 characters.  There is no mandated limit on the size
+  of values.
+
+  Module:
+  Info-DS
+  S*/
+typedef struct MPID_Info {
+    int                 handle;
+    int                 pobj_mutex;
+    int                 ref_count;
+    struct MPID_Info    *next;
+    char                *key;
+    char                *value;
+} MPID_Info;
+
+#define PMI2U_Info MPID_Info
+
+/*@
+  PMI2_Init - initialize the Process Manager Interface
+
+  Output Parameter:
+  + spawned - spawned flag
+  . size - number of processes in the job
+  . rank - rank of this process in the job
+  - appnum - which executable is this on the mpiexec commandline
+  
+  Return values:
+  Returns 'MPI_SUCCESS' on success and an MPI error code on failure.
+  
+  Notes:
+  Initialize PMI for this process group. The value of spawned indicates whether
+  this process was created by 'PMI2_Spawn_multiple'.  'spawned' will be non-zero
+  iff this process group has a parent.
+
+@*/
+int PMI2_Init(int *spawned, int *size, int *rank, int *appnum);
+
+/*@
+  PMI2_Finalize - finalize the Process Manager Interface
+  
+  Return values:
+  Returns 'MPI_SUCCESS' on success and an MPI error code on failure.
+  
+  Notes:
+  Finalize PMI for this job.
+  
+@*/
+int PMI2_Finalize(void);
+
+/*@
+  PMI2_Initialized - check if PMI has been initialized
+
+  Return values:
+  Non-zero if PMI2_Initialize has been called successfully, zero otherwise.
+  
+@*/
+int PMI2_Initialized(void);
+
+/*@
+  PMI2_Abort - abort the process group associated with this process
+  
+  Input Parameters:
+  + flag - non-zero if all processes in this job should abort, zero otherwise
+  - error_msg - error message to be printed
+  
+  Return values:
+  If the abort succeeds this function will not return.  Returns an MPI
+  error code otherwise.
+
+@*/
+int PMI2_Abort(int flag, const char msg[]);
+
+/*@
+  PMI2_Spawn - spawn a new set of processes
+
+  Input Parameters:
+  + count - count of commands
+  . cmds - array of command strings
+  . argcs - size of argv arrays for each command string
+  . argvs - array of argv arrays for each command string
+  . maxprocs - array of maximum processes to spawn for each command string
+  . info_keyval_sizes - array giving the number of elements in each of the 
+    'info_keyval_vectors'
+  . info_keyval_vectors - array of keyval vector arrays
+  . preput_keyval_size - Number of elements in 'preput_keyval_vector'
+  . preput_keyval_vector - array of keyvals to be pre-put in the spawned keyval space
+  - jobIdSize - size of the buffer provided in jobId
+
+  Output Parameter:
+  + jobId - job id of the spawned processes
+  - errors - array of errors for each command
+
+  Return values:
+  Returns 'MPI_SUCCESS' on success and an MPI error code on failure.
+
+  Notes:
+  This function spawns a set of processes into a new job.  The 'count'
+  field refers to the size of the array parameters - 'cmd', 'argvs', 'maxprocs',
+  'info_keyval_sizes' and 'info_keyval_vectors'.  The 'preput_keyval_size' refers
+  to the size of the 'preput_keyval_vector' array.  The 'preput_keyval_vector'
+  contains keyval pairs that will be put in the keyval space of the newly
+  created job before the processes are started.  The 'maxprocs' array
+  specifies the desired number of processes to create for each 'cmd' string.  
+  The actual number of processes may be less than the numbers specified in
+  maxprocs.  The acceptable number of processes spawned may be controlled by
+  ``soft'' keyvals in the info arrays.  The ``soft'' option is specified by
+  mpiexec in the MPI-2 standard.  Environment variables may be passed to the
+  spawned processes through PMI implementation specific 'info_keyval' parameters.
+@*/
+int PMI2_Job_Spawn(int count, const char * cmds[],
+                   int argcs[], const char ** argvs[],
+                   const int maxprocs[],
+                   const int info_keyval_sizes[],
+                   const struct MPID_Info *info_keyval_vectors[],
+                   int preput_keyval_size,
+                   const struct MPID_Info *preput_keyval_vector[],
+                   char jobId[], int jobIdSize,
+                   int errors[]);
+
+
+/*@
+  PMI2_Job_GetId - get job id of this job 
+
+  Input parameters:
+  . jobid_size - size of buffer provided in jobid
+
+  Output parameters:
+  . jobid - the job id of this job
+  
+  Return values:
+  Returns 'MPI_SUCCESS' on success and an MPI error code on failure.
+
+@*/
+int PMI2_Job_GetId(char jobid[], int jobid_size);
+
+/*@
+  PMI2_Job_GetRank - get rank of this job
+
+  Output parameters:
+  . rank - the rank of this job
+
+  Return values:
+  Returns 'PMI2_SUCCESS' on success and an PMI error code on failure.
+
+@*/
+int PMI2_Job_GetRank(int* rank);
+
+/*@
+  PMI2_Info_GetSize - get the number of processes on the node
+
+  Output parameters:
+  . rank - the rank of this job
+
+  Return values:
+  Returns 'PMI2_SUCCESS' on success and an PMI error code on failure.
+@*/
+int PMI2_Info_GetSize(int* size);
+
+/*@
+  PMI2_Job_Connect - connect to the parallel job with ID jobid
+
+  Input parameters:
+  . jobid - job id of the job to connect to
+
+  Output parameters:
+  . conn - connection structure used to exteblish communication with
+    the remote job
+  
+  Return values:
+  Returns 'MPI_SUCCESS' on success and an MPI error code on failure.
+
+  Notes:
+  This just "registers" the other parallel job as part of a parallel
+  program, and is used in the PMI2_KVS_xxx routines (see below). This
+  is not a collective call and establishes a connection between all
+  processes that are connected to the calling processes (on the one
+  side) and that are connected to the named jobId on the other
+  side. Processes that are already connected may call this routine.
+
+@*/
+int PMI2_Job_Connect(const char jobid[], PMI2_Connect_comm_t *conn);
+
+/*@
+  PMI2_Job_Disconnect - disconnects from the job with ID jobid
+
+  Input parameters:
+  . jobid - job id of the job to connect to
+
+  Return values:
+  Returns 'MPI_SUCCESS' on success and an MPI error code on failure.
+
+@*/
+int PMI2_Job_Disconnect(const char jobid[]);
+
+/*@
+  PMI2_KVS_Put - put a key/value pair in the keyval space for this job
+
+  Input Parameters:
+  + key - key
+  - value - value
+  
+  Return values:
+  Returns 'MPI_SUCCESS' on success and an MPI error code on failure.
+
+  Notes:
+  If multiple PMI2_KVS_Put calls are made with the same key between
+  calls to PMI2_KVS_Fence, the behavior is undefined. That is, the
+  value returned by PMI2_KVS_Get for that key after the PMI2_KVS_Fence
+  is not defined.
+
+@*/
+int PMI2_KVS_Put(const char key[], const char value[]);
+/*@
+  PMI2_KVS_Fence - commit all PMI2_KVS_Put calls made before this fence
+
+  Return values:
+  Returns 'MPI_SUCCESS' on success and an MPI error code on failure.
+
+  Notes:
+  This is a collective call across the job.  It has semantics that are
+  similar to those for MPI_Win_fence and hence is most easily
+  implemented as a barrier across all of the processes in the job.
+  Specifically, all PMI2_KVS_Put operations performed by any process in
+  the same job must be visible to all processes (by using PMI2_KVS_Get)
+  after PMI2_KVS_Fence completes.  However, a PMI implementation could
+  make this a lazy operation by not waiting for all processes to enter
+  their corresponding PMI2_KVS_Fence until some process issues a
+  PMI2_KVS_Get. This might be appropriate for some wide-area
+  implementations.
+  
+@*/
+int PMI2_KVS_Fence(void);
+
+/*@
+  PMI2_KVS_Get - returns the value associated with key in the key-value
+      space associated with the job ID jobid
+
+  Input Parameters:
+  + jobid - the job id identifying the key-value space in which to look
+    for key.  If jobid is NULL, look in the key-value space of this job.
+  . src_pmi_id - the pmi id of the process which put this keypair.  This
+    is just a hint to the server.  PMI2_ID_NULL should be passed if no
+    hint is provided.
+  . key - key
+  - maxvalue - size of the buffer provided in value
+
+  Output Parameters:
+  + value - value associated with key
+  - vallen - length of the returned value, or, if the length is longer
+    than maxvalue, the negative of the required length is returned
+  
+  Return values:
+  Returns 'MPI_SUCCESS' on success and an MPI error code on failure.
+
+@*/
+int PMI2_KVS_Get(const char *jobid, int src_pmi_id, const char key[], char value [], int maxvalue, int *vallen);
+
+/*@
+  PMI2_Info_GetNodeAttr - returns the value of the attribute associated
+      with this node
+
+  Input Parameters:
+  + name - name of the node attribute
+  . valuelen - size of the buffer provided in value
+  - waitfor - if non-zero, the function will not return until the
+    attribute is available
+
+  Output Parameters:
+  + value - value of the attribute
+  - found - non-zero indicates that the attribute was found
+  
+  Return values:
+  Returns 'MPI_SUCCESS' on success and an MPI error code on failure.
+
+  Notes:
+  This provides a way, when combined with PMI2_Info_PutNodeAttr, for
+  processes on the same node to share information without requiring a
+  more general barrier across the entire job.
+
+  If waitfor is non-zero, the function will never return with found
+  set to zero.
+
+  Predefined attributes:
+  + memPoolType - If the process manager allocated a shared memory
+    pool for the MPI processes in this job and on this node, return
+    the type of that pool. Types include sysv, anonmmap and ntshm.
+  . memSYSVid - Return the SYSV memory segment id if the memory pool
+    type is sysv. Returned as a string.
+  . memAnonMMAPfd - Return the FD of the anonymous mmap segment. The
+    FD is returned as a string.
+  - memNTName - Return the name of the Windows NT shared memory
+    segment, file mapping object backed by system paging
+    file.  Returned as a string.
+
+@*/
+int PMI2_Info_GetNodeAttr(const char name[], char value[], int valuelen, int *found, int waitfor);
+
+/*@
+  PMI2_Info_GetNodeAttrIntArray - returns the value of the attribute associated
+      with this node.  The value must be an array of integers.
+
+  Input Parameters:
+  + name - name of the node attribute
+  - arraylen - number of elements in array
+
+  Output Parameters:
+  + array - value of attribute
+  . outlen - number of elements returned
+  - found - non-zero if attribute was found
+  
+  Return values:
+  Returns 'MPI_SUCCESS' on success and an MPI error code on failure.
+
+  Notes:
+  Notice that, unlike PMI2_Info_GetNodeAttr, this function does not
+  have a waitfor parameter, and will return immediately with found=0
+  if the attribute was not found.
+
+  Predefined array attribute names:
+  + localRanksCount - Return the number of local ranks that will be
+    returned by the key localRanks.
+  . localRanks - Return the ranks in MPI_COMM_WORLD of the processes
+    that are running on this node.
+  - cartCoords - Return the Cartesian coordinates of this process in
+    the underlying network topology. The coordinates are indexed from
+    zero. Value only if the Job attribute for physTopology includes
+    cartesian.
+
+@*/
+int PMI2_Info_GetNodeAttrIntArray(const char name[], int array[], int arraylen, int *outlen, int *found);
+
+/*@
+  PMI2_Info_PutNodeAttr - stores the value of the named attribute
+  associated with this node
+
+  Input Parameters:
+  + name - name of the node attribute
+  - value - the value of the attribute
+
+  Return values:
+  Returns 'MPI_SUCCESS' on success and an MPI error code on failure.
+
+  Notes:
+  For example, it might be used to share segment ids with other
+  processes on the same SMP node.
+  
+@*/
+int PMI2_Info_PutNodeAttr(const char name[], const char value[]);
+
+/*@
+  PMI2_Info_GetJobAttr - returns the value of the attribute associated
+  with this job
+
+  Input Parameters:
+  + name - name of the job attribute
+  - valuelen - size of the buffer provided in value
+
+  Output Parameters:
+  + value - value of the attribute
+  - found - non-zero indicates that the attribute was found
+  
+  Return values:
+  Returns 'MPI_SUCCESS' on success and an MPI error code on failure.
+
+@*/
+int PMI2_Info_GetJobAttr(const char name[], char value[], int valuelen, int *found);
+
+/*@
+  PMI2_Info_GetJobAttrIntArray - returns the value of the attribute associated
+      with this job.  The value must be an array of integers.
+
+  Input Parameters:
+  + name - name of the job attribute
+  - arraylen - number of elements in array
+
+  Output Parameters:
+  + array - value of attribute
+  . outlen - number of elements returned
+  - found - non-zero if attribute was found
+  
+  Return values:
+  Returns 'MPI_SUCCESS' on success and an MPI error code on failure.
+
+  Predefined array attribute names:
+
+  + universeSize - The size of the "universe" (defined for the MPI
+    attribute MPI_UNIVERSE_SIZE
+
+  . hasNameServ - The value hasNameServ is true if the PMI2 environment
+    supports the name service operations (publish, lookup, and
+    unpublish).
+    
+  . physTopology - Return the topology of the underlying network. The
+    valid topology types include cartesian, hierarchical, complete,
+    kautz, hypercube; additional types may be added as necessary. If
+    the type is hierarchical, then additional attributes may be
+    queried to determine the details of the topology. For example, a
+    typical cluster has a hierarchical physical topology, consisting
+    of two levels of complete networks - the switched Ethernet or
+    Infiniband and the SMP nodes. Other systems, such as IBM BlueGene,
+    have one level that is cartesian (and in virtual node mode, have a
+    single-level physical topology).
+
+  . physTopologyLevels - Return a string describing the topology type
+    for each level of the underlying network. Only valid if the
+    physTopology is hierarchical. The value is a comma-separated list
+    of physical topology types (except for hierarchical). The levels
+    are ordered starting at the top, with the network closest to the
+    processes last. The lower level networks may connect only a subset
+    of processes. For example, for a cartesian mesh of SMPs, the value
+    is cartesian,complete. All processes are connected by the
+    cartesian part of this, but for each complete network, only the
+    processes on the same node are connected.
+    
+  . cartDims - Return a string of comma-separated values describing
+    the dimensions of the Cartesian topology. This must be consistent
+    with the value of cartCoords that may be returned by
+    PMI2_Info_GetNodeAttrIntArray.
+
+    These job attributes are just a start, but they provide both an
+    example of the sort of external data that is available through the
+    PMI interface and how extensions can be added within the same API
+    and wire protocol. For example, adding more complex network
+    topologies requires only adding new keys, not new routines.
+    
+  . isHeterogeneous - The value isHeterogeneous is true if the
+    processes belonging to the job are running on nodes with different
+    underlying data models.
+
+@*/
+int PMI2_Info_GetJobAttrIntArray(const char name[], int array[], int arraylen, int *outlen, int *found);
+
+/*@
+  PMI2_Nameserv_publish - publish a name 
+
+  Input parameters:
+  + service_name - string representing the service being published
+  . info_ptr -
+  - port - string representing the port on which to contact the service
+
+  Return values:
+  Returns 'MPI_SUCCESS' on success and an MPI error code on failure.
+
+@*/
+int PMI2_Nameserv_publish(const char service_name[], const struct MPID_Info *info_ptr, const char port[]);
+
+/*@
+  PMI2_Nameserv_lookup - lookup a service by name
+
+  Input parameters:
+  + service_name - string representing the service being published
+  . info_ptr -
+  - portLen - size of buffer provided in port
+  
+  Output parameters:
+  . port - string representing the port on which to contact the service
+
+  Return values:
+  Returns 'MPI_SUCCESS' on success and an MPI error code on failure.
+
+@*/
+int PMI2_Nameserv_lookup(const char service_name[], const struct MPID_Info *info_ptr,
+                        char port[], int portLen);
+/*@
+  PMI2_Nameserv_unpublish - unpublish a name
+
+  Input parameters:
+  + service_name - string representing the service being unpublished
+  - info_ptr -
+
+  Return values:
+  Returns 'MPI_SUCCESS' on success and an MPI error code on failure.
+
+@*/
+int PMI2_Nameserv_unpublish(const char service_name[], 
+                           const struct MPID_Info *info_ptr);
+
+
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* PMI2_H_INCLUDED */
diff --git a/contribs/pmi2/pmi2_util.c b/contribs/pmi2/pmi2_util.c
new file mode 100644
index 00000000000..62c1caa9055
--- /dev/null
+++ b/contribs/pmi2/pmi2_util.c
@@ -0,0 +1,223 @@
+/* -*- Mode: C; c-basic-offset:4 ; -*- */
+/*
+ *  (C) 2001 by Argonne National Laboratory.
+ *      See COPYRIGHT in top-level directory.
+ */
+
+/* Allow fprintf to logfile */
+/* style: allow:fprintf:1 sig:0 */
+
+/* Utility functions associated with PMI implementation, but not part of
+ the PMI interface itself.  Reading and writing on pipes, signals, and parsing
+ key=value messages
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+
+/* #include "orte/mca/errmgr/errmgr.h" */
+#include "pmi2_util.h"
+
+#define MAXVALLEN 1024
+#define MAXKEYLEN   32
+
+/* These are not the keyvals in the keyval space that is part of the
+ PMI specification.
+ They are just part of this implementation's internal utilities.
+ */
+struct PMI2U_keyval_pairs {
+    char key[MAXKEYLEN];
+    char value[MAXVALLEN];
+};
+static struct PMI2U_keyval_pairs PMI2U_keyval_tab[64] = { { { 0 }, { 0 } } };
+static int PMI2U_keyval_tab_idx = 0;
+
+/* This is used to prepend printed output.  Set the initial value to
+ "unset" */
+static char PMI2U_print_id[PMI2_IDSIZE] = "unset";
+
+void PMI2U_Set_rank(int PMI_rank) {
+    snprintf(PMI2U_print_id, PMI2_IDSIZE, "cli_%d", PMI_rank);
+}
+void PMI2U_SetServer(void) {
+    strncpy(PMI2U_print_id, "server", PMI2_IDSIZE);
+}
+
+#define MAX_READLINE 1024
+/*
+ * Return the next newline-terminated string of maximum length maxlen.
+ * This is a buffered version, and reads from fd as necessary.  A
+ */
+int PMI2U_readline(int fd, char *buf, int maxlen) {
+    static char readbuf[MAX_READLINE];
+    static char *nextChar = 0, *lastChar = 0; /* lastChar is really one past
+     last char */
+    static int lastErrno = 0;
+    static int lastfd = -1;
+    int curlen, n;
+    char *p, ch;
+
+    /* Note: On the client side, only one thread at a time should
+     be calling this, and there should only be a single fd.
+     Server side code should not use this routine (see the
+     replacement version in src/pm/util/pmiserv.c) */
+    /*PMI2U_Assert(nextChar == lastChar || fd == lastfd);*/
+
+    p = buf;
+    curlen = 1; /* Make room for the null */
+    while (curlen < maxlen) {
+        if (nextChar == lastChar) {
+            lastfd = fd;
+            do {
+                n = read(fd, readbuf, sizeof(readbuf) - 1);
+            } while (n == -1 && errno == EINTR);
+            if (n == 0) {
+                /* EOF */
+                break;
+            } else if (n < 0) {
+                /* Error.  Return a negative value if there is no
+                 data.  Save the errno in case we need to return it
+                 later. */
+                lastErrno = errno;
+                if (curlen == 1) {
+                    curlen = 0;
+                }
+                break;
+            }
+            nextChar = readbuf;
+            lastChar = readbuf + n;
+            /* Add a null at the end just to make it easier to print
+             the read buffer */
+            readbuf[n] = 0;
+            /* FIXME: Make this an optional output */
+            /* printf( "Readline %s\n", readbuf ); */
+        }
+
+        ch = *nextChar++;
+        *p++ = ch;
+        curlen++;
+        if (ch == '\n')
+            break;
+    }
+
+    /* We null terminate the string for convenience in printing */
+    *p = 0;
+
+    PMI2U_printf("PMI received: %s", buf);
+
+    /* Return the number of characters, not counting the null */
+    return curlen - 1;
+}
+
+int PMI2U_writeline(int fd, char *buf) {
+    int size = strlen(buf), n;
+
+    if (buf[size - 1] != '\n') /* error:  no newline at end */
+        PMI2U_printf("write_line: message string doesn't end in newline: :%s:", buf);
+    else {
+        PMI2U_printf("PMI sending: %s", buf);
+
+        do {
+            n = write(fd, buf, size);
+        } while (n == -1 && errno == EINTR);
+
+        if (n < 0) {
+            PMI2U_printf("write_line error; fd=%d buf=:%s:", fd, buf);
+            ORTE_PMI_ERROR(-1, "system msg for write_line failure");
+            return (-1);
+        }
+        if (n < size)
+            PMI2U_printf("write_line failed to write entire message");
+    }
+    return 0;
+}
+
+/*
+ * Given an input string st, parse it into internal storage that can be
+ * queried by routines such as PMI2U_getval.
+ */
+int PMI2U_parse_keyvals(char *st) {
+    char *p, *keystart, *valstart;
+    int offset;
+
+    if (!st)
+        return (-1);
+
+    PMI2U_keyval_tab_idx = 0;
+    p = st;
+    while (1) {
+        while (*p == ' ')
+            p++;
+        /* got non-blank */
+        if (*p == '=') {
+            PMI2U_printf("PMI2U_parse_keyvals:  unexpected = at character %ld in %s", p - st, st);
+            return (-1);
+        }
+        if (*p == '\n' || *p == '\0')
+            return (0); /* normal exit */
+        /* got normal character */
+        keystart = p; /* remember where key started */
+        while (*p != ' ' && *p != '=' && *p != '\n' && *p != '\0')
+            p++;
+        if (*p == ' ' || *p == '\n' || *p == '\0') {
+            PMI2U_printf("PMI2U_parse_keyvals: unexpected key delimiter at character %ld in %s", p - st, st);
+            return (-1);
+        }
+        /* Null terminate the key */
+        *p = 0;
+        /* store key */
+        strncpy(PMI2U_keyval_tab[PMI2U_keyval_tab_idx].key, keystart,
+                MAXKEYLEN);
+        PMI2U_keyval_tab[PMI2U_keyval_tab_idx].key[MAXKEYLEN-1] = '\0';
+        valstart = ++p; /* start of value */
+        while (*p != ' ' && *p != '\n' && *p != '\0')
+            p++;
+        /* store value */
+        strncpy(PMI2U_keyval_tab[PMI2U_keyval_tab_idx].value, valstart,
+                MAXVALLEN);
+        offset = p - valstart;
+        /* When compiled with -fPIC, the pgcc compiler generates incorrect
+         code if "p - valstart" is used instead of using the
+         intermediate offset */
+        PMI2U_keyval_tab[PMI2U_keyval_tab_idx].value[offset] = '\0';
+        PMI2U_keyval_tab_idx++;
+        if (*p == ' ')
+            continue;
+        if (*p == '\n' || *p == '\0')
+            return (0); /* value has been set to empty */
+    }
+}
+
+void PMI2U_dump_keyvals(void) {
+    int i;
+    for (i = 0; i < PMI2U_keyval_tab_idx; i++)
+        PMI2U_printf("  %s=%s", PMI2U_keyval_tab[i].key, PMI2U_keyval_tab[i].value);
+}
+
+char *PMI2U_getval(const char *keystr, char *valstr, int vallen) {
+    int i, rc;
+
+    for (i = 0; i < PMI2U_keyval_tab_idx; i++) {
+        if (strcmp(keystr, PMI2U_keyval_tab[i].key) == 0) {
+            rc = strncpy(valstr, PMI2U_keyval_tab[i].value, vallen);
+            PMI2U_keyval_tab[i].value[vallen-1] = '\0';
+            return valstr;
+        }
+    }
+    valstr[0] = '\0';
+    return NULL ;
+}
+
+void PMI2U_chgval(const char *keystr, char *valstr) {
+    int i;
+
+    for (i = 0; i < PMI2U_keyval_tab_idx; i++) {
+        if (strcmp(keystr, PMI2U_keyval_tab[i].key) == 0) {
+            strncpy(PMI2U_keyval_tab[i].value, valstr, MAXVALLEN);
+            PMI2U_keyval_tab[i].value[MAXVALLEN - 1] = '\0';
+        }
+    }
+}
diff --git a/contribs/pmi2/pmi2_util.h b/contribs/pmi2/pmi2_util.h
new file mode 100644
index 00000000000..01be6a7e4e0
--- /dev/null
+++ b/contribs/pmi2/pmi2_util.h
@@ -0,0 +1,118 @@
+/* -*- Mode: C; c-basic-offset:4 ; -*- */
+/*
+ *  (C) 2007 by Argonne National Laboratory.
+ *      See COPYRIGHT in top-level directory.
+ */
+
+#ifndef PMI2UTIL_H_INCLUDED
+#define PMI2UTIL_H_INCLUDED
+
+/* #include "orte/mca/errmgr/errmgr.h" */
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef DEBUG
+#define opal_output(level, x...) do{printf(x);}while(0)
+#else
+#define opal_output(level, x...)
+#endif
+#define ORTE_PMI_ERROR(err, msg) do{printf(msg); exit(err);}while(0)
+
+/* maximum sizes for arrays */
+#define PMI2_MAXLINE 1024
+#define PMI2_IDSIZE    32
+
+#define TRUE  1
+#define FALSE 0
+
+#ifdef HAVE__FUNCTION__
+#define PMI2U_FUNC __FUNCTION__
+#elif defined(HAVE_CAP__FUNC__)
+#define PMI2U_FUNC __FUNC__
+#elif defined(HAVE__FUNC__)
+#define PMI2U_FUNC __func__
+#else
+#define PMI2U_FUNC __FILE__
+#endif
+
+#if (1)
+    #define PMI2U_printf(x...) do { \
+        char logstr[1024]; \
+        snprintf(logstr, 1024, x); \
+        opal_output(0, "[%s (%d): %s] %s\n", \
+            __FILE__, __LINE__, __FUNCTION__, logstr); \
+} while (0)
+#else
+    #define PMI2U_printf(x...)
+#endif
+
+#define PMI2U_Assert(a_) do { \
+    if (!(a_)) { \
+        PMI2U_printf("ASSERT( %s )", #a_); \
+    } \
+} while (0)
+
+#define PMI2U_ERR_POP(err) do { \
+    pmi2_errno = err; \
+    PMI2U_printf("err. %d", pmi2_errno); \
+    goto fn_fail; \
+} while (0)
+
+#define PMI2U_ERR_SETANDJUMP(err, class, x...) do { \
+    char errstr[1024]; \
+    snprintf(errstr, 1024, x); \
+    PMI2U_printf("err. %s", errstr);\
+    pmi2_errno = class; \
+    goto fn_fail; \
+} while (0)
+
+#define PMI2U_ERR_CHKANDJUMP(cond, err, class, x...) do { \
+    if (cond) PMI2U_ERR_SETANDJUMP(err, class, x); \
+} while (0)
+
+#define PMI2U_CHKMEM_SETERR(rc_, nbytes_, name_) do { \
+    PMI2U_printf("ERROR: memory allocation of %lu bytes failed for %s", \
+            (size_t)nbytes_, name_); \
+    rc_ = PMI2_ERR_NOMEM; \
+    goto fn_exit; \
+} while(0)
+
+/* Persistent memory that we may want to recover if something goes wrong */
+#define PMI2U_CHKMEM_DECL(n_) \
+    void* pmi2u_chkmem_stk_[n_] = {0}; \
+    int pmi2u_chkmem_stk_sp_= 0; \
+    const int pmi2u_chkmem_stk_sz_ = n_
+
+#define PMI2U_CHKMEM_REAP() \
+    while (pmi2u_chkmem_stk_sp_ > 0) { \
+        free ((void*)( pmi2u_chkmem_stk_[--pmi2u_chkmem_stk_sp_] )); \
+    }
+
+#define PMI2U_CHKMEM_COMMIT() pmi2u_chkmem_stk_sp_ = 0
+
+#define PMI2U_CHKMEM_MALLOC(pointer_,type_,nbytes_,rc_,name_) do { \
+    pointer_ = (type_)malloc(nbytes_); \
+    if (pointer_ && (pmi2u_chkmem_stk_sp_< pmi2u_chkmem_stk_sz_)) { \
+        pmi2u_chkmem_stk_[pmi2u_chkmem_stk_sp_++] = pointer_; \
+    } else { \
+        PMI2U_CHKMEM_SETERR(rc_,nbytes_,name_); \
+        goto fn_fail; \
+    } \
+} while(0)
+
+#define PMI2U_CHKMEM_FREEALL() \
+    while (pmi2u_chkmem_stk_sp_ > 0) { \
+        free ((void*)( pmi2u_chkmem_stk_[--pmi2u_chkmem_stk_sp_] )); \
+}
+
+/* prototypes for PMIU routines */
+void PMI2U_Set_rank( int PMI_rank );
+void PMI2U_SetServer( void );
+int  PMI2U_readline( int fd, char *buf, int max );
+int  PMI2U_writeline( int fd, char *buf );
+int  PMI2U_parse_keyvals( char *st );
+void PMI2U_dump_keyvals( void );
+char *PMI2U_getval( const char *keystr, char *valstr, int vallen );
+void PMI2U_chgval( const char *keystr, char *valstr );
+
+#endif /* PMI2UTIL_H_INCLUDED */
diff --git a/contribs/pmi2/testpmi2.c b/contribs/pmi2/testpmi2.c
new file mode 100644
index 00000000000..ba2e1e49dc9
--- /dev/null
+++ b/contribs/pmi2/testpmi2.c
@@ -0,0 +1,12 @@
+#include <stdio.h>
+#include <pmi2_api.h>
+
+
+int main() {
+  int spawned, size, rank, appnum;
+  int ret;
+  ret = PMI2_Init(&spawned, &size, &rank, &appnum);
+  printf("spawned=%d, size=%d, rank=%d, appnum=%d\n", spawned, size, rank, appnum);
+  PMI2_Finalize();
+  return 0;
+}
diff --git a/contribs/pmi2/testpmi2_put.c b/contribs/pmi2/testpmi2_put.c
new file mode 100644
index 00000000000..52bf77155f0
--- /dev/null
+++ b/contribs/pmi2/testpmi2_put.c
@@ -0,0 +1,40 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <pmi2_api.h>
+
+
+int main() {
+  int spawned, size, rank, appnum;
+  int ret;
+  ret = PMI2_Init(&spawned, &size, &rank, &appnum);
+  if (ret != PMI2_SUCCESS) {perror("PMI2_Init failed"); return 1;}
+
+  char jobid[50];
+  PMI2_Job_GetId(jobid, sizeof(jobid));
+  printf("spawned=%d, size=%d, rank=%d, appnum=%d, jobid=%s\n",
+         spawned, size, rank, appnum, jobid);
+  fflush(stdout);
+
+  PMI2_KVS_Fence();
+
+  /* broadcast msg=42 from proc 0 */
+  int msg = 0;
+  char val[20] = "0\n";
+  if (rank == 0) {
+    msg = 42;
+    snprintf(val, sizeof(val), "%d\n", msg);
+    PMI2_KVS_Put("msg", val);
+    printf("%d> send %d\n", rank, msg);
+    fflush(stdout);
+  };
+  PMI2_KVS_Fence();
+  int len = 0;
+  PMI2_KVS_Get(jobid, PMI2_ID_NULL, "msg", val, sizeof(val), &len);
+  msg = atoi(val);
+  printf("%d> got %d\n", rank, msg);
+  fflush(stdout);
+
+
+  PMI2_Finalize();
+  return 0;
+}
-- 
GitLab