root/opal/mca/pmix/pmix4x/pmix/src/common/pmix_attributes.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. atrkcon
  2. atrkdes
  3. pmix_init_registered_attrs
  4. process_reg
  5. PMIx_Register_attributes
  6. pmix_release_registered_attrs
  7. pmix_register_client_attrs
  8. pmix_register_server_attrs
  9. pmix_register_tool_attrs
  10. _get_attrs
  11. _get_fns
  12. _local_relcb
  13. relcbfunc
  14. query_cbfunc
  15. pmix_attrs_query_support
  16. pmix_attributes_print_functions
  17. pmix_attributes_print_attrs
  18. pmix_attributes_print_headers
  19. pmix_attributes_print_attr

   1 /* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil -*- */
   2 /*
   3  * Copyright (c) 2014-2019 Intel, Inc.  All rights reserved.
   4  * Copyright (c) 2016      Mellanox Technologies, Inc.
   5  *                         All rights reserved.
   6  * Copyright (c) 2016      IBM Corporation.  All rights reserved.
   7  * $COPYRIGHT$
   8  *
   9  * Additional copyrights may follow
  10  *
  11  * $HEADER$
  12  */
  13 #include <src/include/pmix_config.h>
  14 
  15 #include <pmix.h>
  16 #include <pmix_common.h>
  17 #include <pmix_server.h>
  18 #include <pmix_rename.h>
  19 
  20 #include "src/mca/bfrops/bfrops.h"
  21 #include "src/mca/gds/base/base.h"
  22 #include "src/include/pmix_globals.h"
  23 #include "src/threads/threads.h"
  24 #include "src/client/pmix_client_ops.h"
  25 
  26 #include "src/common/pmix_attributes.h"
  27 
  28 static bool initialized = false;
  29 static pmix_list_t client_attrs;
  30 static pmix_list_t server_attrs;
  31 static pmix_list_t host_attrs;
  32 static pmix_list_t tool_attrs;
  33 
  34 typedef struct {
  35     pmix_list_item_t super;
  36     char *function;
  37     pmix_regattr_t *attrs;
  38     size_t nattrs;
  39 } pmix_attribute_trk_t;
  40 
  41 static void atrkcon(pmix_attribute_trk_t *p)
  42 {
  43     p->function = NULL;
  44     p->attrs = NULL;
  45     p->nattrs = 0;
  46 }
  47 static void atrkdes(pmix_attribute_trk_t *p)
  48 {
  49     if (NULL != p->function) {
  50         free(p->function);
  51     }
  52     if (NULL != p->attrs) {
  53         PMIX_REGATTR_FREE(p->attrs, p->nattrs);
  54     }
  55 }
  56 static PMIX_CLASS_INSTANCE(pmix_attribute_trk_t,
  57                            pmix_list_item_t,
  58                            atrkcon, atrkdes);
  59 
  60 PMIX_EXPORT void pmix_init_registered_attrs(void)
  61 {
  62     if (!initialized) {
  63         PMIX_CONSTRUCT(&client_attrs, pmix_list_t);
  64         PMIX_CONSTRUCT(&server_attrs, pmix_list_t);
  65         PMIX_CONSTRUCT(&host_attrs, pmix_list_t);
  66         PMIX_CONSTRUCT(&tool_attrs, pmix_list_t);
  67         initialized = true;
  68     }
  69 }
  70 
  71 static pmix_status_t process_reg(char *level, char *function,
  72                                  pmix_regattr_t attrs[], size_t nattrs)
  73 {
  74     pmix_attribute_trk_t *fnptr;
  75     pmix_list_t *lst;
  76     size_t n;
  77 
  78     /* select the list this will appear on */
  79     if (0 == strcmp(level, PMIX_CLIENT_ATTRIBUTES)) {
  80         lst = &client_attrs;
  81     } else if (0 == strcmp(level, PMIX_SERVER_ATTRIBUTES)) {
  82         lst = &server_attrs;
  83     } else if (0 == strcmp(level, PMIX_HOST_ATTRIBUTES)) {
  84         lst = &host_attrs;
  85     } else if (0 == strcmp(level, PMIX_TOOL_ATTRIBUTES)) {
  86         lst = &tool_attrs;
  87     } else {
  88         return PMIX_ERR_BAD_PARAM;
  89     }
  90 
  91     /* see if we already have this function */
  92     PMIX_LIST_FOREACH(fnptr, lst, pmix_attribute_trk_t) {
  93         if (0 == strcmp(function, fnptr->function)) {
  94             /* we already have this function at this level
  95              * so we must return an error */
  96             return PMIX_ERR_REPEAT_ATTR_REGISTRATION;
  97         }
  98     }
  99 
 100     fnptr = PMIX_NEW(pmix_attribute_trk_t);
 101     pmix_list_append(lst, &fnptr->super);
 102     fnptr->function = strdup(function);
 103     if (0 < nattrs) {
 104         fnptr->nattrs = nattrs;
 105         PMIX_REGATTR_CREATE(fnptr->attrs, fnptr->nattrs);
 106         for (n=0; n < nattrs; n++) {
 107             fnptr->attrs[n].name = strdup(attrs[n].name);
 108             PMIX_LOAD_KEY(fnptr->attrs[n].string, attrs[n].string);
 109             fnptr->attrs[n].type = attrs[n].type;
 110             PMIX_ARGV_COPY(fnptr->attrs[n].description, attrs[n].description);
 111         }
 112     }
 113     return PMIX_SUCCESS;
 114 }
 115 
 116 PMIX_EXPORT pmix_status_t PMIx_Register_attributes(char *function,
 117                                                    pmix_regattr_t attrs[], size_t nattrs)
 118 {
 119     pmix_status_t rc;
 120 
 121     PMIX_ACQUIRE_THREAD(&pmix_global_lock);
 122     if (pmix_globals.init_cntr <= 0) {
 123         PMIX_RELEASE_THREAD(&pmix_global_lock);
 124         return PMIX_ERR_INIT;
 125     }
 126 
 127     rc = process_reg(PMIX_HOST_ATTRIBUTES, function, attrs, nattrs);
 128     PMIX_RELEASE_THREAD(&pmix_global_lock);
 129     return rc;
 130 }
 131 
 132 PMIX_EXPORT void pmix_release_registered_attrs(void)
 133 {
 134     if (initialized) {
 135         PMIX_LIST_DESTRUCT(&client_attrs);
 136         PMIX_LIST_DESTRUCT(&server_attrs);
 137         PMIX_LIST_DESTRUCT(&host_attrs);
 138         PMIX_LIST_DESTRUCT(&tool_attrs);
 139     }
 140 }
 141 
 142 /* sadly, we cannot dynamically register our supported attributes
 143  * as that would require the user to first call the function whose
 144  * attributes they want to know about - which somewhat defeats the
 145  * purpose. Until someone comes up with a better solution, we will
 146  * manually maintain the list */
 147 static char *client_fns[] = {
 148     "PMIx_Init",
 149     "PMIx_Finalize",
 150     "PMIx_Put",
 151     "PMIx_Get",
 152     "PMIx_Get_nb",
 153     "PMIx_Store_internal",
 154     "PMIx_Commit",
 155     "PMIx_Fence",
 156     "PMIx_Fence_nb",
 157     "PMIx_Publish",
 158     "PMIx_Group_construct",
 159     "PMIx_Group_construct_nb",
 160     "PMIx_Group_destruct",
 161     "PMIx_Group_destruct_nb",
 162     "PMIx_Group_invite",
 163     "PMIx_Group_invite_nb",
 164     "PMIx_Group_join",
 165     "PMIx_Group_join_nb",
 166     "PMIx_Spawn",
 167     "PMIx_Spawn_nb",
 168     "PMIx_Log",
 169     "PMIx_Log_nb"
 170 };
 171 
 172 typedef struct {
 173     char *name;
 174     char *string;
 175     pmix_data_type_t type;
 176     char **description;
 177 } pmix_regattr_input_t;
 178 
 179 static pmix_regattr_input_t client_attributes[] = {
 180         // init
 181         {.name = "PMIX_GDS_MODULE", .string = PMIX_GDS_MODULE, .type = PMIX_STRING, .description = (char *[]){"UNRESTRICTED", NULL}},
 182         {.name = "PMIX_EVENT_BASE", .string = PMIX_EVENT_BASE, .type = PMIX_POINTER, .description = (char *[]){"VALID MEMORY REFERENCE", NULL}},
 183         {.name = "PMIX_HOSTNAME", .string = PMIX_HOSTNAME, .type = PMIX_STRING, .description = (char *[]){"UNRESTRICTED", NULL}},
 184         {.name = "PMIX_NODEID", .string = PMIX_NODEID, .type = PMIX_UINT32, .description = (char *[]){"POSITIVE INTEGERS", NULL}},
 185         {.name = "PMIX_PROGRAMMING_MODEL", .string = PMIX_PROGRAMMING_MODEL, .type = PMIX_STRING, .description = (char *[]){"UNRESTRICTED", NULL}},
 186         {.name = "PMIX_MODEL_LIBRARY_NAME", .string = PMIX_MODEL_LIBRARY_NAME, .type = PMIX_STRING, .description = (char *[]){"UNRESTRICTED", NULL}},
 187         {.name = "PMIX_MODEL_LIBRARY_VERSION", .string = PMIX_MODEL_LIBRARY_VERSION, .type = PMIX_STRING, .description = (char *[]){"UNRESTRICTED", NULL}},
 188         {.name = "PMIX_THREADING_MODEL", .string = PMIX_THREADING_MODEL, .type = PMIX_STRING, .description = (char *[]){"UNRESTRICTED", NULL}},
 189         {.name = "PMIX_USOCK_DISABLE", .string = PMIX_USOCK_DISABLE, .type = PMIX_BOOL, .description = (char *[]){"True,False", "Disable usock messaging interface", NULL}},
 190         {.name = "PMIX_SOCKET_MODE", .string = PMIX_SOCKET_MODE, .type = PMIX_UINT32, .description = (char *[]){"Valid POSIX mode_t value", NULL}},
 191         {.name = "PMIX_TCP_REPORT_URI", .string = PMIX_TCP_REPORT_URI, .type = PMIX_STRING, .description = (char *[]){"-, +, or filename", NULL}},
 192         {.name = "PMIX_TCP_IF_INCLUDE", .string = PMIX_TCP_IF_INCLUDE, .type = PMIX_STRING, .description = (char *[]){"UNRESTRICTED", "Comma-separated list of", "TCP interfaces to include", NULL}},
 193         {.name = "PMIX_TCP_IF_EXCLUDE", .string = PMIX_TCP_IF_EXCLUDE, .type = PMIX_STRING, .description = (char *[]){"UNRESTRICTED", "Comma-separated list of", "TCP interfaces to exclude", NULL}},
 194         {.name = "PMIX_TCP_IPV4_PORT", .string = PMIX_TCP_IPV4_PORT, .type = PMIX_INT, .description = (char *[]){"POSITIVE INTEGERS", "IPv4 port to be used", NULL}},
 195         {.name = "PMIX_TCP_IPV6_PORT", .string = PMIX_TCP_IPV6_PORT, .type = PMIX_INT, .description = (char *[]){"POSITIVE INTEGERS", "IPv6 port to be used", NULL}},
 196         {.name = "PMIX_TCP_DISABLE_IPV4", .string = PMIX_TCP_DISABLE_IPV4, .type = PMIX_BOOL, .description = (char *[]){"True,False", "Disable IPv4 messaging interface", NULL}},
 197         {.name = "PMIX_TCP_DISABLE_IPV6", .string = PMIX_TCP_DISABLE_IPV6, .type = PMIX_BOOL, .description = (char *[]){"True,False", "Disable IPv6 messaging interface", NULL}},
 198         {.name = ""},
 199         // finalize
 200         {.name = "PMIX_EMBED_BARRIER", .string = PMIX_EMBED_BARRIER, .type = PMIX_BOOL, .description = (char *[]){"True,False", "Execute fence during finalize", NULL}},
 201         {.name = ""},
 202         // put
 203         {.name = ""},
 204         // get
 205         {.name = "PMIX_DATA_SCOPE", .string = PMIX_DATA_SCOPE, .type = PMIX_SCOPE, .description = (char *[]){"PMIX_SCOPE_UNDEF,PMIX_LOCAL,", "PMIX_REMOTE,PMIX_GLOBAL,", "PMIX_INTERNAL", NULL}},
 206         {.name = "PMIX_OPTIONAL", .string = PMIX_OPTIONAL, .type = PMIX_BOOL, .description = (char *[]){"True,False", NULL}},
 207         {.name = "PMIX_IMMEDIATE", .string = PMIX_IMMEDIATE, .type = PMIX_BOOL, .description = (char *[]){"True,False", NULL}},
 208         {.name = "PMIX_SESSION_INFO", .string = PMIX_SESSION_INFO, .type = PMIX_BOOL, .description = (char *[]){"True,False", "Requesting session-level value", NULL}},
 209         {.name = "PMIX_JOB_INFO", .string = PMIX_JOB_INFO, .type = PMIX_BOOL, .description = (char *[]){"True,False", "Requesting job-level value", NULL}},
 210         {.name = "PMIX_APP_INFO", .string = PMIX_APP_INFO, .type = PMIX_BOOL, .description = (char *[]){"True,False", "Requesting app-level value", NULL}},
 211         {.name = "PMIX_NODE_INFO", .string = PMIX_NODE_INFO, .type = PMIX_BOOL, .description = (char *[]){"True,False", "Requesting node-level value", NULL}},
 212         {.name = ""},
 213         // get_nb
 214         {.name = "PMIX_DATA_SCOPE", .string = PMIX_DATA_SCOPE, .type = PMIX_SCOPE, .description = (char *[]){"PMIX_SCOPE_UNDEF,PMIX_LOCAL,", "PMIX_REMOTE,PMIX_GLOBAL,", "PMIX_INTERNAL", NULL}},
 215         {.name = "PMIX_OPTIONAL", .string = PMIX_OPTIONAL, .type = PMIX_BOOL, .description = (char *[]){"True,False", NULL}},
 216         {.name = "PMIX_IMMEDIATE", .string = PMIX_IMMEDIATE, .type = PMIX_BOOL, .description = (char *[]){"True,False", NULL}},
 217         {.name = "PMIX_SESSION_INFO", .string = PMIX_SESSION_INFO, .type = PMIX_BOOL, .description = (char *[]){"True,False", "Requesting session-level value", NULL}},
 218         {.name = "PMIX_JOB_INFO", .string = PMIX_JOB_INFO, .type = PMIX_BOOL, .description = (char *[]){"True,False", "Requesting job-level value", NULL}},
 219         {.name = "PMIX_APP_INFO", .string = PMIX_APP_INFO, .type = PMIX_BOOL, .description = (char *[]){"True,False", "Requesting app-level value", NULL}},
 220         {.name = "PMIX_NODE_INFO", .string = PMIX_NODE_INFO, .type = PMIX_BOOL, .description = (char *[]){"True,False", "Requesting node-level value", NULL}},
 221         {.name = ""},
 222         // store_internal
 223         {.name = ""},
 224         // commit
 225         {.name = ""},
 226         // fence
 227         {.name = ""},
 228         // fence_nb
 229         {.name = ""},
 230         // publish
 231         {.name = ""},
 232         // group_construct
 233         {.name = "PMIX_EMBED_BARRIER", .string = PMIX_EMBED_BARRIER, .type = PMIX_BOOL, .description = (char *[]){"True,False", NULL}},
 234         {.name = ""},
 235         // group_construct_nb
 236         {.name = "PMIX_EMBED_BARRIER", .string = PMIX_EMBED_BARRIER, .type = PMIX_BOOL, .description = (char *[]){"True,False", NULL}},
 237         {.name = ""},
 238         // group_destruct
 239         {.name = "PMIX_EMBED_BARRIER", .string = PMIX_EMBED_BARRIER, .type = PMIX_BOOL, .description = (char *[]){"True,False", NULL}},
 240         {.name = ""},
 241         // group_destruct_nb
 242         {.name = "PMIX_TIMEOUT", .string = PMIX_TIMEOUT, .type = PMIX_INT, .description = (char *[]){"POSITIVE INTEGERS", NULL}},
 243         {.name = ""},
 244         // group_invite
 245         {.name = "PMIX_TIMEOUT", .string = PMIX_TIMEOUT, .type = PMIX_INT, .description = (char *[]){"POSITIVE INTEGERS", NULL}},
 246         {.name = ""},
 247         // group_invite_nb
 248         {.name = "PMIX_TIMEOUT", .string = PMIX_TIMEOUT, .type = PMIX_INT, .description = (char *[]){"POSITIVE INTEGERS", NULL}},
 249         {.name = ""},
 250         // group_join
 251         {.name = "PMIX_TIMEOUT", .string = PMIX_TIMEOUT, .type = PMIX_INT, .description = (char *[]){"POSITIVE INTEGERS", NULL}},
 252         {.name = ""},
 253         // group_join_nb
 254         {.name = "PMIX_SETUP_APP_ENVARS", .string = PMIX_SETUP_APP_ENVARS, .type = PMIX_STRING, .description = (char *[]){"UNRESTRICTED", NULL}},
 255         {.name = ""},
 256         // spawn
 257         {.name = "PMIX_SETUP_APP_ENVARS", .string = PMIX_SETUP_APP_ENVARS, .type = PMIX_STRING, .description = (char *[]){"UNRESTRICTED", NULL}},
 258         {.name = ""},
 259         // spawn_nb
 260         {.name = "PMIX_EMBED_BARRIER", .string = PMIX_EMBED_BARRIER, .type = PMIX_BOOL, .description = (char *[]){"True,False", NULL}},
 261         {.name = ""},
 262         // log
 263         {.name = "PMIX_LOG_GENERATE_TIMESTAMP", .string = PMIX_LOG_GENERATE_TIMESTAMP, .type = PMIX_BOOL, .description = (char *[]){"True,False", NULL}},
 264         {.name = ""},
 265         // log_nb
 266         {.name = "PMIX_LOG_GENERATE_TIMESTAMP", .string = PMIX_LOG_GENERATE_TIMESTAMP, .type = PMIX_BOOL, .description = (char *[]){"True,False", NULL}},
 267         {.name = ""},
 268 };
 269 
 270 /*****    REGISTER CLIENT ATTRS    *****/
 271 static bool client_attrs_regd = false;
 272 
 273 PMIX_EXPORT pmix_status_t pmix_register_client_attrs(void)
 274 {
 275     size_t nregs, nattrs, n, m;
 276     size_t cnt = 0;
 277     pmix_status_t rc = PMIX_SUCCESS;
 278     pmix_regattr_t *attrs;
 279 
 280     if (client_attrs_regd) {
 281         return PMIX_SUCCESS;
 282     }
 283     client_attrs_regd = true;
 284 
 285     nregs = sizeof(client_fns) / sizeof(char*);
 286 
 287     /* we know we have to prep the GDS, PTL, BFROPS, and SEC
 288      * entries as these are dynamically defined */
 289     client_attributes[0].description[0] = pmix_gds_base_get_available_modules();
 290 
 291     for (n=0; n < nregs; n++) {
 292         nattrs = 0;
 293         while (0 != strlen(client_attributes[cnt+nattrs].name)) {
 294             ++nattrs;
 295         }
 296         PMIX_REGATTR_CREATE(attrs, nattrs);
 297         for (m=0; m < nattrs; m++) {
 298             attrs[m].name = strdup(client_attributes[m+cnt].name);
 299             PMIX_LOAD_KEY(attrs[m].string, client_attributes[m+cnt].string);
 300             attrs[m].type = client_attributes[m+cnt].type;
 301             PMIX_ARGV_COPY(attrs[m].description, client_attributes[m+cnt].description);
 302         }
 303         rc = process_reg(PMIX_CLIENT_ATTRIBUTES,
 304                          client_fns[n],
 305                          attrs, nattrs);
 306         PMIX_REGATTR_FREE(attrs, nattrs);
 307         if (PMIX_SUCCESS != rc) {
 308             break;
 309         }
 310         cnt += nattrs + 1;
 311     }
 312 
 313     if (NULL != client_attributes[0].description[0]) {
 314         free(client_attributes[0].description[0]);
 315         client_attributes[0].description[0] = NULL;
 316     }
 317     return PMIX_SUCCESS;
 318 }
 319 
 320 static char *server_fns[] = {
 321     "PMIx_server_init",
 322     "PMIx_server_finalize",
 323     "PMIx_generate_regex",
 324     "PMIx_generate_ppn",
 325     "PMIx_server_register_nspace",
 326     "PMIx_server_deregister_nspace",
 327     "PMIx_server_register_client",
 328     "PMIx_server_deregister_client",
 329     "PMIx_server_setup_fork",
 330     "PMIx_server_dmodex_request",
 331     "PMIx_server_setup_application",
 332     "PMIx_Register_attributes",
 333     "PMIx_server_setup_local_support",
 334     "PMIx_server_IOF_deliver",
 335     "PMIx_server_collect_inventory",
 336     "PMIx_server_deliver_inventory",
 337     "PMIx_Get",
 338     "PMIx_Get_nb",
 339     "PMIx_Fence",
 340     "PMIx_Fence_nb",
 341     "PMIx_Spawn",
 342     "PMIx_Spawn_nb",
 343     "PMIx_Connect",
 344     "PMIx_Connect_nb",
 345     "PMIx_Register_event_handler",
 346     "PMIx_Query_info_nb",
 347     "PMIx_Job_control",
 348     "PMIx_Job_control_nb"
 349 };
 350 
 351 static pmix_regattr_input_t server_attributes[] = {
 352     // init
 353         {.name = "PMIX_GDS_MODULE", .string = PMIX_GDS_MODULE, .type = PMIX_STRING, .description = (char *[]){"UNRESTRICTED", NULL}},
 354         {.name = "PMIX_EVENT_BASE", .string = PMIX_EVENT_BASE, .type = PMIX_POINTER, .description = (char *[]){"VALID MEMORY REFERENCE", NULL}},
 355         {.name = "PMIX_HOSTNAME", .string = PMIX_HOSTNAME, .type = PMIX_STRING, .description = (char *[]){"UNRESTRICTED", NULL}},
 356         {.name = "PMIX_NODEID", .string = PMIX_NODEID, .type = PMIX_UINT32, .description = (char *[]){"POSITIVE INTEGERS", NULL}},
 357         {.name = "PMIX_SINGLE_LISTENER", .string = PMIX_SINGLE_LISTENER, .type = PMIX_BOOL, .description = (char *[]){"True,False", "Only use one messaging interface", NULL}},
 358         {.name = "PMIX_USOCK_DISABLE", .string = PMIX_USOCK_DISABLE, .type = PMIX_BOOL, .description = (char *[]){"True,False", "Disable usock messaging interface", NULL}},
 359         {.name = "PMIX_SOCKET_MODE", .string = PMIX_SOCKET_MODE, .type = PMIX_UINT32, .description = (char *[]){"Valid POSIX mode_t value", NULL}},
 360         {.name = "PMIX_TCP_REPORT_URI", .string = PMIX_TCP_REPORT_URI, .type = PMIX_STRING, .description = (char *[]){"-, +, or filename", NULL}},
 361         {.name = "PMIX_TCP_IF_INCLUDE", .string = PMIX_TCP_IF_INCLUDE, .type = PMIX_STRING, .description = (char *[]){"UNRESTRICTED", "Comma-separated list of", "TCP interfaces to include", NULL}},
 362         {.name = "PMIX_TCP_IF_EXCLUDE", .string = PMIX_TCP_IF_EXCLUDE, .type = PMIX_STRING, .description = (char *[]){"UNRESTRICTED", "Comma-separated list of", "TCP interfaces to exclude", NULL}},
 363         {.name = "PMIX_TCP_IPV4_PORT", .string = PMIX_TCP_IPV4_PORT, .type = PMIX_INT, .description = (char *[]){"POSITIVE INTEGERS", "IPv4 port to be used", NULL}},
 364         {.name = "PMIX_TCP_IPV6_PORT", .string = PMIX_TCP_IPV6_PORT, .type = PMIX_INT, .description = (char *[]){"POSITIVE INTEGERS", "IPv6 port to be used", NULL}},
 365         {.name = "PMIX_TCP_DISABLE_IPV4", .string = PMIX_TCP_DISABLE_IPV4, .type = PMIX_BOOL, .description = (char *[]){"True,False", "Disable IPv4 messaging interface", NULL}},
 366         {.name = "PMIX_TCP_DISABLE_IPV6", .string = PMIX_TCP_DISABLE_IPV6, .type = PMIX_BOOL, .description = (char *[]){"True,False", "Disable IPv6 messaging interface", NULL}},
 367         {.name = "PMIX_SERVER_REMOTE_CONNECTIONS", .string = PMIX_SERVER_REMOTE_CONNECTIONS, .type = PMIX_BOOL, .description = (char *[]){"True,False", "Allow connections from", "remote tools", NULL}},
 368         {.name = "PMIX_SERVER_NSPACE", .string = PMIX_SERVER_NSPACE, .type = PMIX_STRING, .description = (char *[]){"UNRESTRICTED", "Namespace assigned to server", NULL}},
 369         {.name = "PMIX_SERVER_RANK", .string = PMIX_SERVER_RANK, .type = PMIX_PROC_RANK, .description = (char *[]){"POSITIVE INTEGERS", "Rank assigned to server", NULL}},
 370         {.name = "PMIX_SERVER_TMPDIR", .string = PMIX_SERVER_TMPDIR, .type = PMIX_STRING, .description = (char *[]){"UNRESTRICTED", "Path to temp directory", "assigned to server", NULL}},
 371         {.name = "PMIX_SYSTEM_TMPDIR", .string = PMIX_SYSTEM_TMPDIR, .type = PMIX_STRING, .description = (char *[]){"UNRESTRICTED", "Path to system temp directory", NULL}},
 372         {.name = "PMIX_SERVER_TOOL_SUPPORT", .string = PMIX_SERVER_TOOL_SUPPORT, .type = PMIX_BOOL, .description = (char *[]){"True,False", "Allow tool connections", NULL}},
 373         {.name = "PMIX_SERVER_SYSTEM_SUPPORT", .string = PMIX_SERVER_SYSTEM_SUPPORT, .type = PMIX_BOOL, .description = (char *[]){"True,False", "Declare server as being the", "local system server for PMIx", "connection requests", NULL}},
 374         {.name = ""},
 375     // finalize
 376         {.name = ""},
 377     // regex
 378         {.name = "PMIX_DATA_SCOPE", .string = PMIX_DATA_SCOPE, .type = PMIX_SCOPE, .description = (char *[]){"PMIX_SCOPE_UNDEF,PMIX_LOCAL,","PMIX_REMOTE,PMIX_GLOBAL,", "PMIX_INTERNAL", NULL}},
 379         {.name = "PMIX_OPTIONAL", .string = PMIX_OPTIONAL, .type = PMIX_BOOL, .description = (char *[]){"True,False", NULL}},
 380         {.name = "PMIX_IMMEDIATE", .string = PMIX_IMMEDIATE, .type = PMIX_BOOL, .description = (char *[]){"True,False", NULL}},
 381         {.name = "PMIX_TIMEOUT", .string = PMIX_TIMEOUT, .type = PMIX_INT, .description = (char *[]){"POSITIVE INTEGERS", NULL}},
 382         {.name = ""},
 383     // ppn
 384         {.name = "PMIX_EMBED_BARRIER", .string = PMIX_EMBED_BARRIER, .type = PMIX_BOOL, .description = (char *[]){"True,False", NULL}},
 385         {.name = ""},
 386     // register_nspace
 387         {.name = "PMIX_EMBED_BARRIER", .string = PMIX_EMBED_BARRIER, .type = PMIX_BOOL, .description = (char *[]){"True,False", NULL}},
 388         {.name = ""},
 389     // deregister_nspace
 390         {.name = "PMIX_EMBED_BARRIER", .string = PMIX_EMBED_BARRIER, .type = PMIX_BOOL, .description = (char *[]){"True,False", NULL}},
 391         {.name = ""},
 392     // register_client
 393         {.name = "PMIX_EMBED_BARRIER", .string = PMIX_EMBED_BARRIER, .type = PMIX_BOOL, .description = (char *[]){"True,False", NULL}},
 394         {.name = ""},
 395     // deregister_client
 396         {.name = "PMIX_TIMEOUT", .string = PMIX_TIMEOUT, .type = PMIX_INT, .description = (char *[]){"POSITIVE INTEGERS", NULL}},
 397         {.name = ""},
 398     // setup_fork
 399         {.name = "PMIX_TIMEOUT", .string = PMIX_TIMEOUT, .type = PMIX_INT, .description = (char *[]){"POSITIVE INTEGERS", NULL}},
 400         {.name = ""},
 401     // dmodex_request
 402         {.name = "PMIX_TIMEOUT", .string = PMIX_TIMEOUT, .type = PMIX_INT, .description = (char *[]){"POSITIVE INTEGERS", NULL}},
 403         {.name = ""},
 404     // setup_application
 405         {.name = "PMIX_TIMEOUT", .string = PMIX_TIMEOUT, .type = PMIX_INT, .description = (char *[]){"POSITIVE INTEGERS", NULL}},
 406         {.name = ""},
 407     // register_attributes
 408         {.name = "PMIX_SETUP_APP_ENVARS", .string = PMIX_SETUP_APP_ENVARS, .type = PMIX_STRING, .description = (char *[]){"UNRESTRICTED", NULL}},
 409         {.name = ""},
 410     // setup_local_support
 411         {.name = "PMIX_SETUP_APP_ENVARS", .string = PMIX_SETUP_APP_ENVARS, .type = PMIX_STRING, .description = (char *[]){"UNRESTRICTED", NULL}},
 412         {.name = ""},
 413     // IOF deliver
 414         {.name = "PMIX_EMBED_BARRIER", .string = PMIX_EMBED_BARRIER, .type = PMIX_BOOL, .description = (char *[]){"True,False", NULL}},
 415         {.name = ""},
 416     // collect_inventory
 417         {.name = "PMIX_LOG_GENERATE_TIMESTAMP", .string = PMIX_LOG_GENERATE_TIMESTAMP, .type = PMIX_BOOL, .description = (char *[]){"True,False", NULL}},
 418         {.name = ""},
 419     // deliver_inventory
 420         {.name = "PMIX_LOG_GENERATE_TIMESTAMP", .string = PMIX_LOG_GENERATE_TIMESTAMP, .type = PMIX_BOOL, .description = (char *[]){"True,False", NULL}},
 421         {.name = ""},
 422     // get
 423         {.name = "PMIX_IMMEDIATE", .string = PMIX_IMMEDIATE, .type = PMIX_BOOL, .description = (char *[]){"True,False", NULL}},
 424         {.name = "PMIX_TIMEOUT", .string = PMIX_TIMEOUT, .type = PMIX_INT, .description = (char *[]){"POSITIVE INTEGERS", NULL}},
 425         {.name = ""},
 426     // get_nb
 427         {.name = "PMIX_IMMEDIATE", .string = PMIX_IMMEDIATE, .type = PMIX_BOOL, .description = (char *[]){"True,False", NULL}},
 428         {.name = "PMIX_TIMEOUT", .string = PMIX_TIMEOUT, .type = PMIX_INT, .description = (char *[]){"POSITIVE INTEGERS", NULL}},
 429         {.name = ""},
 430     // fence
 431         {.name = "PMIX_COLLECT_DATA", .string = PMIX_COLLECT_DATA, .type = PMIX_BOOL, .description = (char *[]){"True,False", NULL}},
 432         {.name = "PMIX_TIMEOUT", .string = PMIX_TIMEOUT, .type = PMIX_INT, .description = (char *[]){"POSITIVE INTEGERS", NULL}},
 433         {.name = ""},
 434     // fence_nb
 435         {.name = "PMIX_COLLECT_DATA", .string = PMIX_COLLECT_DATA, .type = PMIX_BOOL, .description = (char *[]){"True,False", NULL}},
 436         {.name = "PMIX_TIMEOUT", .string = PMIX_TIMEOUT, .type = PMIX_INT, .description = (char *[]){"POSITIVE INTEGERS", NULL}},
 437         {.name = ""},
 438     // spawn
 439         {.name = "PMIX_FWD_STDIN", .string = PMIX_FWD_STDIN, .type = PMIX_BOOL, .description = (char *[]){"True,False", NULL}},
 440         {.name = "PMIX_FWD_STDOUT", .string = PMIX_FWD_STDOUT, .type = PMIX_BOOL, .description = (char *[]){"True,False", NULL}},
 441         {.name = "PMIX_FWD_STDERR", .string = PMIX_FWD_STDERR, .type = PMIX_BOOL, .description = (char *[]){"True,False", NULL}},
 442         {.name = "PMIX_FWD_STDDIAG", .string = PMIX_FWD_STDDIAG, .type = PMIX_BOOL, .description = (char *[]){"True,False", NULL}},
 443         {.name = ""},
 444     // spawn_nb
 445         {.name = "PMIX_FWD_STDIN", .string = PMIX_FWD_STDIN, .type = PMIX_BOOL, .description = (char *[]){"True,False", NULL}},
 446         {.name = "PMIX_FWD_STDOUT", .string = PMIX_FWD_STDOUT, .type = PMIX_BOOL, .description = (char *[]){"True,False", NULL}},
 447         {.name = "PMIX_FWD_STDERR", .string = PMIX_FWD_STDERR, .type = PMIX_BOOL, .description = (char *[]){"True,False", NULL}},
 448         {.name = "PMIX_FWD_STDDIAG", .string = PMIX_FWD_STDDIAG, .type = PMIX_BOOL, .description = (char *[]){"True,False", NULL}},
 449         {.name = ""},
 450     // connect
 451         {.name = "PMIX_TIMEOUT", .string = PMIX_TIMEOUT, .type = PMIX_INT, .description = (char *[]){"POSITIVE INTEGERS", NULL}},
 452         {.name = ""},
 453     // connect_nb
 454         {.name = "PMIX_TIMEOUT", .string = PMIX_TIMEOUT, .type = PMIX_INT, .description = (char *[]){"POSITIVE INTEGERS", NULL}},
 455         {.name = ""},
 456     // register_event
 457         {.name = "PMIX_EVENT_AFFECTED_PROC", .string = PMIX_EVENT_AFFECTED_PROC, .type = PMIX_PROC, .description = (char *[]){"pmix_proc_t*", NULL}},
 458         {.name = "PMIX_EVENT_AFFECTED_PROCS", .string = PMIX_EVENT_AFFECTED_PROCS, .type = PMIX_DATA_ARRAY, .description = (char *[]){"Array of pmix_proc_t", NULL}},
 459         {.name = ""},
 460     // query
 461         {.name = "PMIX_QUERY_REFRESH_CACHE", .string = PMIX_QUERY_REFRESH_CACHE, .type = PMIX_BOOL, .description = (char *[]){"True,False", NULL}},
 462         {.name = "PMIX_PROCID", .string = PMIX_PROCID, .type = PMIX_PROC, .description = (char *[]){"pmix_proc_t*", NULL}},
 463         {.name = "PMIX_NSPACE", .string = PMIX_NSPACE, .type = PMIX_STRING, .description = (char *[]){"UNRESTRICTED", NULL}},
 464         {.name = "PMIX_RANK", .string = PMIX_RANK, .type = PMIX_PROC_RANK, .description = (char *[]){"UNSIGNED INT32", NULL}},
 465         {.name = "PMIX_HOSTNAME", .string = PMIX_HOSTNAME, .type = PMIX_STRING, .description = (char *[]){"UNRESTRICTED", NULL}},
 466         {.name = ""},
 467     // job_ctrl
 468         {.name = "PMIX_REGISTER_CLEANUP", .string = PMIX_REGISTER_CLEANUP, .type = PMIX_STRING, .description = (char *[]){"UNRESTRICTED", NULL}},
 469         {.name = "PMIX_REGISTER_CLEANUP_DIR", .string = PMIX_REGISTER_CLEANUP_DIR, .type = PMIX_STRING, .description = (char *[]){"UNRESTRICTED", NULL}},
 470         {.name = "PMIX_CLEANUP_RECURSIVE", .string = PMIX_CLEANUP_RECURSIVE, .type = PMIX_BOOL, .description = (char *[]){"True,False", NULL}},
 471         {.name = "PMIX_CLEANUP_IGNORE", .string = PMIX_CLEANUP_IGNORE, .type = PMIX_STRING, .description = (char *[]){"UNRESTRICTED", NULL}},
 472         {.name = "PMIX_CLEANUP_LEAVE_TOPDIR", .string = PMIX_CLEANUP_LEAVE_TOPDIR, .type = PMIX_BOOL, .description = (char *[]){"True,False", NULL}},
 473         {.name = ""},
 474     // job_ctrl_nb
 475         {.name = "PMIX_REGISTER_CLEANUP", .string = PMIX_REGISTER_CLEANUP, .type = PMIX_STRING, .description = (char *[]){"UNRESTRICTED", NULL}},
 476         {.name = "PMIX_REGISTER_CLEANUP_DIR", .string = PMIX_REGISTER_CLEANUP_DIR, .type = PMIX_STRING, .description = (char *[]){"UNRESTRICTED", NULL}},
 477         {.name = "PMIX_CLEANUP_RECURSIVE", .string = PMIX_CLEANUP_RECURSIVE, .type = PMIX_BOOL, .description = (char *[]){"True,False", NULL}},
 478         {.name = "PMIX_CLEANUP_IGNORE", .string = PMIX_CLEANUP_IGNORE, .type = PMIX_STRING, .description = (char *[]){"UNRESTRICTED", NULL}},
 479         {.name = "PMIX_CLEANUP_LEAVE_TOPDIR", .string = PMIX_CLEANUP_LEAVE_TOPDIR, .type = PMIX_BOOL, .description = (char *[]){"True,False", NULL}},
 480         {.name = ""},
 481 };
 482 
 483 /*****    REGISTER SERVER ATTRS    *****/
 484 static bool server_attrs_regd = false;
 485 
 486 PMIX_EXPORT pmix_status_t pmix_register_server_attrs(void)
 487 {
 488     size_t nregs, nattrs, n, m;
 489     size_t cnt = 0;
 490     pmix_status_t rc = PMIX_SUCCESS;
 491     pmix_regattr_t *attrs;
 492 
 493     if (server_attrs_regd) {
 494         return PMIX_SUCCESS;
 495     }
 496     server_attrs_regd = true;
 497 
 498     nregs = sizeof(server_fns) / sizeof(char*);
 499 
 500     /* we know we have to prep the GDS, PTL, BFROPS, and SEC
 501      * entries as these are dynamically defined */
 502     server_attributes[0].description[0] = pmix_gds_base_get_available_modules();
 503 
 504     for (n=0; n < nregs; n++) {
 505         nattrs = 0;
 506         while (0 != strlen(server_attributes[cnt+nattrs].name)) {
 507             ++nattrs;
 508         }
 509         PMIX_REGATTR_CREATE(attrs, nattrs);
 510         for (m=0; m < nattrs; m++) {
 511             attrs[m].name = strdup(server_attributes[m+cnt].name);
 512             PMIX_LOAD_KEY(attrs[m].string, server_attributes[m+cnt].string);
 513             attrs[m].type = server_attributes[m+cnt].type;
 514             PMIX_ARGV_COPY(attrs[m].description, server_attributes[m+cnt].description);
 515         }
 516         rc = process_reg(PMIX_SERVER_ATTRIBUTES,
 517                          server_fns[n],
 518                          attrs, nattrs);
 519         PMIX_REGATTR_FREE(attrs, nattrs);
 520         if (PMIX_SUCCESS != rc) {
 521             break;
 522         }
 523         cnt += nattrs + 1;
 524     }
 525 
 526     return PMIX_SUCCESS;
 527 }
 528 
 529 static char *tool_fns[] = {
 530     "PMIx_tool_init",
 531     "PMIx_tool_finalize",
 532     "PMIx_tool_connect_to_server"
 533 };
 534 
 535 static pmix_regattr_input_t tool_attributes[] = {
 536     // init
 537         {.name = "PMIX_GDS_MODULE", .string = PMIX_GDS_MODULE, .type = PMIX_STRING, .description = (char *[]){"UNRESTRICTED", NULL}},
 538         {.name = "PMIX_EVENT_BASE", .string = PMIX_EVENT_BASE, .type = PMIX_POINTER, .description = (char *[]){"VALID MEMORY REFERENCE", NULL}},
 539         {.name = "PMIX_HOSTNAME", .string = PMIX_HOSTNAME, .type = PMIX_STRING, .description = (char *[]){"UNRESTRICTED", NULL}},
 540         {.name = "PMIX_NODEID", .string = PMIX_NODEID, .type = PMIX_UINT32, .description = (char *[]){"POSITIVE INTEGERS", NULL}},
 541         {.name = "PMIX_TOOL_NSPACE", .string = PMIX_TOOL_NSPACE, .type = PMIX_STRING, .description = (char *[]){"UNRESTRICTED", NULL}},
 542         {.name = "PMIX_TOOL_RANK", .string = PMIX_TOOL_RANK, .type = PMIX_PROC_RANK, .description = (char *[]){"POSITIVE INTEGERS", "Rank assigned to tool", NULL}},
 543         {.name = "PMIX_TOOL_DO_NOT_CONNECT", .string = PMIX_TOOL_DO_NOT_CONNECT, .type = PMIX_BOOL, .description = (char *[]){"True,False", "Do not connect to server", NULL}},
 544         {.name = "PMIX_CONNECT_TO_SYSTEM", .string = PMIX_CONNECT_TO_SYSTEM, .type = PMIX_BOOL, .description = (char *[]){"True,False", "Connect to system PMIx server", NULL}},
 545         {.name = "PMIX_CONNECT_SYSTEM_FIRST", .string = PMIX_CONNECT_SYSTEM_FIRST, .type = PMIX_BOOL, .description = (char *[]){"True,False", "Try system PMIx server first", NULL}},
 546         {.name = "PMIX_SERVER_PIDINFO", .string = PMIX_SERVER_PIDINFO, .type = PMIX_PID, .description = (char *[]){"Valid pid_t value", "PID of target PMIx server", NULL}},
 547         {.name = "PMIX_TCP_URI", .string = PMIX_TCP_URI, .type = PMIX_STRING, .description = (char *[]){"Valid PMIx URI", "URI of PMIx server to connect to", NULL}},
 548         {.name = "PMIX_SERVER_URI", .string = PMIX_SERVER_URI, .type = PMIX_STRING, .description = (char *[]){"Valid PMIx URI", "URI of PMIx server to connect to", NULL}},
 549         {.name = "PMIX_SERVER_NSPACE", .string = PMIX_SERVER_NSPACE, .type = PMIX_STRING, .description = (char *[]){"UNRESTRICTED", "Namespace of PMIx server to", "connect to", NULL}},
 550         {.name = "PMIX_CONNECT_RETRY_DELAY", .string = PMIX_CONNECT_RETRY_DELAY, .type = PMIX_UINT32, .description = (char *[]){"POSITIVE INTEGERS", "Seconds between connection", "attempts", NULL}},
 551         {.name = "PMIX_CONNECT_MAX_RETRIES", .string = PMIX_CONNECT_MAX_RETRIES, .type = PMIX_UINT32, .description = (char *[]){"POSITIVE INTEGERS", "Max number of connection retries", NULL}},
 552         {.name = "PMIX_SOCKET_MODE", .string = PMIX_SOCKET_MODE, .type = PMIX_UINT32, .description = (char *[]){"Valid POSIX mode_t value", NULL}},
 553         {.name = "PMIX_TCP_REPORT_URI", .string = PMIX_TCP_REPORT_URI, .type = PMIX_STRING, .description = (char *[]){"-, +, or filename", NULL}},
 554         {.name = "PMIX_TCP_IF_INCLUDE", .string = PMIX_TCP_IF_INCLUDE, .type = PMIX_STRING, .description = (char *[]){"UNRESTRICTED", "Comma-separated list of", "TCP interfaces to include", NULL}},
 555         {.name = "PMIX_TCP_IF_EXCLUDE", .string = PMIX_TCP_IF_EXCLUDE, .type = PMIX_STRING, .description = (char *[]){"UNRESTRICTED", "Comma-separated list of", "TCP interfaces to exclude", NULL}},
 556         {.name = "PMIX_TCP_IPV4_PORT", .string = PMIX_TCP_IPV4_PORT, .type = PMIX_INT, .description = (char *[]){"POSITIVE INTEGERS", "IPv4 port to be used", NULL}},
 557         {.name = "PMIX_TCP_IPV6_PORT", .string = PMIX_TCP_IPV6_PORT, .type = PMIX_INT, .description = (char *[]){"POSITIVE INTEGERS", "IPv6 port to be used", NULL}},
 558         {.name = "PMIX_TCP_DISABLE_IPV4", .string = PMIX_TCP_DISABLE_IPV4, .type = PMIX_BOOL, .description = (char *[]){"True,False", "Disable IPv4 messaging interface", NULL}},
 559         {.name = "PMIX_TCP_DISABLE_IPV6", .string = PMIX_TCP_DISABLE_IPV6, .type = PMIX_BOOL, .description = (char *[]){"True,False", "Disable IPv6 messaging interface", NULL}},
 560         {.name = "PMIX_FWD_STDIN", .string = PMIX_FWD_STDIN, .type = PMIX_BOOL, .description = (char *[]){"True,False", "Forward stdin of the tool", NULL}},
 561         {.name = "PMIX_LAUNCHER", .string = PMIX_LAUNCHER, .type = PMIX_BOOL, .description = (char *[]){"True,False", "Tool is a job launcher", NULL}},
 562         {.name = "PMIX_SERVER_TMPDIR", .string = PMIX_SERVER_TMPDIR, .type = PMIX_STRING, .description = (char *[]){"UNRESTRICTED", "Path to temp directory", "assigned to tool", NULL}},
 563         {.name = "PMIX_SYSTEM_TMPDIR", .string = PMIX_SYSTEM_TMPDIR, .type = PMIX_STRING, .description = (char *[]){"UNRESTRICTED", "Path to system temp directory", NULL}},
 564         {.name = ""},
 565     // finalize
 566         {.name = ""},
 567     // connect_to_server
 568         {.name = "PMIX_CONNECT_TO_SYSTEM", .string = PMIX_CONNECT_TO_SYSTEM, .type = PMIX_BOOL, .description = (char *[]){"True,False", "Connect to system PMIx server", NULL}},
 569         {.name = "PMIX_CONNECT_SYSTEM_FIRST", .string = PMIX_CONNECT_SYSTEM_FIRST, .type = PMIX_BOOL, .description = (char *[]){"True,False", "Try system PMIx server first", NULL}},
 570         {.name = "PMIX_SERVER_PIDINFO", .string = PMIX_SERVER_PIDINFO, .type = PMIX_PID, .description = (char *[]){"Valid pid_t value", "PID of target PMIx server", NULL}},
 571         {.name = "PMIX_TCP_URI", .string = PMIX_TCP_URI, .type = PMIX_STRING, .description = (char *[]){"Valid PMIx URI", "URI of PMIx server to connect to", NULL}},
 572         {.name = "PMIX_SERVER_URI", .string = PMIX_SERVER_URI, .type = PMIX_STRING, .description = (char *[]){"Valid PMIx URI", "URI of PMIx server to connect to", NULL}},
 573         {.name = "PMIX_SERVER_NSPACE", .string = PMIX_SERVER_NSPACE, .type = PMIX_STRING, .description = (char *[]){"UNRESTRICTED", "Namespace of PMIx server to", "connect to", NULL}},
 574         {.name = ""},
 575 };
 576 
 577 /*****    REGISTER TOOL ATTRS    *****/
 578 static bool tool_attrs_regd = false;
 579 
 580 PMIX_EXPORT pmix_status_t pmix_register_tool_attrs(void)
 581 {
 582     size_t nregs, nattrs, n, m;
 583     size_t cnt = 0;
 584     pmix_status_t rc = PMIX_SUCCESS;
 585     pmix_regattr_t *attrs;
 586 
 587     if (tool_attrs_regd) {
 588         return PMIX_SUCCESS;
 589     }
 590     tool_attrs_regd = true;
 591 
 592     nregs = sizeof(tool_fns) / sizeof(char*);
 593 
 594     /* we know we have to prep the GDS, PTL, BFROPS, and SEC
 595      * entries as these are dynamically defined */
 596     tool_attributes[0].description[0] = pmix_gds_base_get_available_modules();
 597 
 598     for (n=0; n < nregs; n++) {
 599         nattrs = 0;
 600         while (0 != strlen(tool_attributes[cnt+nattrs].name)) {
 601             ++nattrs;
 602         }
 603         PMIX_REGATTR_CREATE(attrs, nattrs);
 604         for (m=0; m < nattrs; m++) {
 605             attrs[m].name = strdup(tool_attributes[m+cnt].name);
 606             PMIX_LOAD_KEY(attrs[m].string, tool_attributes[m+cnt].string);
 607             attrs[m].type = tool_attributes[m+cnt].type;
 608             PMIX_ARGV_COPY(attrs[m].description, tool_attributes[m+cnt].description);
 609         }
 610         rc = process_reg(PMIX_TOOL_ATTRIBUTES,
 611                          tool_fns[n],
 612                          attrs, nattrs);
 613         PMIX_REGATTR_FREE(attrs, nattrs);
 614         if (PMIX_SUCCESS != rc) {
 615             break;
 616         }
 617         cnt += nattrs + 1;
 618     }
 619 
 620     return PMIX_SUCCESS;
 621 }
 622 
 623 /*****   PROCESS QUERY ATTRS    *****/
 624 static void _get_attrs(pmix_list_t *lst,
 625                        pmix_info_t *info,
 626                        pmix_list_t *attrs)
 627 {
 628     pmix_attribute_trk_t *trk, *tptr;
 629     pmix_infolist_t *ip;
 630     pmix_data_array_t *darray;
 631     pmix_regattr_t *regarray;
 632     size_t m;
 633     char **fns;
 634 
 635     /* the value in the info is a comma-delimited list of
 636      * functions whose attributes are being requested */
 637     fns = pmix_argv_split(info->value.data.string, ',');
 638 
 639     /* search the list for these functions */
 640     PMIX_LIST_FOREACH(tptr, attrs, pmix_attribute_trk_t) {
 641         trk = NULL;
 642         for (m=0; NULL != fns[m] && NULL == trk; m++) {
 643             if (0 == strcmp(fns[m], tptr->function) ||
 644                 0 == strcmp(fns[m], "all")) {
 645                 trk = tptr;
 646                 break;
 647             }
 648         }
 649         if (NULL == trk || NULL == trk->attrs) {
 650             /* function wasn't found - no attrs
 651              * registered for it */
 652             continue;
 653         }
 654         /* add the found attrs to the results */
 655         ip = PMIX_NEW(pmix_infolist_t);
 656         PMIX_LOAD_KEY(ip->info.key, tptr->function);
 657         /* create the data array to hold the results */
 658         PMIX_DATA_ARRAY_CREATE(darray, trk->nattrs, PMIX_REGATTR);
 659         ip->info.value.type = PMIX_DATA_ARRAY;
 660         ip->info.value.data.darray = darray;
 661         regarray = (pmix_regattr_t*)darray->array;
 662         for (m=0; m < trk->nattrs; m++) {
 663             PMIX_REGATTR_XFER(&regarray[m], &trk->attrs[m]);
 664         }
 665         pmix_list_append(lst, &ip->super);
 666     }
 667     pmix_argv_free(fns);
 668 }
 669 
 670 static void _get_fns(pmix_list_t *lst,
 671                      pmix_info_t *info,
 672                      pmix_list_t *attrs)
 673 {
 674     pmix_attribute_trk_t *tptr;
 675     pmix_infolist_t *ip;
 676     char **fns = NULL, *tmp;
 677 
 678     /* search the list for these functions */
 679     PMIX_LIST_FOREACH(tptr, attrs, pmix_attribute_trk_t) {
 680         pmix_argv_append_nosize(&fns, tptr->function);
 681     }
 682     if (0 < pmix_argv_count(fns)) {
 683         ip = PMIX_NEW(pmix_infolist_t);
 684         tmp = pmix_argv_join(fns, ',');
 685         PMIX_INFO_LOAD(&ip->info, info->key, tmp, PMIX_STRING);
 686         pmix_list_append(lst, &ip->super);
 687         pmix_argv_free(fns);
 688     }
 689 }
 690 
 691 static void _local_relcb(void *cbdata)
 692 {
 693     pmix_query_caddy_t *cd = (pmix_query_caddy_t*)cbdata;
 694     PMIX_RELEASE(cd);
 695 }
 696 
 697 static void relcbfunc(void *cbdata)
 698 {
 699     pmix_shift_caddy_t *cd = (pmix_shift_caddy_t*)cbdata;
 700 
 701     pmix_output_verbose(2, pmix_globals.debug_output,
 702                         "pmix:query release callback");
 703 
 704     if (NULL != cd->info) {
 705         PMIX_INFO_FREE(cd->info, cd->ninfo);
 706     }
 707     PMIX_RELEASE(cd);
 708 }
 709 static void query_cbfunc(struct pmix_peer_t *peer,
 710                          pmix_ptl_hdr_t *hdr,
 711                          pmix_buffer_t *buf, void *cbdata)
 712 {
 713     pmix_query_caddy_t *cd = (pmix_query_caddy_t*)cbdata;
 714     pmix_status_t rc;
 715     pmix_shift_caddy_t *results;
 716     int cnt;
 717 
 718     pmix_output_verbose(2, pmix_globals.debug_output,
 719                         "pmix:attrs:query cback from server");
 720 
 721     results = PMIX_NEW(pmix_shift_caddy_t);
 722 
 723     /* unpack the status */
 724     cnt = 1;
 725     PMIX_BFROPS_UNPACK(rc, peer, buf, &results->status, &cnt, PMIX_STATUS);
 726     if (PMIX_SUCCESS != rc) {
 727         results->status = rc;
 728         goto complete;
 729     }
 730     if (PMIX_SUCCESS != results->status) {
 731         goto complete;
 732     }
 733 
 734     /* unpack any returned data */
 735     cnt = 1;
 736     PMIX_BFROPS_UNPACK(rc, peer, buf, &results->ninfo, &cnt, PMIX_SIZE);
 737     if (PMIX_SUCCESS != rc) {
 738         results->status = rc;
 739         goto complete;
 740     }
 741     if (0 < results->ninfo) {
 742         PMIX_INFO_CREATE(results->info, results->ninfo);
 743         cnt = results->ninfo;
 744         PMIX_BFROPS_UNPACK(rc, peer, buf, results->info, &cnt, PMIX_INFO);
 745         if (PMIX_SUCCESS != rc) {
 746             results->status = rc;
 747             goto complete;
 748         }
 749     }
 750 
 751   complete:
 752     pmix_output_verbose(2, pmix_globals.debug_output,
 753                         "pmix:query cback from server releasing");
 754     /* release the caller */
 755     if (NULL != cd->cbfunc) {
 756         cd->cbfunc(results->status, results->info, results->ninfo, cd->cbdata, relcbfunc, results);
 757     }
 758     PMIX_RELEASE(cd);
 759 }
 760 
 761 PMIX_EXPORT void pmix_attrs_query_support(int sd, short args, void *cbdata)
 762 {
 763     pmix_query_caddy_t *cd = (pmix_query_caddy_t*)cbdata;
 764     pmix_infolist_t *info, *head;
 765     pmix_list_t kyresults;
 766     size_t n, m, p;
 767     pmix_info_t *iptr;
 768     pmix_data_array_t *darray;
 769     pmix_buffer_t *msg;
 770     pmix_cmd_t cmd = PMIX_QUERY_CMD;
 771     pmix_status_t rc;
 772 
 773     PMIX_ACQUIRE_THREAD(&pmix_global_lock);
 774 
 775     for (n=0; n < cd->nqueries; n++) {
 776         if (0 != strcmp(cd->queries[n].keys[0], PMIX_QUERY_ATTRIBUTE_SUPPORT)) {
 777             /* skip this one */
 778             continue;
 779         }
 780         head = NULL;
 781         for (m=0; m < cd->queries[n].nqual; m++) {
 782             PMIX_CONSTRUCT(&kyresults, pmix_list_t);
 783             if (NULL == cd->queries[n].qualifiers ||
 784                 PMIX_CHECK_KEY(&cd->queries[n].qualifiers[m], PMIX_CLIENT_ATTRIBUTES)) {
 785                 /* everyone has access to the client attrs */
 786                 _get_attrs(&kyresults, &cd->queries[n].qualifiers[m], &client_attrs);
 787             }
 788             if (NULL == cd->queries[n].qualifiers ||
 789                 PMIX_CHECK_KEY(&cd->queries[n].qualifiers[m], PMIX_CLIENT_FUNCTIONS)) {
 790                 /* everyone has access to the client functions */
 791                 _get_fns(&kyresults, &cd->queries[n].qualifiers[m], &client_attrs);
 792             }
 793             if (NULL == cd->queries[n].qualifiers ||
 794                 PMIX_CHECK_KEY(&cd->queries[n].qualifiers[m], PMIX_SERVER_ATTRIBUTES)) {
 795                 /* if I am a server, add in my attrs */
 796                 if (PMIX_PROC_IS_SERVER(pmix_globals.mypeer)) {
 797                     _get_attrs(&kyresults, &cd->queries[n].qualifiers[m], &server_attrs);
 798                  } else {
 799                     /* we need to ask our server for them */
 800                     PMIX_LIST_DESTRUCT(&kyresults);
 801                     goto query;
 802                 }
 803             }
 804             if (NULL == cd->queries[n].qualifiers ||
 805                 PMIX_CHECK_KEY(&cd->queries[n].qualifiers[m], PMIX_SERVER_FUNCTIONS)) {
 806                 /* if I am a server, add in my fns */
 807                 if (PMIX_PROC_IS_SERVER(pmix_globals.mypeer)) {
 808                     _get_fns(&kyresults, &cd->queries[n].qualifiers[m], &server_attrs);
 809                  } else {
 810                     /* we need to ask our server for them */
 811                     PMIX_LIST_DESTRUCT(&kyresults);
 812                     goto query;
 813                 }
 814             }
 815             if (NULL == cd->queries[n].qualifiers ||
 816                 PMIX_CHECK_KEY(&cd->queries[n].qualifiers[m], PMIX_TOOL_ATTRIBUTES)) {
 817                 if (PMIX_PROC_IS_TOOL(pmix_globals.mypeer)) {
 818                     _get_attrs(&kyresults, &cd->queries[n].qualifiers[m], &tool_attrs);
 819                 }
 820             }
 821             if (NULL == cd->queries[n].qualifiers ||
 822                 PMIX_CHECK_KEY(&cd->queries[n].qualifiers[m], PMIX_TOOL_FUNCTIONS)) {
 823                 if (PMIX_PROC_IS_TOOL(pmix_globals.mypeer)) {
 824                     _get_fns(&kyresults, &cd->queries[n].qualifiers[m], &tool_attrs);
 825                 }
 826             }
 827             if (NULL == cd->queries[n].qualifiers ||
 828                 PMIX_CHECK_KEY(&cd->queries[n].qualifiers[m], PMIX_HOST_ATTRIBUTES)) {
 829                 /* if I am a server, add in the host's */
 830                 if (PMIX_PROC_IS_SERVER(pmix_globals.mypeer)) {
 831                     _get_attrs(&kyresults, &cd->queries[n].qualifiers[m], &host_attrs);
 832                 } else {
 833                     /* we need to ask our server for them */
 834                     PMIX_LIST_DESTRUCT(&kyresults);
 835                     goto query;
 836                 }
 837             }
 838             if (NULL == cd->queries[n].qualifiers ||
 839                 PMIX_CHECK_KEY(&cd->queries[n].qualifiers[m], PMIX_HOST_FUNCTIONS)) {
 840                 /* if I am a server, add in the host's */
 841                 if (PMIX_PROC_IS_SERVER(pmix_globals.mypeer)) {
 842                     _get_fns(&kyresults, &cd->queries[n].qualifiers[m], &host_attrs);
 843                 } else {
 844                     /* we need to ask our server for them */
 845                     PMIX_LIST_DESTRUCT(&kyresults);
 846                     goto query;
 847                 }
 848             }
 849             if (0 < (p = pmix_list_get_size(&kyresults))) {
 850                 head = PMIX_NEW(pmix_infolist_t);
 851                 PMIX_LOAD_KEY(head->info.key, cd->queries[n].keys[m]);
 852                 head->info.value.type = PMIX_DATA_ARRAY;
 853                 /* create the data array to hold the results */
 854                 PMIX_DATA_ARRAY_CREATE(darray, p, PMIX_INFO);
 855                 head->info.value.data.darray = darray;
 856                 iptr = (pmix_info_t*)darray->array;
 857                 p = 0;
 858                 PMIX_LIST_FOREACH(info, &kyresults, pmix_infolist_t) {
 859                     PMIX_INFO_XFER(&iptr[p], &info->info);
 860                     ++p;
 861                 }
 862                 pmix_list_append(&cd->results, &head->super);
 863             }
 864             PMIX_LIST_DESTRUCT(&kyresults);
 865         }
 866     }
 867     /* prep the response by converting the list
 868      * of results into an array */
 869     if (0 < (cd->ninfo = pmix_list_get_size(&cd->results))) {
 870         PMIX_INFO_CREATE(cd->info, cd->ninfo);
 871         n = 0;
 872         PMIX_LIST_FOREACH(info, &cd->results, pmix_infolist_t) {
 873             PMIX_INFO_XFER(&cd->info[n], &info->info);
 874             ++n;
 875         }
 876         cd->status = PMIX_SUCCESS;
 877     } else {
 878         cd->status = PMIX_ERR_NOT_FOUND;
 879     }
 880     PMIX_RELEASE_THREAD(&pmix_global_lock);
 881     goto release;
 882 
 883   query:
 884     /* if we aren't connected, don't attempt to send */
 885     if (!pmix_globals.connected) {
 886         PMIX_RELEASE_THREAD(&pmix_global_lock);
 887         cd->status = PMIX_ERR_NOT_FOUND;
 888         goto release;
 889     }
 890     PMIX_RELEASE_THREAD(&pmix_global_lock);
 891 
 892     /* relay this request to the server */
 893     msg = PMIX_NEW(pmix_buffer_t);
 894     PMIX_BFROPS_PACK(rc, pmix_client_globals.myserver,
 895                      msg, &cmd, 1, PMIX_COMMAND);
 896     if (PMIX_SUCCESS != rc) {
 897         PMIX_RELEASE(msg);
 898         cd->status = rc;
 899         goto release;
 900     }
 901     PMIX_BFROPS_PACK(rc, pmix_client_globals.myserver,
 902                      msg, &cd->nqueries, 1, PMIX_SIZE);
 903     if (PMIX_SUCCESS != rc) {
 904         PMIX_RELEASE(msg);
 905         cd->status = rc;
 906         goto release;
 907     }
 908     PMIX_BFROPS_PACK(rc, pmix_client_globals.myserver,
 909                      msg, cd->queries, cd->nqueries, PMIX_QUERY);
 910     if (PMIX_SUCCESS != rc) {
 911         PMIX_RELEASE(msg);
 912         cd->status = rc;
 913         goto release;
 914     }
 915 
 916     pmix_output_verbose(2, pmix_globals.debug_output,
 917                         "pmix:query sending to server");
 918     PMIX_PTL_SEND_RECV(rc, pmix_client_globals.myserver,
 919                        msg, query_cbfunc, (void*)cd);
 920     if (PMIX_SUCCESS != rc) {
 921         cd->status = rc;
 922         goto release;
 923     }
 924     return;
 925 
 926   release:
 927     if (NULL != cd->cbfunc) {
 928         cd->cbfunc(cd->status, cd->info, cd->ninfo, cd, _local_relcb, cd);
 929         return;
 930     }
 931 
 932     PMIX_RELEASE(cd);
 933 }
 934 
 935 /*****   PRINT QUERY FUNCTIONS RESULTS   *****/
 936 PMIX_EXPORT char** pmix_attributes_print_functions(char *level)
 937 {
 938     char *title1 = "CLIENT SUPPORTED FUNCTIONS: ";
 939     char *title2 = "SERVER SUPPORTED FUNCTIONS: ";
 940     char *title3 = "HOST SUPPORTED FUNCTIONS: ";
 941     char *title4 = "TOOL SUPPORTED FUNCTIONS: ";
 942     char **ans = NULL;
 943     pmix_list_t *lst;
 944     pmix_attribute_trk_t *fnptr;
 945 
 946     /* select title */
 947     if (0 == strcmp(level, PMIX_CLIENT_FUNCTIONS)) {
 948         pmix_argv_append_nosize(&ans, title1);
 949         lst = &client_attrs;
 950     } else if (0 == strcmp(level, PMIX_SERVER_FUNCTIONS)) {
 951         pmix_argv_append_nosize(&ans, title2);
 952         lst = &server_attrs;
 953     } else if (0 == strcmp(level, PMIX_HOST_FUNCTIONS)) {
 954         pmix_argv_append_nosize(&ans, title3);
 955         lst = &host_attrs;
 956     } else if (0 == strcmp(level, PMIX_TOOL_FUNCTIONS)) {
 957         pmix_argv_append_nosize(&ans, title4);
 958         lst = &tool_attrs;
 959     } else {
 960         return NULL;
 961     }
 962 
 963     PMIX_LIST_FOREACH(fnptr, lst, pmix_attribute_trk_t) {
 964         pmix_argv_append_nosize(&ans, fnptr->function);
 965     }
 966     return ans;
 967 }
 968 
 969 /*****   PRINT QUERY ATTRS RESULTS   *****/
 970 
 971 #define PMIX_PRINT_NAME_COLUMN_WIDTH      30
 972 #define PMIX_PRINT_STRING_COLUMN_WIDTH    30
 973 #define PMIX_PRINT_TYPE_COLUMN_WIDTH      20
 974 #define PMIX_PRINT_ATTR_COLUMN_WIDTH     120
 975 
 976 void pmix_attributes_print_attrs(char ***ans, char *function,
 977                                  pmix_regattr_t *attrs,
 978                                  size_t nattrs)
 979 {
 980     char line[PMIX_PRINT_ATTR_COLUMN_WIDTH], *tmp;
 981     size_t n, m, len;
 982 
 983     /* print the function */
 984     memset(line, ' ', PMIX_PRINT_ATTR_COLUMN_WIDTH);
 985     m = 0;
 986     for (n=0; n < strlen(function); n++) {
 987         line[m] = function[n];
 988         ++m;
 989     }
 990     line[m++] = ':';
 991     line[m] = '\0';
 992     pmix_argv_append_nosize(ans, line);
 993 
 994     for (n=0; n < nattrs; n++) {
 995         memset(line, ' ', PMIX_PRINT_ATTR_COLUMN_WIDTH);
 996         line[PMIX_PRINT_ATTR_COLUMN_WIDTH-1] = '\0';
 997         len = strlen(attrs[n].name);
 998         if (PMIX_PRINT_NAME_COLUMN_WIDTH < len) {
 999             len = PMIX_PRINT_NAME_COLUMN_WIDTH;
1000         }
1001         memcpy(line, attrs[n].name, len);
1002 
1003         len = strlen(attrs[n].string);
1004         if (PMIX_PRINT_STRING_COLUMN_WIDTH < len) {
1005             len = PMIX_PRINT_STRING_COLUMN_WIDTH;
1006         }
1007         memcpy(&line[PMIX_PRINT_NAME_COLUMN_WIDTH+2], attrs[n].string, len);
1008 
1009         tmp = (char*)PMIx_Data_type_string(attrs[n].type);
1010         len = strlen(tmp);
1011         if (PMIX_PRINT_STRING_COLUMN_WIDTH < len) {
1012             len = PMIX_PRINT_STRING_COLUMN_WIDTH;
1013         }
1014         memcpy(&line[PMIX_PRINT_NAME_COLUMN_WIDTH+PMIX_PRINT_STRING_COLUMN_WIDTH+4], tmp, len);
1015 
1016         len = strlen(attrs[n].description[0]);
1017         if ((PMIX_PRINT_ATTR_COLUMN_WIDTH-PMIX_PRINT_NAME_COLUMN_WIDTH-PMIX_PRINT_STRING_COLUMN_WIDTH-PMIX_PRINT_TYPE_COLUMN_WIDTH-6) < len) {
1018             len = PMIX_PRINT_ATTR_COLUMN_WIDTH-PMIX_PRINT_NAME_COLUMN_WIDTH-PMIX_PRINT_STRING_COLUMN_WIDTH-PMIX_PRINT_TYPE_COLUMN_WIDTH-6;
1019         }
1020         memcpy(&line[PMIX_PRINT_NAME_COLUMN_WIDTH+PMIX_PRINT_STRING_COLUMN_WIDTH+PMIX_PRINT_TYPE_COLUMN_WIDTH+6], attrs[n].description[0], len);
1021         line[PMIX_PRINT_ATTR_COLUMN_WIDTH-1] = '\0';  // ensure NULL termination
1022         pmix_argv_append_nosize(ans, line);
1023 
1024         for (m=1; NULL != attrs[n].description[m]; m++) {
1025             memset(line, ' ', PMIX_PRINT_ATTR_COLUMN_WIDTH);
1026             line[PMIX_PRINT_ATTR_COLUMN_WIDTH-1] = '\0';
1027             len = strlen(attrs[n].description[m]);
1028             if ((PMIX_PRINT_ATTR_COLUMN_WIDTH-PMIX_PRINT_NAME_COLUMN_WIDTH-PMIX_PRINT_STRING_COLUMN_WIDTH-PMIX_PRINT_TYPE_COLUMN_WIDTH-6) < len) {
1029                 len = PMIX_PRINT_ATTR_COLUMN_WIDTH-PMIX_PRINT_NAME_COLUMN_WIDTH-PMIX_PRINT_STRING_COLUMN_WIDTH-PMIX_PRINT_TYPE_COLUMN_WIDTH-6;
1030             }
1031             memcpy(&line[PMIX_PRINT_NAME_COLUMN_WIDTH+PMIX_PRINT_STRING_COLUMN_WIDTH+PMIX_PRINT_TYPE_COLUMN_WIDTH+6], attrs[n].description[m], len);
1032             line[PMIX_PRINT_ATTR_COLUMN_WIDTH-1] = '\0';  // ensure NULL termination
1033             pmix_argv_append_nosize(ans, line);
1034         }
1035     }
1036 }
1037 
1038 void pmix_attributes_print_headers(char ***ans, char *level)
1039 {
1040     size_t n, m, left;
1041     char *title1 = "CLIENT SUPPORTED ATTRIBUTES: ";
1042     char *title2 = "SERVER SUPPORTED ATTRIBUTES: ";
1043     char *title3 = "HOST SUPPORTED ATTRIBUTES: ";
1044     char *title4 = "TOOL SUPPORTED ATTRIBUTES: ";
1045     char line[PMIX_PRINT_ATTR_COLUMN_WIDTH];
1046 
1047     /* select title */
1048     if (0 == strcmp(level, PMIX_CLIENT_ATTRIBUTES)) {
1049         pmix_argv_append_nosize(ans, title1);
1050     } else if (0 == strcmp(level, PMIX_SERVER_ATTRIBUTES)) {
1051         pmix_argv_append_nosize(ans, title2);
1052     } else if (0 == strcmp(level, PMIX_HOST_ATTRIBUTES)) {
1053         pmix_argv_append_nosize(ans, title3);
1054     } else if (0 == strcmp(level, PMIX_TOOL_ATTRIBUTES)) {
1055         pmix_argv_append_nosize(ans, title4);
1056     } else {
1057         return;
1058     }
1059 
1060     /* print the column headers */
1061     memset(line, ' ', PMIX_PRINT_ATTR_COLUMN_WIDTH);
1062     line[PMIX_PRINT_ATTR_COLUMN_WIDTH-1] = '\0';
1063     left = PMIX_PRINT_NAME_COLUMN_WIDTH/2 - 1;
1064     memcpy(&line[left], "NAME", 4);
1065 
1066     left = 3 + PMIX_PRINT_NAME_COLUMN_WIDTH + (PMIX_PRINT_STRING_COLUMN_WIDTH/2) - 2;
1067     memcpy(&line[left], "STRING", 6);
1068 
1069     left = 3 + PMIX_PRINT_NAME_COLUMN_WIDTH + PMIX_PRINT_STRING_COLUMN_WIDTH + (PMIX_PRINT_TYPE_COLUMN_WIDTH/2) - 2;
1070     memcpy(&line[left], "TYPE", 4);
1071 
1072     left = PMIX_PRINT_NAME_COLUMN_WIDTH + PMIX_PRINT_STRING_COLUMN_WIDTH + PMIX_PRINT_TYPE_COLUMN_WIDTH +
1073            ((PMIX_PRINT_ATTR_COLUMN_WIDTH-PMIX_PRINT_NAME_COLUMN_WIDTH-PMIX_PRINT_STRING_COLUMN_WIDTH-PMIX_PRINT_TYPE_COLUMN_WIDTH)/2) - 3 - strlen("DESCRIPTION")/2;
1074     memcpy(&line[left], "DESCRIPTION", strlen("DESCRIPTION"));
1075     pmix_argv_append_nosize(ans, line);
1076 
1077     /* print the dashes under the column headers */
1078     memset(line, ' ', PMIX_PRINT_ATTR_COLUMN_WIDTH);
1079     line[PMIX_PRINT_ATTR_COLUMN_WIDTH-1] = '\0';
1080     m=0;
1081     for (n=0; n < PMIX_PRINT_NAME_COLUMN_WIDTH; n++) {
1082         line[m] = '-';
1083         ++m;
1084     }
1085     m += 2; // leave gap
1086     for (n=0; n < PMIX_PRINT_STRING_COLUMN_WIDTH; n++) {
1087         line[m] = '-';
1088         ++m;
1089     }
1090     m += 2; // leave gap
1091     for (n=0; n < PMIX_PRINT_TYPE_COLUMN_WIDTH; n++) {
1092         line[m] = '-';
1093         ++m;
1094     }
1095     m += 2; // leave gap
1096     while (m < PMIX_PRINT_ATTR_COLUMN_WIDTH-1) {
1097         line[m] = '-';
1098         ++m;
1099     }
1100     pmix_argv_append_nosize(ans, line);
1101 }
1102 
1103 PMIX_EXPORT char** pmix_attributes_print_attr(char *level, char *function)
1104 {
1105     size_t n;
1106     char **tmp, **ans=NULL;
1107     pmix_list_t *lst;
1108     pmix_attribute_trk_t *fnptr;
1109     char line[PMIX_PRINT_ATTR_COLUMN_WIDTH];
1110 
1111     /* select title */
1112     if (0 == strcmp(level, PMIX_CLIENT_ATTRIBUTES)) {
1113         lst = &client_attrs;
1114     } else if (0 == strcmp(level, PMIX_SERVER_ATTRIBUTES)) {
1115         lst = &server_attrs;
1116     } else if (0 == strcmp(level, PMIX_HOST_ATTRIBUTES)) {
1117         lst = &host_attrs;
1118     } else if (0 == strcmp(level, PMIX_TOOL_ATTRIBUTES)) {
1119         lst = &tool_attrs;
1120     } else {
1121         return NULL;
1122     }
1123 
1124     /* print the column headers */
1125     pmix_attributes_print_headers(&ans, level);
1126     memset(line, ' ', PMIX_PRINT_ATTR_COLUMN_WIDTH);
1127     line[1] = '\0';
1128 
1129     /* can be comma-delimited list of functions */
1130     tmp = pmix_argv_split(function, ',');
1131     for (n=0; NULL != tmp[n]; n++) {
1132         PMIX_LIST_FOREACH(fnptr, lst, pmix_attribute_trk_t) {
1133             if (0 == strcmp(tmp[n], "all")) {
1134                 pmix_attributes_print_attrs(&ans, fnptr->function, fnptr->attrs, fnptr->nattrs);
1135                 pmix_argv_append_nosize(&ans, line);
1136             } else if (0 == strcmp(tmp[n], fnptr->function)) {
1137                 pmix_attributes_print_attrs(&ans, fnptr->function, fnptr->attrs, fnptr->nattrs);
1138                 break;
1139             }
1140         }
1141     }
1142     pmix_argv_free(tmp);
1143 
1144 
1145     return ans;
1146 }

/* [<][>][^][v][top][bottom][index][help] */