From 8d5ee42710d0a3a5f1f3b9c9474e8a668b76aa46 Mon Sep 17 00:00:00 2001 From: Jim Garlick Date: Tue, 29 Sep 2015 16:33:33 -0700 Subject: [PATCH] libpmi-client: add pmi client library Flux will use this library to bootstrap from a PM, including itself. We will also use this library to test our PMI implementation. Initially there are two embeded implementations: "dlopen" - this implementation dlopens libpmi.so. Functions that are undefined will fail gracefully wtih a PMI_FAIL return code. "simple" - this implements the simple v1 PMI wire protocol used by mpich and hydra. It is sufficient to allow hydra to launch Flux, but otherwise is very rigid about what it expects on the wire, and is missing a few functions that could be implemented. The pmi client object can be initialized to explicitly use one of these implementations with parameters such as a fully qualified path to a library to dlopen, or pmi_create_guess() can be called to pick one based on the environment. --- configure.ac | 1 + src/common/Makefile.am | 3 +- src/common/libpmi-client/Makefile.am | 20 ++ src/common/libpmi-client/pmi-client.h | 132 +++++++++ src/common/libpmi-client/pmi-dlopen.c | 374 +++++++++++++++++++++++++ src/common/libpmi-client/pmi-impl.h | 66 +++++ src/common/libpmi-client/pmi-simple.c | 384 ++++++++++++++++++++++++++ src/common/libpmi-client/pmi.c | 361 ++++++++++++++++++++++++ 8 files changed, 1340 insertions(+), 1 deletion(-) create mode 100644 src/common/libpmi-client/Makefile.am create mode 100644 src/common/libpmi-client/pmi-client.h create mode 100644 src/common/libpmi-client/pmi-dlopen.c create mode 100644 src/common/libpmi-client/pmi-impl.h create mode 100644 src/common/libpmi-client/pmi-simple.c create mode 100644 src/common/libpmi-client/pmi.c diff --git a/configure.ac b/configure.ac index 9ce74b2e71ed..89e5ed3a7576 100644 --- a/configure.ac +++ b/configure.ac @@ -152,6 +152,7 @@ AC_CONFIG_FILES( \ src/common/liblsd/Makefile \ src/common/libutil/Makefile \ src/common/libev/Makefile \ + src/common/libpmi-client/Makefile \ src/common/libflux/Makefile \ src/common/libcompat/Makefile \ src/lib/Makefile \ diff --git a/src/common/Makefile.am b/src/common/Makefile.am index 828a09587770..fa720348b04b 100644 --- a/src/common/Makefile.am +++ b/src/common/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = libtap libev liblsd libutil libflux libcompat +SUBDIRS = libtap libev libpmi-client liblsd libutil libflux libcompat AM_CFLAGS = @GCCWARN@ $(CODE_COVERAGE_CFLAGS) AM_LDFLAGS = $(CODE_COVERAGE_LDFLAGS) @@ -12,6 +12,7 @@ libflux_internal_la_LIBADD = \ $(builddir)/liblsd/liblsd.la \ $(builddir)/libutil/libutil.la \ $(builddir)/libev/libev.la \ + $(builddir)/libpmi-client/libpmi-client.la \ $(builddir)/libcompat/libcompat.la \ $(LIBMUNGE) $(JSON_LIBS) $(ZMQ_LIBS) $(LIBPTHREAD) $(LIBUTIL) \ $(LIBDL) -lrt diff --git a/src/common/libpmi-client/Makefile.am b/src/common/libpmi-client/Makefile.am new file mode 100644 index 000000000000..e6f550531a89 --- /dev/null +++ b/src/common/libpmi-client/Makefile.am @@ -0,0 +1,20 @@ +AM_CFLAGS = \ + @GCCWARN@ \ + $(CODE_COVERAGE_CFLAGS) + +AM_LDFLAGS = \ + $(CODE_COVERAGE_LDFLAGS) + +AM_CPPFLAGS = \ + $(JSON_CFLAGS) $(ZMQ_CFLAGS) \ + -Wno-strict-aliasing \ + -I$(top_srcdir) -I$(top_srcdir)/src/include + +noinst_LTLIBRARIES = libpmi-client.la + +libpmi_client_la_SOURCES = \ + pmi-client.h \ + pmi-impl.h \ + pmi.c \ + pmi-dlopen.c \ + pmi-simple.c diff --git a/src/common/libpmi-client/pmi-client.h b/src/common/libpmi-client/pmi-client.h new file mode 100644 index 000000000000..0f8374f74f90 --- /dev/null +++ b/src/common/libpmi-client/pmi-client.h @@ -0,0 +1,132 @@ +#ifndef _FLUX_CORE_PMI_CLIENT_H +#define _FLUX_CORE_PMI_CLIENT_H + +#ifndef PMI_SUCCESS +#define PMI_SUCCESS 0 +#endif +#ifndef PMI_FAIL +#define PMI_FAIL -1 +#endif +#ifndef PMI_ERR_INIT +#define PMI_ERR_INIT 1 +#endif +#ifndef PMI_ERR_NOMEM +#define PMI_ERR_NOMEM 2 +#endif +#ifndef PMI_ERR_INVALID_ARG +#define PMI_ERR_INVALID_ARG 3 +#endif +#ifndef PMI_ERR_INVALID_KEY +#define PMI_ERR_INVALID_KEY 4 +#endif +#ifndef PMI_ERR_INVALID_KEY_LENGTH +#define PMI_ERR_INVALID_KEY_LENGTH 5 +#endif +#ifndef PMI_ERR_INVALID_VAL +#define PMI_ERR_INVALID_VAL 6 +#endif +#ifndef PMI_ERR_INVALID_VAL_LENGTH +#define PMI_ERR_INVALID_VAL_LENGTH 7 +#endif +#ifndef PMI_ERR_INVALID_LENGTH +#define PMI_ERR_INVALID_LENGTH 8 +#endif +#ifndef PMI_ERR_INVALID_NUM_ARGS +#define PMI_ERR_INVALID_NUM_ARGS 9 +#endif +#ifndef PMI_ERR_INVALID_ARGS +#define PMI_ERR_INVALID_ARGS 10 +#endif +#ifndef PMI_ERR_INVALID_NUM_PARSED +#define PMI_ERR_INVALID_NUM_PARSED 11 +#endif +#ifndef PMI_ERR_INVALID_KEYVALP +#define PMI_ERR_INVALID_KEYVALP 12 +#endif +#ifndef PMI_ERR_INVALID_SIZE +#define PMI_ERR_INVALID_SIZE 13 +#endif + +typedef void (*pmi_free_f)(void *impl); + +typedef struct pmi_struct pmi_t; + +/* Create/destroy pmi_t class. + */ +void pmi_destroy (pmi_t *pmi); + +pmi_t *pmi_create_dlopen (const char *filename); +pmi_t *pmi_create_simple (int fd, int rank, int size); +pmi_t *pmi_create_guess (void); + +const char *pmi_strerror (int errnum); + + +/* Functions below match the canonical PMIv1 ABI, except for + * the pmi_t first argument and the lower case function names. + */ + +typedef struct { + const char *key; + char *val; +} pmi_keyval_t; + +int pmi_init (pmi_t *pmi, int *spawned); +int pmi_initialized (pmi_t *pmi, int *initialized); +int pmi_finalize (pmi_t *pmi); +int pmi_get_size (pmi_t *pmi, int *size); +int pmi_get_rank (pmi_t *pmi, int *rank); +int pmi_get_universe_size (pmi_t *pmi, int *size); +int pmi_get_appnum (pmi_t *pmi, int *appnum); +int pmi_publish_name (pmi_t *pmi, const char *service_name, const char *port); +int pmi_unpublish_name (pmi_t *pmi, const char *service_name); +int pmi_lookup_name (pmi_t *pmi, const char *service_name, char *port); +int pmi_barrier (pmi_t *pmi); +int pmi_abort (pmi_t *pmi, int exit_code, const char *error_msg); +int pmi_kvs_get_my_name (pmi_t *pmi, char *kvsname, int length); +int pmi_kvs_get_name_length_max (pmi_t *pmi, int *length); +int pmi_kvs_get_key_length_max (pmi_t *pmi, int *length); +int pmi_kvs_get_value_length_max (pmi_t *pmi, int *length); +int pmi_kvs_put (pmi_t *pmi, + const char *kvsname, const char *key, const char *value); +int pmi_kvs_commit (pmi_t *pmi, const char *kvsname); +int pmi_kvs_get (pmi_t *pmi, + const char *kvsname, const char *key, char *value, int len); +int pmi_spawn_multiple (pmi_t *pmi, + int count, + const char *cmds[], + const char **argvs[], + const int maxprocs[], + const int info_keyval_sizesp[], + const pmi_keyval_t *info_keyval_vectors[], + int preput_keyval_size, + const pmi_keyval_t preput_keyval_vector[], + int errors[]); + +/* These functions were eventually deprecated in MPICH. + * Be careful using these - if unimplemented PMI_FAIL is returned. + */ + +int pmi_get_id (pmi_t *pmi, char *id_str, int length); +int pmi_get_kvs_domain_id (pmi_t *pmi, char *id_str, int length); +int pmi_get_id_length_max (pmi_t *pmi, int *length); +int pmi_get_clique_size (pmi_t *pmi, int *size); +int pmi_get_clique_ranks (pmi_t *pmi, int *ranks, int length); +int pmi_kvs_create (pmi_t *pmi, char *kvsname, int length); +int pmi_kvs_destroy (pmi_t *pmi, const char *kvsname); +int pmi_kvs_iter_first (pmi_t *pmi, const char *kvsname, + char *key, int key_len, char *val, int val_len); +int pmi_kvs_iter_next (pmi_t *pmi, const char *kvsname, + char *key, int key_len, char *val, int val_len); +int pmi_parse_option (pmi_t *pmi, int num_args, char *args[], + int *num_parsed, pmi_keyval_t **keyvalp, int *size); +int pmi_args_to_keyval (pmi_t *pmi, int *argcp, char *((*argvp)[]), + pmi_keyval_t **keyvalp, int *size); +int pmi_free_keyvals (pmi_t *pmi, pmi_keyval_t keyvalp[], int size); +int pmi_get_options (pmi_t *pmi, char *str, int length); + +#endif /* ! _FLUX_CORE_PMI_CLIENT_H */ + +/* + * vi:tabstop=4 shiftwidth=4 expandtab + */ diff --git a/src/common/libpmi-client/pmi-dlopen.c b/src/common/libpmi-client/pmi-dlopen.c new file mode 100644 index 000000000000..fe6fb5a03255 --- /dev/null +++ b/src/common/libpmi-client/pmi-dlopen.c @@ -0,0 +1,374 @@ +/*****************************************************************************\ + * Copyright (c) 2014 Lawrence Livermore National Security, LLC. Produced at + * the Lawrence Livermore National Laboratory (cf, AUTHORS, DISCLAIMER.LLNS). + * LLNL-CODE-658032 All rights reserved. + * + * This file is part of the Flux resource manager framework. + * For details, see https://github.com/flux-framework. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the license, or (at your option) + * any later version. + * + * Flux is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the IMPLIED WARRANTY OF MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the terms and conditions of the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * See also: http://www.gnu.org/licenses/ +\*****************************************************************************/ + +#if HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include +#include + +#include "pmi-client.h" +#include "pmi-impl.h" + +#include "src/common/libutil/log.h" +#include "src/common/libutil/xzmalloc.h" + +struct dlopen_impl { + void *dso; +}; + +void destroy_dlopen (void *impl) +{ + struct dlopen_impl *d = impl; + if (d) { + if (d->dso) + dlclose (d->dso); + free (d); + } +} + +static int dlopen_init (void *impl, int *spawned) +{ + struct dlopen_impl *d = impl; + int (*f)(int *) = dlsym (d->dso, "PMI_Init"); + return f ? f (spawned) : PMI_FAIL; +} + +static int dlopen_initialized (void *impl, int *initialized) +{ + struct dlopen_impl *d = impl; + int (*f)(int *) = dlsym (d->dso, "PMI_Initialized"); + return f ? f (initialized) : PMI_FAIL; +} + +static int dlopen_finalize (void *impl) +{ + struct dlopen_impl *d = impl; + int (*f)(void) = dlsym (d->dso, "PMI_Finalize"); + return f ? f () : PMI_FAIL; +} + +static int dlopen_get_size (void *impl, int *size) +{ + struct dlopen_impl *d = impl; + int (*f)(int *) = dlsym (d->dso, "PMI_Get_size"); + return f ? f (size) : PMI_FAIL; +} + +static int dlopen_get_rank (void *impl, int *rank) +{ + struct dlopen_impl *d = impl; + int (*f)(int *) = dlsym (d->dso, "PMI_Get_rank"); + return f ? f (rank) : PMI_FAIL; +} + +static int dlopen_get_universe_size (void *impl, int *size) +{ + struct dlopen_impl *d = impl; + int (*f)(int *) = dlsym (d->dso, "PMI_Get_universe_size"); + return f ? f (size) : PMI_FAIL; +} + +static int dlopen_get_appnum (void *impl, int *appnum) +{ + struct dlopen_impl *d = impl; + int (*f)(int *) = dlsym (d->dso, "PMI_Get_appnum"); + return f ? f (appnum) : PMI_FAIL; +} + +static int dlopen_publish_name (void *impl, + const char *service_name, const char *port) +{ + struct dlopen_impl *d = impl; + int (*f)(const char *, const char *) = dlsym (d->dso, + "PMI_Publish_name"); + return f ? f (service_name, port) : PMI_FAIL; +} + +static int dlopen_unpublish_name (void *impl, + const char *service_name) +{ + struct dlopen_impl *d = impl; + int (*f)(const char *) = dlsym (d->dso, "PMI_Unpublish_name"); + return f ? f (service_name) : PMI_FAIL; +} + +static int dlopen_lookup_name (void *impl, + const char *service_name, char *port) +{ + struct dlopen_impl *d = impl; + int (*f)(const char *, char *) = dlsym (d->dso, "PMI_Lookup_name"); + return f ? f (service_name, port) : PMI_FAIL; +} + +static int dlopen_barrier (void *impl) +{ + struct dlopen_impl *d = impl; + int (*f)(void) = dlsym (d->dso, "PMI_Barrier"); + return f ? f () : PMI_FAIL; +} + +static int dlopen_abort (void *impl, + int exit_code, const char *error_msg) +{ + struct dlopen_impl *d = impl; + int (*f)(int, const char *) = dlsym (d->dso, "PMI_Abort"); + return f ? f (exit_code, error_msg) : PMI_FAIL; +} + +static int dlopen_kvs_get_my_name (void *impl, + char *kvsname, int length) +{ + struct dlopen_impl *d = impl; + int (*f)(char *, int) = dlsym (d->dso, "PMI_KVS_Get_my_name"); + return f ? f (kvsname, length) : PMI_FAIL; +} + +static int dlopen_kvs_get_name_length_max (void *impl, int *length) +{ + struct dlopen_impl *d = impl; + int (*f)(int *) = dlsym (d->dso, "PMI_KVS_Get_name_length_max"); + return f ? f (length) : PMI_FAIL; +} + +static int dlopen_kvs_get_key_length_max (void *impl, int *length) +{ + struct dlopen_impl *d = impl; + int (*f)(int *) = dlsym (d->dso, "PMI_KVS_Get_key_length_max"); + return f ? f (length) : PMI_FAIL; +} + +static int dlopen_kvs_get_value_length_max (void *impl, int *length) +{ + struct dlopen_impl *d = impl; + int (*f)(int *) = dlsym (d->dso, "PMI_KVS_Get_value_length_max"); + return f ? f (length) : PMI_FAIL; +} + +static int dlopen_kvs_put (void *impl, + const char *kvsname, const char *key, const char *value) +{ + struct dlopen_impl *d = impl; + int (*f)(const char *, const char *, const char *) = dlsym (d->dso, + "PMI_KVS_Put"); + return f ? f (kvsname, key, value) : PMI_FAIL; +} + +static int dlopen_kvs_commit (void *impl, const char *kvsname) +{ + struct dlopen_impl *d = impl; + int (*f)(const char *) = dlsym (d->dso, "PMI_KVS_Commit"); + return f ? f (kvsname) : PMI_FAIL; +} + +static int dlopen_kvs_get (void *impl, + const char *kvsname, const char *key, char *value, int len) +{ + struct dlopen_impl *d = impl; + int (*f)(const char *, const char *, char *, int) = dlsym (d->dso, + "PMI_KVS_Get"); + return f ? f (kvsname, key, value, len) : PMI_FAIL; +} + +static int dlopen_spawn_multiple (void *impl, + int count, + const char *cmds[], + const char **argvs[], + const int maxprocs[], + const int info_keyval_sizesp[], + const pmi_keyval_t *info_keyval_vectors[], + int preput_keyval_size, + const pmi_keyval_t preput_keyval_vector[], + int errors[]) +{ + struct dlopen_impl *d = impl; + int (*f)(int, const char **, const char ***, const int *, const int *, + const pmi_keyval_t **, int, const pmi_keyval_t [], + int *) = dlsym (d->dso, "PMI_Spawn_multiple"); + return f ? f (count, cmds, argvs, maxprocs, info_keyval_sizesp, + info_keyval_vectors, preput_keyval_size, preput_keyval_vector, + errors) : PMI_FAIL; +} + +static int dlopen_get_id (void *impl, char *id_str, int length) +{ + struct dlopen_impl *d = impl; + int (*f)(char *, int) = dlsym (d->dso, "PMI_Get_id"); + return f ? f (id_str, length) : PMI_FAIL; +} + +static int dlopen_get_kvs_domain_id (void *impl, + char *id_str, int length) +{ + struct dlopen_impl *d = impl; + int (*f)(char *, int) = dlsym (d->dso, "PMI_Get_kvs_domain_id"); + return f ? f (id_str, length) : PMI_FAIL; +} + +static int dlopen_get_id_length_max (void *impl, int *length) +{ + struct dlopen_impl *d = impl; + int (*f)(int *) = dlsym (d->dso, "PMI_Get_id_length_max"); + return f ? f (length) : PMI_FAIL; +} + +static int dlopen_get_clique_size (void *impl, int *size) +{ + struct dlopen_impl *d = impl; + int (*f)(int *) = dlsym (d->dso, "PMI_Get_clique_size"); + return f ? f (size) : PMI_FAIL; +} + +static int dlopen_get_clique_ranks (void *impl, + int *ranks, int length) +{ + struct dlopen_impl *d = impl; + int (*f)(int *, int) = dlsym (d->dso, "PMI_Get_clique_ranks"); + return f ? f (ranks, length) : PMI_FAIL; +} + +static int dlopen_kvs_create (void *impl, char *kvsname, int length) +{ + struct dlopen_impl *d = impl; + int (*f)(char *, int) = dlsym (d->dso, "PMI_KVS_Create"); + return f ? f (kvsname, length) : PMI_FAIL; +} + +static int dlopen_kvs_destroy (void *impl, const char *kvsname) +{ + struct dlopen_impl *d = impl; + int (*f)(const char *) = dlsym (d->dso, "PMI_KVS_Destroy"); + return f ? f (kvsname) : PMI_FAIL; +} + +static int dlopen_kvs_iter_first (void *impl, + const char *kvsname, char *key, int key_len, char *val, int val_len) +{ + struct dlopen_impl *d = impl; + int (*f)(const char *, char *, int, char *, int) = dlsym (d->dso, + "PMI_KVS_Iter_first"); + return f ? f (kvsname, key, key_len, val, val_len) : PMI_FAIL; +} + +static int dlopen_kvs_iter_next (void *impl, + const char *kvsname, char *key, int key_len, char *val, int val_len) +{ + struct dlopen_impl *d = impl; + int (*f)(const char *, char *, int, char *, int) = dlsym (d->dso, + "PMI_KVS_Iter_next"); + return f ? f (kvsname, key, key_len, val, val_len) : PMI_FAIL; +} + +static int dlopen_parse_option (void *impl, + int num_args, char *args[], int *num_parsed, + pmi_keyval_t **keyvalp, int *size) +{ + struct dlopen_impl *d = impl; + int (*f)(int, char **, int *, pmi_keyval_t **, int *) = dlsym (d->dso, + "PMI_Parse_option"); + return f ? f (num_args, args, num_parsed, keyvalp, size) : PMI_FAIL; +} + +static int dlopen_args_to_keyval (void *impl, + int *argcp, char *((*argvp)[]), pmi_keyval_t **keyvalp, int *size) +{ + struct dlopen_impl *d = impl; + int (*f)(int *, char *((*)[]), pmi_keyval_t **, int *) = dlsym (d->dso, + "PMI_Args_to_keyval"); + return f ? f (argcp, argvp, keyvalp, size) : PMI_FAIL; +} + +static int dlopen_free_keyvals (void *impl, + pmi_keyval_t *keyvalp, int size) +{ + struct dlopen_impl *d = impl; + int (*f)(pmi_keyval_t *, int) = dlsym (d->dso, "PMI_Free_keyvals"); + return f ? f (keyvalp, size) : PMI_FAIL; +} + +static int dlopen_get_options (void *impl, char *str, int length) +{ + struct dlopen_impl *d = impl; + int (*f)(char *, int) = dlsym (d->dso, "PMI_Get_options"); + return f ? f (str, length) : PMI_FAIL; +} + +pmi_t *pmi_create_dlopen (const char *libname) +{ + struct dlopen_impl *d = xzmalloc (sizeof (*d)); + pmi_t *pmi; + + if (!libname) + libname = "libpmi.so"; + dlerror (); + if (!(d->dso = dlopen (libname, RTLD_NOW | RTLD_LOCAL)) + || !(pmi = pmi_create (d, destroy_dlopen))) { + destroy_dlopen (d); + return NULL; + } + pmi->init = dlopen_init; + pmi->initialized = dlopen_initialized; + pmi->finalize = dlopen_finalize; + pmi->get_size = dlopen_get_size; + pmi->get_rank = dlopen_get_rank; + pmi->get_universe_size = dlopen_get_universe_size; + pmi->get_appnum = dlopen_get_appnum; + pmi->publish_name = dlopen_publish_name; + pmi->unpublish_name = dlopen_unpublish_name; + pmi->lookup_name = dlopen_lookup_name; + pmi->barrier = dlopen_barrier; + pmi->abort = dlopen_abort; + pmi->kvs_get_my_name = dlopen_kvs_get_my_name; + pmi->kvs_get_name_length_max = dlopen_kvs_get_name_length_max; + pmi->kvs_get_key_length_max = dlopen_kvs_get_key_length_max; + pmi->kvs_get_value_length_max = dlopen_kvs_get_value_length_max; + pmi->kvs_put = dlopen_kvs_put; + pmi->kvs_commit = dlopen_kvs_commit; + pmi->kvs_get = dlopen_kvs_get; + pmi->spawn_multiple = dlopen_spawn_multiple; + + /* deprecated */ + pmi->get_id = dlopen_get_id; + pmi->get_kvs_domain_id = dlopen_get_kvs_domain_id; + pmi->get_id_length_max = dlopen_get_id_length_max; + pmi->get_clique_size = dlopen_get_clique_size; + pmi->get_clique_ranks = dlopen_get_clique_ranks; + pmi->kvs_create = dlopen_kvs_create; + pmi->kvs_destroy = dlopen_kvs_destroy; + pmi->kvs_iter_first = dlopen_kvs_iter_first; + pmi->kvs_iter_next = dlopen_kvs_iter_next; + pmi->parse_option = dlopen_parse_option; + pmi->args_to_keyval = dlopen_args_to_keyval; + pmi->free_keyvals = dlopen_free_keyvals; + pmi->get_options = dlopen_get_options; + + return pmi; +} + +/* + * vi:tabstop=4 shiftwidth=4 expandtab + */ diff --git a/src/common/libpmi-client/pmi-impl.h b/src/common/libpmi-client/pmi-impl.h new file mode 100644 index 000000000000..88ccabccabb6 --- /dev/null +++ b/src/common/libpmi-client/pmi-impl.h @@ -0,0 +1,66 @@ +#ifndef _FLUX_CORE_PMI_CLIENT_IMPL_H +#define _FLUX_CORE_PMI_CLIENT_IMPL_H + +struct pmi_struct { + int (*init)(void *impl, int *spawned); + int (*initialized)(void *impl, int *initialized); + int (*finalize)(void *impl); int (*get_size)(void *impl, int *size); + int (*get_rank)(void *impl, int *rank); + int (*get_universe_size)(void *impl, int *size); + int (*get_appnum)(void *impl, int *appnum); + int (*publish_name)(void *impl, const char *service_name, const char *port); + int (*unpublish_name)(void *impl, const char *service_name); + int (*lookup_name)(void *impl, const char *service_name, char *port); + int (*barrier)(void *impl); int (*abort)(void *impl, + int exit_code, const char *error_msg); + int (*kvs_get_my_name)(void *impl, char *kvsname, int length); + int (*kvs_get_name_length_max)(void *impl, int *length); + int (*kvs_get_key_length_max)(void *impl, int *length); + int (*kvs_get_value_length_max)(void *impl, int *length); + int (*kvs_put)(void *impl, + const char *kvsname, const char *key, const char *value); + int (*kvs_commit)(void *impl, const char *kvsname); + int (*kvs_get)(void *impl, + const char *kvsname, const char *key, char *value, int len); + int (*spawn_multiple)(void *impl, + int count, + const char *cmds[], + const char **argvs[], + const int maxprocs[], + const int info_keyval_sizesp[], + const pmi_keyval_t *info_keyval_vectors[], + int preput_keyval_size, + const pmi_keyval_t preput_keyval_vector[], + int errors[]); + + // deprecated + int (*get_id)(void *impl, char *id_str, int length); + int (*get_kvs_domain_id)(void *impl, char *id_str, int length); + int (*get_id_length_max)(void *impl, int *length); + int (*get_clique_size)(void *impl, int *size); + int (*get_clique_ranks)(void *impl, int *ranks, int length); + int (*kvs_create)(void *impl, char *kvsname, int length); + int (*kvs_destroy)(void *impl, const char *kvsname); + int (*kvs_iter_first)(void *impl, const char *kvsname, + char *key, int key_len, char *val, int val_len); + int (*kvs_iter_next)(void *impl, const char *kvsname, + char *key, int key_len, char *val, int val_len); + int (*parse_option)(void *impl, int num_args, char *args[], int *num_parsed, + pmi_keyval_t **keyvalp, int *size); + int (*args_to_keyval)(void *impl, int *argcp, char *((*argvp)[]), + pmi_keyval_t **keyvalp, int *size); + int (*free_keyvals)(void *impl, pmi_keyval_t keyvalp[], int size); + int (*get_options)(void *impl, char *str, int length); + + // internal + void *impl; + pmi_free_f impl_destroy; +}; + +pmi_t *pmi_create (void *impl, pmi_free_f destroy); + +#endif /* ! _FLUX_CORE_PMI_CLIENT_IMPL_H */ + +/* + * vi:tabstop=4 shiftwidth=4 expandtab + */ diff --git a/src/common/libpmi-client/pmi-simple.c b/src/common/libpmi-client/pmi-simple.c new file mode 100644 index 000000000000..17116fc95904 --- /dev/null +++ b/src/common/libpmi-client/pmi-simple.c @@ -0,0 +1,384 @@ +/*****************************************************************************\ + * Copyright (c) 2014 Lawrence Livermore National Security, LLC. Produced at + * the Lawrence Livermore National Laboratory (cf, AUTHORS, DISCLAIMER.LLNS). + * LLNL-CODE-658032 All rights reserved. + * + * This file is part of the Flux resource manager framework. + * For details, see https://github.com/flux-framework. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the license, or (at your option) + * any later version. + * + * Flux is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the IMPLIED WARRANTY OF MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the terms and conditions of the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * See also: http://www.gnu.org/licenses/ +\*****************************************************************************/ + +#if HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include +#include +#include +#include + +#include "pmi-client.h" +#include "pmi-impl.h" + +#include "src/common/libutil/log.h" +#include "src/common/libutil/xzmalloc.h" + +#define BUFSIZE 1024 + +struct simple_impl { + int fd; + int rank; + int size; + int initialized; + int kvsname_max; + int keylen_max; + int vallen_max; + char buf[BUFSIZE]; +}; + +void destroy_simple (void *arg) +{ + struct simple_impl *impl = arg; + if (impl) { + if (impl->fd != -1) + (void)close (impl->fd); + free (impl); + } +} + +static int dgetline (int fd, char *buf, int len) +{ + int i = 0; + while (i < len - 1) { + if (read (fd, &buf[i], 1) <= 0) + return -1; + if (buf[i] == '\n') + break; + i++; + } + buf[i] = '\0'; + return 0; +} + +static int simple_init (void *impl, int *spawned) +{ + struct simple_impl *s = impl; + int result = PMI_FAIL; + int rc, vers, subvers; + + if (dprintf (s->fd, "cmd=init pmi_version=1 pmi_subversion=1\n") < 0) + goto done; + if (dgetline (s->fd, s->buf, sizeof (s->buf)) < 0) + goto done; + if (sscanf (s->buf, + "cmd=response_to_init pmi_version=%d pmi_subversion=%d rc=%d", + &vers, &subvers, &rc) != 3) + goto done; + if (vers != 1 || subvers != 1 || rc != 0) + goto done; + if (dprintf (s->fd, "cmd=get_maxes\n") < 0) + goto done; + if (dgetline (s->fd, s->buf, sizeof (s->buf)) < 0) + goto done; + if (sscanf (s->buf, "cmd=maxes kvsname_max=%d keylen_max=%d vallen_max=%d", + &s->kvsname_max, &s->keylen_max, &s->vallen_max) != 3) + goto done; + s->initialized = 1; + result = PMI_SUCCESS; +done: + return result; +} + +static int simple_initialized (void *impl, int *initialized) +{ + struct simple_impl *s = impl; + *initialized = s->initialized; + return PMI_SUCCESS; +} + +static int simple_finalize (void *impl) +{ + struct simple_impl *s = impl; + int result = PMI_FAIL; + + if (dprintf (s->fd, "cmd=finalize\n") < 0) + goto done; + if (dgetline (s->fd, s->buf, sizeof (s->buf)) < 0) + goto done; + if (strcmp (s->buf, "cmd=finalize_ack") != 0) + goto done; + result = PMI_SUCCESS; +done: + return result; +} + +static int simple_get_size (void *impl, int *size) +{ + struct simple_impl *s = impl; + if (!s->initialized) + return PMI_FAIL; + *size = s->size; + return PMI_SUCCESS; +} + +static int simple_get_rank (void *impl, int *rank) +{ + struct simple_impl *s = impl; + if (!s->initialized) + return PMI_FAIL; + *rank = s->rank; + return PMI_SUCCESS; +} + +static int simple_get_appnum (void *impl, int *appnum) +{ + struct simple_impl *s = impl; + int result = PMI_FAIL; + int num; + + if (!s->initialized) + goto done; + if (dprintf (s->fd, "cmd=get_appnum\n") < 0) + goto done; + if (dgetline (s->fd, s->buf, sizeof (s->buf)) < 0) + goto done; + if (sscanf (s->buf, "cmd=appnum appnum=%d", &num) != 1) + goto done; + *appnum = num; + result = PMI_SUCCESS; +done: + return result; +} + +static int simple_get_universe_size (void *impl, int *universe_size) +{ + struct simple_impl *s = impl; + int result = PMI_FAIL; + int size; + + if (!s->initialized) + goto done; + if (dprintf (s->fd, "cmd=get_universe_size\n") < 0) + goto done; + if (dgetline (s->fd, s->buf, sizeof (s->buf)) < 0) + goto done; + if (sscanf (s->buf, "cmd=universe_size size=%d", &size) != 1) + goto done; + *universe_size = size; + result = PMI_SUCCESS; +done: + return result; +} + +static int simple_publish_name (void *impl, + const char *service_name, const char *port) +{ + return PMI_FAIL; +} + +static int simple_unpublish_name (void *impl, + const char *service_name) +{ + return PMI_FAIL; +} + +static int simple_lookup_name (void *impl, + const char *service_name, char *port) +{ + return PMI_FAIL; +} + +static int simple_barrier (void *impl) +{ + struct simple_impl *s = impl; + int result = PMI_FAIL; + + if (!s->initialized) + goto done; + if (dprintf (s->fd, "cmd=barrier_in\n") < 0) + goto done; + if (dgetline (s->fd, s->buf, sizeof (s->buf)) < 0) + goto done; + if (strcmp (s->buf, "cmd=barrier_out") != 0) + goto done; + result = PMI_SUCCESS; +done: + return result; +} + +static int simple_abort (void *impl, + int exit_code, const char *error_msg) +{ + return PMI_FAIL; +} + +#define S_(x) #x +#define S(x) S_(x) +static int simple_kvs_get_my_name (void *impl, + char *kvsname, int length) +{ + struct simple_impl *s = impl; + char val[BUFSIZE + 1]; + int result = PMI_FAIL; + + if (!s->initialized) + goto done; + if (dprintf (s->fd, "cmd=get_my_kvsname\n") < 0) + goto done; + if (dgetline (s->fd, s->buf, sizeof (s->buf)) < 0) + goto done; + if (sscanf (s->buf, "cmd=my_kvsname kvsname=%" S(BUFSIZE) "s", val) != 1) + goto done; + if (strlen (val) >= length) + goto done; + strcpy (kvsname, val); + result = PMI_SUCCESS; +done: + return result; +} + +static int simple_kvs_get_name_length_max (void *impl, int *length) +{ + struct simple_impl *s = impl; + if (!s->initialized) + return PMI_FAIL; + *length = s->kvsname_max; + return PMI_SUCCESS; +} + +static int simple_kvs_get_key_length_max (void *impl, int *length) +{ + struct simple_impl *s = impl; + if (!s->initialized) + return PMI_FAIL; + *length = s->keylen_max; + return PMI_SUCCESS; +} + +static int simple_kvs_get_value_length_max (void *impl, int *length) +{ + struct simple_impl *s = impl; + if (!s->initialized) + return PMI_FAIL; + *length = s->vallen_max; + return PMI_SUCCESS; +} + +static int simple_kvs_put (void *impl, + const char *kvsname, const char *key, const char *value) +{ + struct simple_impl *s = impl; + int result = PMI_FAIL; + + if (!s->initialized) + goto done; + if (dprintf (s->fd, "cmd=put kvsname=%s key=%s value=%s\n", + kvsname, key, value) < 0) + goto done; + if (dgetline (s->fd, s->buf, sizeof (s->buf)) < 0) + goto done; + if (strcmp (s->buf, "cmd=put_result rc=0 msg=success") != 0) + goto done; + result = PMI_SUCCESS; +done: + return result; +} + +static int simple_kvs_commit (void *impl, const char *kvsname) +{ + return PMI_SUCCESS; /* a no-op here */ +} + +static int simple_kvs_get (void *impl, + const char *kvsname, const char *key, char *value, int len) +{ + struct simple_impl *s = impl; + char val[BUFSIZE + 1]; + int result = PMI_FAIL; + + if (!s->initialized) + goto done; + if (dprintf (s->fd, "cmd=get kvsname=%s key=%s\n", kvsname, key) < 0) + goto done; + if (dgetline (s->fd, s->buf, sizeof (s->buf)) < 0) + goto done; + if (sscanf (s->buf, "cmd=get_result rc=0 msg=success value=%" + S(BUFSIZE) "s", val) != 1) + goto done; + if (strlen (val) >= len) + goto done; + strcpy (value, val); + result = PMI_SUCCESS; +done: + return result; +} + +static int simple_spawn_multiple (void *impl, + int count, + const char *cmds[], + const char **argvs[], + const int maxprocs[], + const int info_keyval_sizesp[], + const pmi_keyval_t *info_keyval_vectors[], + int preput_keyval_size, + const pmi_keyval_t preput_keyval_vector[], + int errors[]) +{ + return PMI_FAIL; +} + +pmi_t *pmi_create_simple (int fd, int rank, int size) +{ + struct simple_impl *s = xzmalloc (sizeof (*s)); + pmi_t *pmi; + + s->fd = fd; + s->rank = rank; + s->size = size; + if (!(pmi = pmi_create (s, destroy_simple))) { + destroy_simple (s); + return NULL; + } + pmi->init = simple_init; + pmi->initialized = simple_initialized; + pmi->finalize = simple_finalize; + pmi->get_size = simple_get_size; + pmi->get_rank = simple_get_rank; + pmi->get_appnum = simple_get_appnum; + pmi->get_universe_size = simple_get_universe_size; + pmi->publish_name = simple_publish_name; + pmi->unpublish_name = simple_unpublish_name; + pmi->lookup_name = simple_lookup_name; + pmi->barrier = simple_barrier; + pmi->abort = simple_abort; + pmi->kvs_get_my_name = simple_kvs_get_my_name; + pmi->kvs_get_name_length_max = simple_kvs_get_name_length_max; + pmi->kvs_get_key_length_max = simple_kvs_get_key_length_max; + pmi->kvs_get_value_length_max = simple_kvs_get_value_length_max; + pmi->kvs_put = simple_kvs_put; + pmi->kvs_commit = simple_kvs_commit; + pmi->kvs_get = simple_kvs_get; + pmi->spawn_multiple = simple_spawn_multiple; + + return pmi; +} + +/* + * vi:tabstop=4 shiftwidth=4 expandtab + */ diff --git a/src/common/libpmi-client/pmi.c b/src/common/libpmi-client/pmi.c new file mode 100644 index 000000000000..262971c17ff0 --- /dev/null +++ b/src/common/libpmi-client/pmi.c @@ -0,0 +1,361 @@ +/*****************************************************************************\ + * Copyright (c) 2014 Lawrence Livermore National Security, LLC. Produced at + * the Lawrence Livermore National Laboratory (cf, AUTHORS, DISCLAIMER.LLNS). + * LLNL-CODE-658032 All rights reserved. + * + * This file is part of the Flux resource manager framework. + * For details, see https://github.com/flux-framework. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the license, or (at your option) + * any later version. + * + * Flux is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the IMPLIED WARRANTY OF MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the terms and conditions of the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * See also: http://www.gnu.org/licenses/ +\*****************************************************************************/ + +#if HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include +#include +#include + +#include "pmi-client.h" +#include "pmi-impl.h" + +#include "src/common/libutil/log.h" +#include "src/common/libutil/xzmalloc.h" + +typedef struct { + int errnum; + const char *errstr; +} etab_t; + +static etab_t pmi_errors[] = { + { PMI_SUCCESS, "operation completed successfully" }, + { PMI_FAIL, "operation failed" }, + { PMI_ERR_NOMEM, "input buffer not large enough" }, + { PMI_ERR_INIT, "PMI not initialized" }, + { PMI_ERR_INVALID_ARG, "invalid argument" }, + { PMI_ERR_INVALID_KEY, "invalid key argument" }, + { PMI_ERR_INVALID_KEY_LENGTH,"invalid key length argument" }, + { PMI_ERR_INVALID_VAL, "invalid val argument" }, + { PMI_ERR_INVALID_VAL_LENGTH,"invalid val length argument" }, + { PMI_ERR_INVALID_LENGTH, "invalid length argument" }, + { PMI_ERR_INVALID_NUM_ARGS, "invalid number of arguments" }, + { PMI_ERR_INVALID_ARGS, "invalid args argument" }, + { PMI_ERR_INVALID_NUM_PARSED, "invalid num_parsed length argument" }, + { PMI_ERR_INVALID_KEYVALP, "invalid keyvalp argument" }, + { PMI_ERR_INVALID_SIZE, "invalid size argument" }, +}; +static const int pmi_errors_len = sizeof (pmi_errors) / sizeof (pmi_errors[0]); + +const char *pmi_strerror (int rc) +{ + static char unknown[] = "pmi error XXXXXXXXX"; + int i; + + for (i = 0; i < pmi_errors_len; i++) { + if (pmi_errors[i].errnum == rc) + return pmi_errors[i].errstr; + } + snprintf (unknown, sizeof (unknown), "pmi error %d", rc); + return unknown; +} + +void pmi_destroy (pmi_t *pmi) +{ + if (pmi) { + if (pmi->impl_destroy) + pmi->impl_destroy (pmi->impl); + free (pmi); + } +} + +pmi_t *pmi_create (void *impl, pmi_free_f destroy) +{ + pmi_t *pmi = xzmalloc (sizeof (*pmi)); + pmi->impl = impl; + pmi->impl_destroy = destroy; + return pmi; +} + +pmi_t *pmi_create_guess (void) +{ + const char *fd_str = getenv ("PMI_FD"); + const char *rank_str = getenv ("PMI_RANK"); + const char *size_str = getenv ("PMI_SIZE"); + + if (fd_str && rank_str && size_str) + return pmi_create_simple (strtol (fd_str, NULL, 10), + strtol (rank_str, NULL, 10), + strtol (size_str, NULL, 10)); + else + return pmi_create_dlopen (NULL); +} + +int pmi_init (pmi_t *pmi, int *spawned) +{ + if (!pmi->init) + return PMI_FAIL; + return pmi->init (pmi->impl, spawned); +} + +int pmi_initialized (pmi_t *pmi, int *initialized) +{ + if (!pmi->initialized) + return PMI_FAIL; + return pmi->initialized (pmi->impl, initialized); +} + +int pmi_finalize (pmi_t *pmi) +{ + if (!pmi->finalize) + return PMI_FAIL; + return pmi->finalize (pmi->impl); +} + +int pmi_get_size (pmi_t *pmi, int *size) +{ + if (!pmi->get_size) + return PMI_FAIL; + return pmi->get_size (pmi->impl, size); +} + +int pmi_get_rank (pmi_t *pmi, int *rank) +{ + if (!pmi->get_rank) + return PMI_FAIL; + return pmi->get_rank (pmi->impl, rank); +} + +int pmi_get_universe_size (pmi_t *pmi, int *size) +{ + if (!pmi->get_universe_size) + return PMI_FAIL; + return pmi->get_universe_size (pmi->impl, size); +} + +int pmi_get_appnum (pmi_t *pmi, int *appnum) +{ + if (!pmi->get_appnum) + return PMI_FAIL; + return pmi->get_appnum (pmi->impl, appnum); +} + +int pmi_publish_name (pmi_t *pmi, const char *service_name, const char *port) +{ + if (!pmi->publish_name) + return PMI_FAIL; + return pmi->publish_name (pmi->impl, service_name, port); +} + +int pmi_unpublish_name (pmi_t *pmi, const char *service_name) +{ + if (!pmi->publish_name) + return PMI_FAIL; + return pmi->unpublish_name (pmi->impl, service_name); +} + +int pmi_lookup_name (pmi_t *pmi, const char *service_name, char *port) +{ + if (!pmi->lookup_name) + return PMI_FAIL; + return pmi->lookup_name (pmi->impl, service_name, port); +} + +int pmi_barrier (pmi_t *pmi) +{ + if (!pmi->barrier) + return PMI_FAIL; + return pmi->barrier (pmi->impl); +} + +int pmi_abort (pmi_t *pmi, int exit_code, const char *error_msg) +{ + if (!pmi->abort) + return PMI_FAIL; + return pmi->abort (pmi->impl, exit_code, error_msg); +} + +int pmi_kvs_get_my_name (pmi_t *pmi, char *kvsname, int length) +{ + if (!pmi->kvs_get_my_name) + return PMI_FAIL; + return pmi->kvs_get_my_name (pmi->impl, kvsname, length); +} + +int pmi_kvs_get_name_length_max (pmi_t *pmi, int *length) +{ + if (!pmi->kvs_get_name_length_max) + return PMI_FAIL; + return pmi->kvs_get_name_length_max (pmi->impl, length); +} + +int pmi_kvs_get_key_length_max (pmi_t *pmi, int *length) +{ + if (!pmi->kvs_get_key_length_max) + return PMI_FAIL; + return pmi->kvs_get_key_length_max (pmi->impl, length); +} + +int pmi_kvs_get_value_length_max (pmi_t *pmi, int *length) +{ + if (!pmi->kvs_get_value_length_max) + return PMI_FAIL; + return pmi->kvs_get_value_length_max (pmi->impl, length); +} + +int pmi_kvs_put (pmi_t *pmi, + const char *kvsname, const char *key, const char *value) +{ + if (!pmi->kvs_put) + return PMI_FAIL; + return pmi->kvs_put (pmi->impl, kvsname, key, value); +} + + +int pmi_kvs_commit (pmi_t *pmi, const char *kvsname) +{ + if (!pmi->kvs_commit) + return PMI_FAIL; + return pmi->kvs_commit (pmi->impl, kvsname); +} + +int pmi_kvs_get (pmi_t *pmi, + const char *kvsname, const char *key, char *value, int len) +{ + if (!pmi->kvs_get) + return PMI_FAIL; + return pmi->kvs_get (pmi->impl, kvsname, key, value, len); +} + +int pmi_spawn_multiple (pmi_t *pmi, + int count, + const char *cmds[], + const char **argvs[], + const int maxprocs[], + const int info_keyval_sizesp[], + const pmi_keyval_t *info_keyval_vectors[], + int preput_keyval_size, + const pmi_keyval_t preput_keyval_vector[], + int errors[]) +{ + if (!pmi->spawn_multiple) + return PMI_FAIL; + return pmi->spawn_multiple (pmi->impl, count, cmds, argvs, maxprocs, + info_keyval_sizesp, info_keyval_vectors, preput_keyval_size, + preput_keyval_vector, errors); +} + +int pmi_get_id (pmi_t *pmi, char *id_str, int length) +{ + if (!pmi->get_id) + return PMI_FAIL; + return pmi->get_id (pmi->impl, id_str, length); +} + +int pmi_get_kvs_domain_id (pmi_t *pmi, char *id_str, int length) +{ + if (!pmi->get_kvs_domain_id) + return PMI_FAIL; + return pmi->get_kvs_domain_id (pmi->impl, id_str, length); +} + +int pmi_get_id_length_max (pmi_t *pmi, int *length) +{ + if (!pmi->get_id_length_max) + return PMI_FAIL; + return pmi->get_id_length_max (pmi->impl, length); +} + +int pmi_get_clique_size (pmi_t *pmi, int *size) +{ + if (!pmi->get_clique_size) + return PMI_FAIL; + return pmi->get_clique_size (pmi->impl, size); +} + +int pmi_get_clique_ranks (pmi_t *pmi, int *ranks, int length) +{ + if (!pmi->get_clique_ranks) + return PMI_FAIL; + return pmi->get_clique_ranks (pmi->impl, ranks, length); +} + +int pmi_kvs_create (pmi_t *pmi, char *kvsname, int length) +{ + if (!pmi->kvs_create) + return PMI_FAIL; + return pmi->kvs_create (pmi->impl, kvsname, length); +} + +int pmi_kvs_destroy (pmi_t *pmi, const char *kvsname) +{ + if (!pmi->kvs_destroy) + return PMI_FAIL; + return pmi->kvs_destroy (pmi->impl, kvsname); +} + +int pmi_kvs_iter_first (pmi_t *pmi, const char *kvsname, + char *key, int key_len, char *val, int val_len) +{ + if (!pmi->kvs_iter_first) + return PMI_FAIL; + return pmi->kvs_iter_first (pmi->impl, kvsname, key, key_len, val, val_len); +} + +int pmi_kvs_iter_next (pmi_t *pmi, const char *kvsname, + char *key, int key_len, char *val, int val_len) +{ + if (!pmi->kvs_iter_next) + return PMI_FAIL; + return pmi->kvs_iter_next (pmi->impl, kvsname, key, key_len, val, val_len); +} + +int pmi_parse_option (pmi_t *pmi, int num_args, char *args[], + int *num_parsed, pmi_keyval_t **keyvalp, int *size) +{ + if (!pmi->parse_option) + return PMI_FAIL; + return pmi->parse_option (pmi->impl, num_args, args, num_parsed, + keyvalp, size); +} + +int pmi_args_to_keyval (pmi_t *pmi, int *argcp, char *((*argvp)[]), + pmi_keyval_t **keyvalp, int *size) +{ + if (!pmi->args_to_keyval) + return PMI_FAIL; + return pmi->args_to_keyval (pmi->impl, argcp, argvp, keyvalp, size); +} + +int pmi_free_keyvals (pmi_t *pmi, pmi_keyval_t keyvalp[], int size) +{ + if (!pmi->free_keyvals) + return PMI_FAIL; + return pmi->free_keyvals (pmi->impl, keyvalp, size); +} + +int pmi_get_options (pmi_t *pmi, char *str, int length) +{ + if (!pmi->get_options) + return PMI_FAIL; + return pmi->get_options (pmi->impl, str, length); +} + +/* + * vi:tabstop=4 shiftwidth=4 expandtab + */