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