root/opal/mca/pmix/pmix4x/pmix/src/mca/preg/native/preg_native.c

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

DEFINITIONS

This source file includes following definitions.
  1. generate_node_regex
  2. generate_ppn
  3. parse_nodes
  4. parse_procs
  5. resolve_peers
  6. resolve_nodes
  7. pmix_regex_extract_nodes
  8. regex_parse_value_ranges
  9. regex_parse_value_range
  10. pmix_regex_extract_ppn

   1 /*
   2  * Copyright (c) 2015-2018 Intel, Inc.  All rights reserved.
   3  * Copyright (c) 2016-2019 IBM Corporation.  All rights reserved.
   4  * Copyright (c) 2018      Research Organization for Information Science
   5  *                         and Technology (RIST).  All rights reserved.
   6  *
   7  * $COPYRIGHT$
   8  *
   9  * Additional copyrights may follow
  10  *
  11  * $HEADER$
  12  */
  13 
  14 #include <src/include/pmix_config.h>
  15 
  16 #ifdef HAVE_STRING_H
  17 #include <string.h>
  18 #endif
  19 #include <fcntl.h>
  20 #ifdef HAVE_UNISTD_H
  21 #include <unistd.h>
  22 #endif
  23 #ifdef HAVE_SYS_TYPES_H
  24 #include <sys/types.h>
  25 #endif
  26 #include <ctype.h>
  27 
  28 
  29 #include <pmix_common.h>
  30 #include <pmix.h>
  31 
  32 #include "src/include/pmix_socket_errno.h"
  33 #include "src/include/pmix_globals.h"
  34 #include "src/util/argv.h"
  35 #include "src/util/error.h"
  36 #include "src/util/output.h"
  37 #include "src/class/pmix_list.h"
  38 #include "src/mca/gds/gds.h"
  39 #include "src/client/pmix_client_ops.h"
  40 
  41 #include "src/mca/preg/preg.h"
  42 #include "preg_native.h"
  43 
  44 static pmix_status_t generate_node_regex(const char *input,
  45                                          char **regex);
  46 static pmix_status_t generate_ppn(const char *input,
  47                                   char **ppn);
  48 static pmix_status_t parse_nodes(const char *regexp,
  49                                  char ***names);
  50 static pmix_status_t parse_procs(const char *regexp,
  51                                  char ***procs);
  52 static pmix_status_t resolve_peers(const char *nodename,
  53                                    const char *nspace,
  54                                    pmix_proc_t **procs, size_t *nprocs);
  55 static pmix_status_t resolve_nodes(const char *nspace,
  56                                    char **nodelist);
  57 
  58 pmix_preg_module_t pmix_preg_native_module = {
  59     .name = "pmix",
  60     .generate_node_regex = generate_node_regex,
  61     .generate_ppn = generate_ppn,
  62     .parse_nodes = parse_nodes,
  63     .parse_procs = parse_procs,
  64     .resolve_peers = resolve_peers,
  65     .resolve_nodes = resolve_nodes
  66 };
  67 
  68 static pmix_status_t regex_parse_value_ranges(char *base, char *ranges,
  69                                               int num_digits, char *suffix,
  70                                               char ***names);
  71 static pmix_status_t regex_parse_value_range(char *base, char *range,
  72                                              int num_digits, char *suffix,
  73                                              char ***names);
  74 static pmix_status_t pmix_regex_extract_nodes(char *regexp, char ***names);
  75 static pmix_status_t pmix_regex_extract_ppn(char *regexp, char ***procs);
  76 
  77 
  78 static pmix_status_t generate_node_regex(const char *input,
  79                                          char **regexp)
  80 {
  81     char *vptr, *vsave;
  82     char prefix[PMIX_MAX_NODE_PREFIX];
  83     int i, j, len, startnum, vnum, numdigits;
  84     bool found, fullval;
  85     char *suffix, *sfx;
  86     pmix_regex_value_t *vreg;
  87     pmix_regex_range_t *range;
  88     pmix_list_t vids;
  89     char **regexargs = NULL, *tmp, *tmp2;
  90     char *cptr;
  91 
  92     /* define the default */
  93     *regexp = NULL;
  94 
  95     /* setup the list of results */
  96     PMIX_CONSTRUCT(&vids, pmix_list_t);
  97 
  98     /* cycle thru the array of input values - first copy
  99      * it so we don't overwrite what we were given*/
 100     vsave = strdup(input);
 101     vptr = vsave;
 102     while (NULL != (cptr = strchr(vptr, ',')) || 0 < strlen(vptr)) {
 103         if (NULL != cptr) {
 104             *cptr = '\0';
 105         }
 106         /* determine this node's prefix by looking for first non-alpha char */
 107         fullval = false;
 108         len = strlen(vptr);
 109         startnum = -1;
 110         memset(prefix, 0, PMIX_MAX_NODE_PREFIX);
 111         for (i=0, j=0; i < len; i++) {
 112             if (!isalpha(vptr[i])) {
 113                 /* found a non-alpha char */
 114                 if (!isdigit(vptr[i])) {
 115                     /* if it is anything but a digit, we just use
 116                      * the entire name
 117                      */
 118                     fullval = true;
 119                     break;
 120                 }
 121                 /* count the size of the numeric field - but don't
 122                  * add the digits to the prefix
 123                  */
 124                 if (startnum < 0) {
 125                     /* okay, this defines end of the prefix */
 126                     startnum = i;
 127                 }
 128                 continue;
 129             }
 130             if (startnum < 0) {
 131                 prefix[j++] = vptr[i];
 132             }
 133         }
 134         if (fullval || startnum < 0) {
 135             /* can't compress this name - just add it to the list */
 136             vreg = PMIX_NEW(pmix_regex_value_t);
 137             vreg->prefix = strdup(vptr);
 138             pmix_list_append(&vids, &vreg->super);
 139             /* move to the next posn */
 140             if (NULL == cptr) {
 141                 break;
 142             }
 143             vptr = cptr + 1;
 144             continue;
 145         }
 146         /* convert the digits and get any suffix */
 147         vnum = strtol(&vptr[startnum], &sfx, 10);
 148         if (NULL != sfx) {
 149             suffix = strdup(sfx);
 150             numdigits = (int)(sfx - &vptr[startnum]);
 151         } else {
 152             suffix = NULL;
 153             numdigits = (int)strlen(&vptr[startnum]);
 154         }
 155 
 156         /* is this value already on our list? */
 157         found = false;
 158         PMIX_LIST_FOREACH(vreg, &vids, pmix_regex_value_t) {
 159             // The regex must preserve ordering of the values.
 160             // If we disqualified this entry in a previous check then exclude it
 161             // from future checks as well. This will prevent a later entry from
 162             // being 'pulled forward' accidentally. For example, given:
 163             // "a28n01,a99n02,a28n02"
 164             // Without this 'skip' the loop would have 'a28n02' combine with
 165             // 'a28n01' jumping over the 'a99n02' entry, and thus not preserving
 166             // the order of the list when the regex is unpacked.
 167             if( vreg->skip ) {
 168                 continue;
 169             }
 170 
 171             if (0 < strlen(prefix) && NULL == vreg->prefix) {
 172                 continue;
 173             }
 174             if (0 == strlen(prefix) && NULL != vreg->prefix) {
 175                 continue;
 176             }
 177             if (0 < strlen(prefix) && NULL != vreg->prefix
 178                 && 0 != strcmp(prefix, vreg->prefix)) {
 179                 vreg->skip = true;
 180                 continue;
 181             }
 182             if (NULL == suffix && NULL != vreg->suffix) {
 183                 continue;
 184             }
 185             if (NULL != suffix && NULL == vreg->suffix) {
 186                 continue;
 187             }
 188             if (NULL != suffix && NULL != vreg->suffix &&
 189                 0 != strcmp(suffix, vreg->suffix)) {
 190                 vreg->skip = true;
 191                 continue;
 192             }
 193             if (numdigits != vreg->num_digits) {
 194                 vreg->skip = true;
 195                 continue;
 196             }
 197             /* found a match - flag it */
 198             found = true;
 199             /* get the last range on this nodeid - we do this
 200              * to preserve order
 201              */
 202             range = (pmix_regex_range_t*)pmix_list_get_last(&vreg->ranges);
 203             if (NULL == range) {
 204                 /* first range for this value */
 205                 range = PMIX_NEW(pmix_regex_range_t);
 206                 range->start = vnum;
 207                 range->cnt = 1;
 208                 pmix_list_append(&vreg->ranges, &range->super);
 209                 break;
 210             }
 211             /* see if the value is out of sequence */
 212             if (vnum != (range->start + range->cnt)) {
 213                 /* start a new range */
 214                 range = PMIX_NEW(pmix_regex_range_t);
 215                 range->start = vnum;
 216                 range->cnt = 1;
 217                 pmix_list_append(&vreg->ranges, &range->super);
 218                 break;
 219             }
 220             /* everything matches - just increment the cnt */
 221             range->cnt++;
 222             break;
 223         }
 224         if (!found) {
 225             /* need to add it */
 226             vreg = PMIX_NEW(pmix_regex_value_t);
 227             if (0 < strlen(prefix)) {
 228                 vreg->prefix = strdup(prefix);
 229             }
 230             if (NULL != suffix) {
 231                 vreg->suffix = strdup(suffix);
 232             }
 233             vreg->num_digits = numdigits;
 234             pmix_list_append(&vids, &vreg->super);
 235             /* record the first range for this value - we took
 236              * care of values we can't compress above
 237              */
 238             range = PMIX_NEW(pmix_regex_range_t);
 239             range->start = vnum;
 240             range->cnt = 1;
 241             pmix_list_append(&vreg->ranges, &range->super);
 242         }
 243         if (NULL != suffix) {
 244             free(suffix);
 245         }
 246         /* move to the next posn */
 247         if (NULL == cptr) {
 248             break;
 249         }
 250         vptr = cptr + 1;
 251     }
 252     free(vsave);
 253 
 254     /* begin constructing the regular expression */
 255     while (NULL != (vreg = (pmix_regex_value_t*)pmix_list_remove_first(&vids))) {
 256         /* if no ranges, then just add the name */
 257         if (0 == pmix_list_get_size(&vreg->ranges)) {
 258             if (NULL != vreg->prefix) {
 259                 pmix_argv_append_nosize(&regexargs, vreg->prefix);
 260             }
 261             PMIX_RELEASE(vreg);
 262             continue;
 263         }
 264         /* start the regex for this value with the prefix */
 265         if (NULL != vreg->prefix) {
 266             if (0 > asprintf(&tmp, "%s[%d:", vreg->prefix, vreg->num_digits)) {
 267                 return PMIX_ERR_NOMEM;
 268             }
 269         } else {
 270             if (0 > asprintf(&tmp, "[%d:", vreg->num_digits)) {
 271                 return PMIX_ERR_NOMEM;
 272             }
 273         }
 274         /* add the ranges */
 275         while (NULL != (range = (pmix_regex_range_t*)pmix_list_remove_first(&vreg->ranges))) {
 276             if (1 == range->cnt) {
 277                 if (0 > asprintf(&tmp2, "%s%d,", tmp, range->start)) {
 278                     return PMIX_ERR_NOMEM;
 279                 }
 280             } else {
 281                 if (0 > asprintf(&tmp2, "%s%d-%d,", tmp, range->start, range->start + range->cnt - 1)) {
 282                     return PMIX_ERR_NOMEM;
 283                 }
 284             }
 285             free(tmp);
 286             tmp = tmp2;
 287             PMIX_RELEASE(range);
 288         }
 289         /* replace the final comma */
 290         tmp[strlen(tmp)-1] = ']';
 291         if (NULL != vreg->suffix) {
 292             /* add in the suffix, if provided */
 293             if (0 > asprintf(&tmp2, "%s%s", tmp, vreg->suffix)) {
 294                 return PMIX_ERR_NOMEM;
 295             }
 296             free(tmp);
 297             tmp = tmp2;
 298         }
 299         pmix_argv_append_nosize(&regexargs, tmp);
 300         free(tmp);
 301         PMIX_RELEASE(vreg);
 302     }
 303 
 304     /* assemble final result */
 305     tmp = pmix_argv_join(regexargs, ',');
 306     if (0 > asprintf(regexp, "pmix[%s]", tmp)) {
 307         return PMIX_ERR_NOMEM;
 308     }
 309     free(tmp);
 310 
 311     /* cleanup */
 312     pmix_argv_free(regexargs);
 313 
 314     PMIX_DESTRUCT(&vids);
 315     return PMIX_SUCCESS;
 316 }
 317 
 318 static pmix_status_t generate_ppn(const char *input,
 319                                   char **regexp)
 320 {
 321     char **ppn, **npn;
 322     int i, j, start, end;
 323     pmix_regex_value_t *vreg;
 324     pmix_regex_range_t *rng;
 325     pmix_list_t nodes;
 326     char *tmp, *tmp2;
 327     char *cptr;
 328 
 329     /* define the default */
 330     *regexp = NULL;
 331 
 332     /* setup the list of results */
 333     PMIX_CONSTRUCT(&nodes, pmix_list_t);
 334 
 335     /* split the input by node */
 336     ppn = pmix_argv_split(input, ';');
 337 
 338     /* for each node, split the input by comma */
 339     for (i=0; NULL != ppn[i]; i++) {
 340         rng = NULL;
 341         /* create a record for this node */
 342         vreg = PMIX_NEW(pmix_regex_value_t);
 343         pmix_list_append(&nodes, &vreg->super);
 344         /* split the input for this node */
 345         npn = pmix_argv_split(ppn[i], ',');
 346         /* look at each element */
 347         for (j=0; NULL != npn[j]; j++) {
 348             /* is this a range? */
 349             if (NULL != (cptr = strchr(npn[j], '-'))) {
 350                 /* terminate the string */
 351                 *cptr = '\0';
 352                 ++cptr;
 353                 start = strtol(npn[j], NULL, 10);
 354                 end = strtol(cptr, NULL, 10);
 355                 /* are we collecting a range? */
 356                 if (NULL == rng) {
 357                     /* no - better start one */
 358                     rng = PMIX_NEW(pmix_regex_range_t);
 359                     rng->start = start;
 360                     rng->cnt = end - start + 1;
 361                     pmix_list_append(&vreg->ranges, &rng->super);
 362                 } else {
 363                     /* is this a continuation of the current range? */
 364                     if (start == (rng->start + rng->cnt)) {
 365                         /* just add it to the end of this range */
 366                         rng->cnt++;
 367                     } else {
 368                         /* nope, there is a break - create new range */
 369                         rng = PMIX_NEW(pmix_regex_range_t);
 370                         rng->start = start;
 371                         rng->cnt = end - start + 1;
 372                         pmix_list_append(&vreg->ranges, &rng->super);
 373                     }
 374                 }
 375             } else {
 376                 /* single rank given */
 377                 start = strtol(npn[j], NULL, 10);
 378                 /* are we collecting a range? */
 379                 if (NULL == rng) {
 380                     /* no - better start one */
 381                     rng = PMIX_NEW(pmix_regex_range_t);
 382                     rng->start = start;
 383                     rng->cnt = 1;
 384                     pmix_list_append(&vreg->ranges, &rng->super);
 385                 } else {
 386                     /* is this a continuation of the current range? */
 387                     if (start == (rng->start + rng->cnt)) {
 388                         /* just add it to the end of this range */
 389                         rng->cnt++;
 390                     } else {
 391                         /* nope, there is a break - create new range */
 392                         rng = PMIX_NEW(pmix_regex_range_t);
 393                         rng->start = start;
 394                         rng->cnt = 1;
 395                         pmix_list_append(&vreg->ranges, &rng->super);
 396                     }
 397                 }
 398             }
 399         }
 400         pmix_argv_free(npn);
 401     }
 402     pmix_argv_free(ppn);
 403 
 404 
 405     /* begin constructing the regular expression */
 406     tmp = strdup("pmix[");
 407     PMIX_LIST_FOREACH(vreg, &nodes, pmix_regex_value_t) {
 408         while (NULL != (rng = (pmix_regex_range_t*)pmix_list_remove_first(&vreg->ranges))) {
 409             if (1 == rng->cnt) {
 410                 if (0 > asprintf(&tmp2, "%s%d,", tmp, rng->start)) {
 411                     return PMIX_ERR_NOMEM;
 412                 }
 413             } else {
 414                 if (0 > asprintf(&tmp2, "%s%d-%d,", tmp, rng->start, rng->start + rng->cnt - 1)) {
 415                     return PMIX_ERR_NOMEM;
 416                 }
 417             }
 418             free(tmp);
 419             tmp = tmp2;
 420             PMIX_RELEASE(rng);
 421         }
 422         /* replace the final comma */
 423         tmp[strlen(tmp)-1] = ';';
 424     }
 425 
 426     /* replace the final semi-colon */
 427     tmp[strlen(tmp)-1] = ']';
 428 
 429     /* assemble final result */
 430     *regexp = tmp;
 431 
 432     PMIX_LIST_DESTRUCT(&nodes);
 433     return PMIX_SUCCESS;
 434 }
 435 
 436 static pmix_status_t parse_nodes(const char *regexp,
 437                                  char ***names)
 438 {
 439     char *tmp, *ptr;
 440     pmix_status_t rc;
 441 
 442     /* set default */
 443     *names = NULL;
 444 
 445     /* protect against bozo */
 446     if (NULL == regexp) {
 447         return PMIX_SUCCESS;
 448     }
 449 
 450     /* protect the input string */
 451     tmp = strdup(regexp);
 452     /* strip the trailing bracket */
 453     tmp[strlen(tmp)-1] = '\0';
 454 
 455     /* the regex generator used to create this regex
 456      * is tagged at the beginning of the string */
 457     if (NULL == (ptr = strchr(tmp, '['))) {
 458         PMIX_ERROR_LOG(PMIX_ERR_BAD_PARAM);
 459         free(tmp);
 460         return PMIX_ERR_BAD_PARAM;
 461     }
 462     *ptr = '\0';
 463     ++ptr;
 464 
 465     /* if it was done by PMIx, use that parser */
 466     if (0 == strcmp(tmp, "pmix")) {
 467         if (PMIX_SUCCESS != (rc = pmix_regex_extract_nodes(ptr, names))) {
 468             PMIX_ERROR_LOG(rc);
 469         }
 470     } else {
 471         /* this isn't an error - let someone else try */
 472         rc = PMIX_ERR_TAKE_NEXT_OPTION;
 473     }
 474     free(tmp);
 475     return rc;
 476 
 477 }
 478 static pmix_status_t parse_procs(const char *regexp,
 479                                  char ***procs)
 480 {
 481     char *tmp, *ptr;
 482     pmix_status_t rc;
 483 
 484     /* set default */
 485     *procs = NULL;
 486 
 487     /* protect against bozo */
 488     if (NULL == regexp) {
 489         return PMIX_SUCCESS;
 490     }
 491 
 492     /* protect the input string */
 493     tmp = strdup(regexp);
 494     /* strip the trailing bracket */
 495     tmp[strlen(tmp)-1] = '\0';
 496 
 497     /* the regex generator used to create this regex
 498      * is tagged at the beginning of the string */
 499     if (NULL == (ptr = strchr(tmp, '['))) {
 500         PMIX_ERROR_LOG(PMIX_ERR_BAD_PARAM);
 501         free(tmp);
 502         return PMIX_ERR_BAD_PARAM;
 503     }
 504     *ptr = '\0';
 505     ++ptr;
 506 
 507     /* if it was done by PMIx, use that parser */
 508     if (0 == strcmp(tmp, "pmix")) {
 509         if (PMIX_SUCCESS != (rc = pmix_regex_extract_ppn(ptr, procs))) {
 510             PMIX_ERROR_LOG(rc);
 511         }
 512     } else {
 513         /* this isn't an error - let someone else try */
 514         rc = PMIX_ERR_TAKE_NEXT_OPTION;
 515     }
 516     free(tmp);
 517     return rc;
 518 }
 519 
 520 static pmix_status_t resolve_peers(const char *nodename,
 521                                    const char *nspace,
 522                                    pmix_proc_t **procs, size_t *nprocs)
 523 {
 524     pmix_cb_t cb;
 525     pmix_status_t rc;
 526     pmix_kval_t *kv;
 527     pmix_proc_t proc;
 528     char **ptr;
 529     pmix_info_t *info;
 530     pmix_proc_t *p=NULL;
 531     size_t ninfo, np=0, n, j;
 532 
 533     PMIX_CONSTRUCT(&cb, pmix_cb_t);
 534 
 535     cb.key = strdup(nodename);
 536     /* this data isn't going anywhere, so we don't require a copy */
 537     cb.copy = false;
 538     /* scope is irrelevant as the info we seek must be local */
 539     cb.scope = PMIX_SCOPE_UNDEF;
 540     /* let the proc point to the nspace */
 541     pmix_strncpy(proc.nspace, nspace, PMIX_MAX_NSLEN);
 542     proc.rank = PMIX_RANK_WILDCARD;
 543     cb.proc = &proc;
 544 
 545     PMIX_GDS_FETCH_KV(rc, pmix_client_globals.myserver, &cb);
 546     if (PMIX_SUCCESS != rc) {
 547         if (PMIX_ERR_INVALID_NAMESPACE != rc) {
 548             PMIX_ERROR_LOG(rc);
 549         }
 550         goto complete;
 551     }
 552     /* should just be the one value on the list */
 553     if (1 != pmix_list_get_size(&cb.kvs)) {
 554         PMIX_ERROR_LOG(PMIX_ERR_BAD_PARAM);
 555         rc = PMIX_ERR_BAD_PARAM;
 556         goto complete;
 557     }
 558     kv = (pmix_kval_t*)pmix_list_get_first(&cb.kvs);
 559     /* the hostname used as a key with wildcard rank will return
 560      * a pmix_data_array_t of pmix_info_t structs */
 561     if (NULL == kv->value ||
 562         PMIX_DATA_ARRAY != kv->value->type ||
 563         NULL == kv->value->data.darray ||
 564         PMIX_INFO != kv->value->data.darray->type) {
 565         PMIX_ERROR_LOG(PMIX_ERR_DATA_VALUE_NOT_FOUND);
 566         rc = PMIX_ERR_DATA_VALUE_NOT_FOUND;
 567         goto complete;
 568     }
 569     info = (pmix_info_t*)kv->value->data.darray->array;
 570     ninfo = kv->value->data.darray->size;
 571     /* find the PMIX_LOCAL_PEERS key */
 572     for (n=0; n < ninfo; n++) {
 573         if (0 == strncmp(info[n].key, PMIX_LOCAL_PEERS, PMIX_MAX_KEYLEN)) {
 574             /* split the string */
 575             ptr = pmix_argv_split(info[n].value.data.string, ',');
 576             np = pmix_argv_count(ptr);
 577             PMIX_PROC_CREATE(p, np);
 578             if (NULL == p) {
 579                 rc = PMIX_ERR_NOMEM;
 580                 pmix_argv_free(ptr);
 581                 goto complete;
 582             }
 583             for (j=0; j < np; j++) {
 584                 pmix_strncpy(p[j].nspace, nspace, PMIX_MAX_NSLEN);
 585                 p[j].rank = strtoul(ptr[j], NULL, 10);
 586             }
 587             rc = PMIX_SUCCESS;
 588             pmix_argv_free(ptr);
 589             break;
 590         }
 591     }
 592 
 593   complete:
 594     if (NULL != cb.info) {
 595         PMIX_INFO_FREE(cb.info, cb.ninfo);
 596     }
 597     if (NULL != cb.key) {
 598         free(cb.key);
 599         cb.key = NULL;
 600     }
 601     PMIX_DESTRUCT(&cb);
 602     *procs = p;
 603     *nprocs = np;
 604 
 605     return rc;
 606 }
 607 
 608 static pmix_status_t resolve_nodes(const char *nspace,
 609                                    char **nodelist)
 610 {
 611     pmix_cb_t cb;
 612     pmix_status_t rc;
 613     pmix_kval_t *kv;
 614     pmix_proc_t proc;
 615 
 616     PMIX_CONSTRUCT(&cb, pmix_cb_t);
 617 
 618     /* setup default answer */
 619     *nodelist = NULL;
 620 
 621     /* create a pmix_info_t so we can pass the nspace
 622     * into the fetch as a qualifier */
 623     PMIX_INFO_CREATE(cb.info, 1);
 624     if (NULL == cb.info) {
 625         PMIX_DESTRUCT(&cb);
 626         return PMIX_ERR_NOMEM;
 627     }
 628     cb.ninfo = 1;
 629     PMIX_INFO_LOAD(&cb.info[0], PMIX_NSPACE, nspace, PMIX_STRING);
 630 
 631     /* tell the GDS what we want */
 632     cb.key = PMIX_NODE_MAP;
 633     /* this data isn't going anywhere, so we don't require a copy */
 634     cb.copy = false;
 635     /* scope is irrelevant as the info we seek must be local */
 636     cb.scope = PMIX_SCOPE_UNDEF;
 637     /* put the nspace in the proc field */
 638     pmix_strncpy(proc.nspace, nspace, PMIX_MAX_NSLEN);
 639     /* the info will be associated with PMIX_RANK_WILDCARD */
 640     proc.rank = PMIX_RANK_WILDCARD;
 641     cb.proc = &proc;
 642 
 643     PMIX_GDS_FETCH_KV(rc, pmix_client_globals.myserver, &cb);
 644     if (PMIX_SUCCESS != rc) {
 645         PMIX_ERROR_LOG(rc);
 646         goto complete;
 647     }
 648     /* should just be the one value on the list */
 649     if (1 != pmix_list_get_size(&cb.kvs)) {
 650         PMIX_ERROR_LOG(PMIX_ERR_BAD_PARAM);
 651         rc = PMIX_ERR_BAD_PARAM;
 652         goto complete;
 653     }
 654     kv = (pmix_kval_t*)pmix_list_get_first(&cb.kvs);
 655     /* the PMIX_NODE_MAP key is supposed to return
 656     * a regex string  - check that it did */
 657     if (NULL == kv->value ||
 658         PMIX_STRING != kv->value->type) {
 659         PMIX_ERROR_LOG(PMIX_ERR_DATA_VALUE_NOT_FOUND);
 660         rc = PMIX_ERR_DATA_VALUE_NOT_FOUND;
 661         goto complete;
 662     }
 663     /* return the string */
 664     if (NULL != kv->value->data.string) {
 665         *nodelist = strdup(kv->value->data.string);
 666     }
 667 
 668   complete:
 669     if (NULL != cb.info) {
 670       PMIX_INFO_FREE(cb.info, cb.ninfo);
 671     }
 672     return rc;
 673 }
 674 
 675 static pmix_status_t pmix_regex_extract_nodes(char *regexp, char ***names)
 676 {
 677     int i, j, k, len;
 678     pmix_status_t ret;
 679     char *base;
 680     char *orig, *suffix;
 681     bool found_range = false;
 682     bool more_to_come = false;
 683     int num_digits;
 684 
 685     /* set the default */
 686     *names = NULL;
 687 
 688     if (NULL == regexp) {
 689         return PMIX_SUCCESS;
 690     }
 691 
 692     orig = base = strdup(regexp);
 693     if (NULL == base) {
 694         PMIX_ERROR_LOG(PMIX_ERR_OUT_OF_RESOURCE);
 695         return PMIX_ERR_OUT_OF_RESOURCE;
 696     }
 697 
 698     PMIX_OUTPUT_VERBOSE((1, pmix_globals.debug_output,
 699                          "pmix:extract:nodes: checking list: %s", regexp));
 700 
 701     do {
 702         /* Find the base */
 703         len = strlen(base);
 704         for (i = 0; i <= len; ++i) {
 705             if (base[i] == '[') {
 706                 /* we found a range. this gets dealt with below */
 707                 base[i] = '\0';
 708                 found_range = true;
 709                 break;
 710             }
 711             if (base[i] == ',') {
 712                 /* we found a singleton value, and there are more to come */
 713                 base[i] = '\0';
 714                 found_range = false;
 715                 more_to_come = true;
 716                 break;
 717             }
 718             if (base[i] == '\0') {
 719                 /* we found a singleton value */
 720                 found_range = false;
 721                 more_to_come = false;
 722                 break;
 723             }
 724         }
 725         if (i == 0 && !found_range) {
 726             /* we found a special character at the beginning of the string */
 727             free(orig);
 728             return PMIX_ERR_BAD_PARAM;
 729         }
 730 
 731         if (found_range) {
 732             /* If we found a range, get the number of digits in the numbers */
 733             i++;  /* step over the [ */
 734             for (j=i; j < len; j++) {
 735                 if (base[j] == ':') {
 736                     base[j] = '\0';
 737                     break;
 738                 }
 739             }
 740             if (j >= len) {
 741                 /* we didn't find the number of digits */
 742                 free(orig);
 743                 return PMIX_ERR_BAD_PARAM;
 744             }
 745             num_digits = strtol(&base[i], NULL, 10);
 746             i = j + 1;  /* step over the : */
 747             /* now find the end of the range */
 748             for (j = i; j < len; ++j) {
 749                 if (base[j] == ']') {
 750                     base[j] = '\0';
 751                     break;
 752                 }
 753             }
 754             if (j >= len) {
 755                 /* we didn't find the end of the range */
 756                 free(orig);
 757                 return PMIX_ERR_BAD_PARAM;
 758             }
 759             /* check for a suffix */
 760             if (j+1 < len && base[j+1] != ',') {
 761                 /* find the next comma, if present */
 762                 for (k=j+1; k < len && base[k] != ','; k++);
 763                 if (k < len) {
 764                     base[k] = '\0';
 765                 }
 766                 suffix = strdup(&base[j+1]);
 767                 if (k < len) {
 768                     base[k] = ',';
 769                 }
 770                 j = k-1;
 771             } else {
 772                 suffix = NULL;
 773             }
 774             PMIX_OUTPUT_VERBOSE((1, pmix_globals.debug_output,
 775                                  "regex:extract:nodes: parsing range %s %s %s",
 776                                  base, base + i, suffix));
 777 
 778             ret = regex_parse_value_ranges(base, base + i, num_digits, suffix, names);
 779             if (NULL != suffix) {
 780                 free(suffix);
 781             }
 782             if (PMIX_SUCCESS != ret) {
 783                 free(orig);
 784                 return ret;
 785             }
 786             if (j+1 < len && base[j + 1] == ',') {
 787                 more_to_come = true;
 788                 base = &base[j + 2];
 789             } else {
 790                 more_to_come = false;
 791             }
 792         } else {
 793             /* If we didn't find a range, just add the value */
 794             if(PMIX_SUCCESS != (ret = pmix_argv_append_nosize(names, base))) {
 795                 PMIX_ERROR_LOG(ret);
 796                 free(orig);
 797                 return ret;
 798             }
 799             /* step over the comma */
 800             i++;
 801             /* set base equal to the (possible) next base to look at */
 802             base = &base[i];
 803         }
 804     } while(more_to_come);
 805 
 806     free(orig);
 807 
 808     /* All done */
 809     return ret;
 810 }
 811 
 812 
 813 /*
 814  * Parse one or more ranges in a set
 815  *
 816  * @param base     The base text of the value name
 817  * @param *ranges  A pointer to a range. This can contain multiple ranges
 818  *                 (i.e. "1-3,10" or "5" or "9,0100-0130,250")
 819  * @param ***names An argv array to add the newly discovered values to
 820  */
 821 static pmix_status_t regex_parse_value_ranges(char *base, char *ranges,
 822                                               int num_digits, char *suffix,
 823                                               char ***names)
 824 {
 825     int i, len;
 826     pmix_status_t ret;
 827     char *start, *orig;
 828 
 829     /* Look for commas, the separator between ranges */
 830 
 831     len = strlen(ranges);
 832     for (orig = start = ranges, i = 0; i < len; ++i) {
 833         if (',' == ranges[i]) {
 834             ranges[i] = '\0';
 835             ret = regex_parse_value_range(base, start, num_digits, suffix, names);
 836             if (PMIX_SUCCESS != ret) {
 837                 PMIX_ERROR_LOG(ret);
 838                 return ret;
 839             }
 840             start = ranges + i + 1;
 841         }
 842     }
 843 
 844     /* Pick up the last range, if it exists */
 845 
 846     if (start < orig + len) {
 847 
 848         PMIX_OUTPUT_VERBOSE((1, pmix_globals.debug_output,
 849                              "regex:parse:ranges: parse range %s (2)", start));
 850 
 851         ret = regex_parse_value_range(base, start, num_digits, suffix, names);
 852         if (PMIX_SUCCESS != ret) {
 853             PMIX_ERROR_LOG(ret);
 854             return ret;
 855         }
 856     }
 857 
 858     /* All done */
 859     return PMIX_SUCCESS;
 860 }
 861 
 862 
 863 /*
 864  * Parse a single range in a set and add the full names of the values
 865  * found to the names argv
 866  *
 867  * @param base     The base text of the value name
 868  * @param *ranges  A pointer to a single range. (i.e. "1-3" or "5")
 869  * @param ***names An argv array to add the newly discovered values to
 870  */
 871 static pmix_status_t regex_parse_value_range(char *base, char *range,
 872                                              int num_digits, char *suffix,
 873                                              char ***names)
 874 {
 875     char *str, tmp[132];
 876     size_t i, k, start, end;
 877     size_t base_len, len;
 878     bool found;
 879     pmix_status_t ret;
 880 
 881     if (NULL == base || NULL == range) {
 882         return PMIX_ERROR;
 883     }
 884 
 885     len = strlen(range);
 886     base_len = strlen(base);
 887     /* Silence compiler warnings; start and end are always assigned
 888      properly, below */
 889     start = end = 0;
 890 
 891     /* Look for the beginning of the first number */
 892 
 893     for (found = false, i = 0; i < len; ++i) {
 894         if (isdigit((int) range[i])) {
 895             if (!found) {
 896                 start = atoi(range + i);
 897                 found = true;
 898                 break;
 899             }
 900         }
 901     }
 902     if (!found) {
 903         PMIX_ERROR_LOG(PMIX_ERR_NOT_FOUND);
 904         return PMIX_ERR_NOT_FOUND;
 905     }
 906 
 907     /* Look for the end of the first number */
 908 
 909     for (found = false; i < len; ++i) {
 910         if (!isdigit(range[i])) {
 911             break;
 912         }
 913     }
 914 
 915     /* Was there no range, just a single number? */
 916 
 917     if (i >= len) {
 918         end = start;
 919         found = true;
 920     } else {
 921         /* Nope, there was a range.  Look for the beginning of the second
 922          * number
 923          */
 924         for (; i < len; ++i) {
 925             if (isdigit(range[i])) {
 926                 end = strtol(range + i, NULL, 10);
 927                 found = true;
 928                 break;
 929             }
 930         }
 931     }
 932     if (!found) {
 933         PMIX_ERROR_LOG(PMIX_ERR_NOT_FOUND);
 934         return PMIX_ERR_NOT_FOUND;
 935     }
 936 
 937     /* Make strings for all values in the range */
 938 
 939     len = base_len + num_digits + 32;
 940     if (NULL != suffix) {
 941         len += strlen(suffix);
 942     }
 943     str = (char *) malloc(len);
 944     if (NULL == str) {
 945         PMIX_ERROR_LOG(PMIX_ERR_OUT_OF_RESOURCE);
 946         return PMIX_ERR_OUT_OF_RESOURCE;
 947     }
 948     for (i = start; i <= end; ++i) {
 949         memset(str, 0, len);
 950         strcpy(str, base);
 951         /* we need to zero-pad the digits */
 952         for (k=0; k < (size_t)num_digits; k++) {
 953             str[k+base_len] = '0';
 954         }
 955         memset(tmp, 0, 132);
 956         snprintf(tmp, 132, "%lu", (unsigned long)i);
 957         for (k=0; k < strlen(tmp); k++) {
 958             str[base_len + num_digits - k - 1] = tmp[strlen(tmp)-k-1];
 959         }
 960         /* if there is a suffix, add it */
 961         if (NULL != suffix) {
 962             strcat(str, suffix);
 963         }
 964         ret = pmix_argv_append_nosize(names, str);
 965         if(PMIX_SUCCESS != ret) {
 966             PMIX_ERROR_LOG(ret);
 967             free(str);
 968             return ret;
 969         }
 970     }
 971     free(str);
 972 
 973     /* All done */
 974     return PMIX_SUCCESS;
 975 }
 976 
 977 static pmix_status_t pmix_regex_extract_ppn(char *regexp, char ***procs)
 978 {
 979     char **rngs, **nds, *t, **ps=NULL;
 980     int i, j, k, start, end;
 981 
 982     /* split on semi-colons for nodes */
 983     nds = pmix_argv_split(regexp, ';');
 984     for (j=0; NULL != nds[j]; j++) {
 985         /* for each node, split it by comma */
 986         rngs = pmix_argv_split(nds[j], ',');
 987         /* parse each element */
 988         for (i=0; NULL != rngs[i]; i++) {
 989             /* look for a range */
 990             if (NULL == (t = strchr(rngs[i], '-'))) {
 991                 /* just one value */
 992                 pmix_argv_append_nosize(&ps, rngs[i]);
 993             } else {
 994                 /* handle the range */
 995                 *t = '\0';
 996                 start = strtol(rngs[i], NULL, 10);
 997                 ++t;
 998                 end = strtol(t, NULL, 10);
 999                 for (k=start; k <= end; k++) {
1000                     if (0 > asprintf(&t, "%d", k)) {
1001                         pmix_argv_free(nds);
1002                         pmix_argv_free(rngs);
1003                         return PMIX_ERR_NOMEM;
1004                     }
1005                     pmix_argv_append_nosize(&ps, t);
1006                     free(t);
1007                 }
1008             }
1009         }
1010         pmix_argv_free(rngs);
1011         /* create the node entry */
1012         t = pmix_argv_join(ps, ',');
1013         pmix_argv_append_nosize(procs, t);
1014         free(t);
1015         pmix_argv_free(ps);
1016         ps = NULL;
1017     }
1018 
1019     pmix_argv_free(nds);
1020     return PMIX_SUCCESS;
1021 }

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