1 /*
2  * Copyright (c) 2016-2018 Intel, Inc.  All rights reserved.
3  * Copyright (c) 2018      Research Organization for Information Science
4  *                         and Technology (RIST). All rights reserved.
5  * $COPYRIGHT$
6  *
7  * Additional copyrights may follow
8  *
9  * $HEADER$
10  *
11  */
12 
13 #include "orte_config.h"
14 #include "orte/types.h"
15 #include "opal/types.h"
16 
17 #ifdef HAVE_UNISTD_H
18 #include <unistd.h>
19 #endif
20 #include <ctype.h>
21 
22 #include "opal/util/argv.h"
23 #include "opal/util/basename.h"
24 #include "opal/util/opal_environ.h"
25 
26 #include "orte/runtime/orte_globals.h"
27 #include "orte/util/name_fns.h"
28 #include "orte/util/show_help.h"
29 #include "orte/mca/errmgr/errmgr.h"
30 #include "orte/mca/rmaps/base/base.h"
31 #include "orte/mca/routed/routed.h"
32 #include "orte/mca/regx/base/base.h"
33 
34 #include "regx_fwd.h"
35 
36 static int nidmap_create(opal_pointer_array_t *pool, char **regex);
37 
38 orte_regx_base_module_t orte_regx_fwd_module = {
39     .nidmap_create = nidmap_create,
40     .nidmap_parse = orte_regx_base_nidmap_parse,
41     .extract_node_names = orte_regx_base_extract_node_names,
42     .encode_nodemap = orte_regx_base_encode_nodemap,
43     .decode_daemon_nodemap = orte_regx_base_decode_daemon_nodemap,
44     .generate_ppn = orte_regx_base_generate_ppn,
45     .parse_ppn = orte_regx_base_parse_ppn
46 };
47 
nidmap_create(opal_pointer_array_t * pool,char ** regex)48 static int nidmap_create(opal_pointer_array_t *pool, char **regex)
49 {
50     char *node;
51     char prefix[ORTE_MAX_NODE_PREFIX];
52     int i, j, n, len, startnum, nodenum, numdigits;
53     bool found;
54     char *suffix, *sfx, *nodenames;
55     orte_regex_node_t *ndreg;
56     orte_regex_range_t *range, *rng;
57     opal_list_t nodenms, dvpids;
58     opal_list_item_t *item, *itm2;
59     char **regexargs = NULL, *tmp, *tmp2;
60     orte_node_t *nptr;
61     orte_vpid_t vpid;
62 
63     OBJ_CONSTRUCT(&nodenms, opal_list_t);
64     OBJ_CONSTRUCT(&dvpids, opal_list_t);
65 
66     rng = NULL;
67     for (n=0; n < pool->size; n++) {
68         if (NULL == (nptr = (orte_node_t*)opal_pointer_array_get_item(pool, n))) {
69             continue;
70         }
71         /* if no daemon has been assigned, then this node is not being used */
72         if (NULL == nptr->daemon) {
73             vpid = -1;  // indicates no daemon assigned
74         } else {
75             vpid = nptr->daemon->name.vpid;
76         }
77         /* deal with the daemon vpid - see if it is next in the
78          * current range */
79         if (NULL == rng) {
80             /* just starting */
81             rng = OBJ_NEW(orte_regex_range_t);
82             rng->vpid = vpid;
83             rng->cnt = 1;
84             opal_list_append(&dvpids, &rng->super);
85         } else if (UINT32_MAX == vpid) {
86             if (-1 == rng->vpid) {
87                 rng->cnt++;
88             } else {
89                 /* need to start another range */
90                 rng = OBJ_NEW(orte_regex_range_t);
91                 rng->vpid = vpid;
92                 rng->cnt = 1;
93                 opal_list_append(&dvpids, &rng->super);
94             }
95         } else if (-1 == rng->vpid) {
96             /* need to start another range */
97             rng = OBJ_NEW(orte_regex_range_t);
98             rng->vpid = vpid;
99             rng->cnt = 1;
100             opal_list_append(&dvpids, &rng->super);
101         } else {
102             /* is this the next in line */
103             if (vpid == (orte_vpid_t)(rng->vpid + rng->cnt)) {
104                 rng->cnt++;
105             } else {
106                 /* need to start another range */
107                 rng = OBJ_NEW(orte_regex_range_t);
108                 rng->vpid = vpid;
109                 rng->cnt = 1;
110                 opal_list_append(&dvpids, &rng->super);
111             }
112         }
113         node = nptr->name;
114         /* determine this node's prefix by looking for first digit char */
115         len = strlen(node);
116         startnum = -1;
117         memset(prefix, 0, ORTE_MAX_NODE_PREFIX);
118         for (i=0, j=0; i < len; i++) {
119             /* valid hostname characters are ascii letters, digits and the '-' character. */
120             if (isdigit(node[i])) {
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             /* this must be either an alpha, a '.', or '-' */
131             if (!isalpha(node[i]) && '-' != node[i] && '.' != node[i]) {
132                 orte_show_help("help-regex.txt", "regex:invalid-name", true, node);
133                 return ORTE_ERR_SILENT;
134             }
135             if (startnum < 0) {
136                 prefix[j++] = node[i];
137             }
138         }
139         if (startnum < 0) {
140             /* can't compress this name - just add it to the list */
141             ndreg = OBJ_NEW(orte_regex_node_t);
142             ndreg->prefix = strdup(node);
143             opal_list_append(&nodenms, &ndreg->super);
144             continue;
145         }
146         /* convert the digits and get any suffix */
147         nodenum = strtol(&node[startnum], &sfx, 10);
148         if (NULL != sfx) {
149             suffix = strdup(sfx);
150             numdigits = (int)(sfx - &node[startnum]);
151         } else {
152             suffix = NULL;
153             numdigits = (int)strlen(&node[startnum]);
154         }
155         /* is this node name already on our list? */
156         found = false;
157         if (0 != opal_list_get_size(&nodenms)) {
158             ndreg = (orte_regex_node_t*)opal_list_get_last(&nodenms);
159 
160             if ((0 < strlen(prefix) && NULL == ndreg->prefix) ||
161                 (0 == strlen(prefix) && NULL != ndreg->prefix) ||
162                 (0 < strlen(prefix) && NULL != ndreg->prefix &&
163                     0 != strcmp(prefix, ndreg->prefix)) ||
164                 (NULL == suffix && NULL != ndreg->suffix) ||
165                 (NULL != suffix && NULL == ndreg->suffix) ||
166                 (NULL != suffix && NULL != ndreg->suffix &&
167                     0 != strcmp(suffix, ndreg->suffix)) ||
168                 (numdigits != ndreg->num_digits)) {
169                 found = false;
170             } else {
171                 /* found a match - flag it */
172                 found = true;
173             }
174         }
175         if (found) {
176             range = (orte_regex_range_t*)opal_list_get_last(&ndreg->ranges);
177             if (NULL == range) {
178                 /* first range for this nodeid */
179                 range = OBJ_NEW(orte_regex_range_t);
180                 range->vpid = nodenum;
181                 range->cnt = 1;
182                 opal_list_append(&ndreg->ranges, &range->super);
183             /* see if the node number is out of sequence */
184             } else if (nodenum != (range->vpid + range->cnt)) {
185                 /* start a new range */
186                 range = OBJ_NEW(orte_regex_range_t);
187                 range->vpid = nodenum;
188                 range->cnt = 1;
189                 opal_list_append(&ndreg->ranges, &range->super);
190             } else {
191                 /* everything matches - just increment the cnt */
192                 range->cnt++;
193             }
194         } else {
195             /* need to add it */
196             ndreg = OBJ_NEW(orte_regex_node_t);
197             if (0 < strlen(prefix)) {
198                 ndreg->prefix = strdup(prefix);
199             }
200             if (NULL != suffix) {
201                 ndreg->suffix = strdup(suffix);
202             }
203             ndreg->num_digits = numdigits;
204             opal_list_append(&nodenms, &ndreg->super);
205             /* record the first range for this nodeid - we took
206              * care of names we can't compress above
207              */
208             range = OBJ_NEW(orte_regex_range_t);
209             range->vpid = nodenum;
210             range->cnt = 1;
211             opal_list_append(&ndreg->ranges, &range->super);
212         }
213         if (NULL != suffix) {
214             free(suffix);
215         }
216     }
217     /* begin constructing the regular expression */
218     while (NULL != (item = opal_list_remove_first(&nodenms))) {
219         ndreg = (orte_regex_node_t*)item;
220 
221         /* if no ranges, then just add the name */
222         if (0 == opal_list_get_size(&ndreg->ranges)) {
223             if (NULL != ndreg->prefix) {
224                 /* solitary node */
225                 asprintf(&tmp, "%s", ndreg->prefix);
226                 opal_argv_append_nosize(&regexargs, tmp);
227                 free(tmp);
228             }
229             OBJ_RELEASE(ndreg);
230             continue;
231         }
232         /* start the regex for this nodeid with the prefix */
233         if (NULL != ndreg->prefix) {
234             asprintf(&tmp, "%s[%d:", ndreg->prefix, ndreg->num_digits);
235         } else {
236             asprintf(&tmp, "[%d:", ndreg->num_digits);
237         }
238         /* add the ranges */
239         while (NULL != (itm2 = opal_list_remove_first(&ndreg->ranges))) {
240             range = (orte_regex_range_t*)itm2;
241             if (1 == range->cnt) {
242                 asprintf(&tmp2, "%s%u,", tmp, range->vpid);
243             } else {
244                 asprintf(&tmp2, "%s%u-%u,", tmp, range->vpid, range->vpid + range->cnt - 1);
245             }
246             free(tmp);
247             tmp = tmp2;
248             OBJ_RELEASE(range);
249         }
250         /* replace the final comma */
251         tmp[strlen(tmp)-1] = ']';
252         if (NULL != ndreg->suffix) {
253             /* add in the suffix, if provided */
254             asprintf(&tmp2, "%s%s", tmp, ndreg->suffix);
255             free(tmp);
256             tmp = tmp2;
257         }
258         opal_argv_append_nosize(&regexargs, tmp);
259         free(tmp);
260         OBJ_RELEASE(ndreg);
261     }
262 
263     /* assemble final result */
264     nodenames = opal_argv_join(regexargs, ',');
265     /* cleanup */
266     opal_argv_free(regexargs);
267     OBJ_DESTRUCT(&nodenms);
268 
269     /* do the same for the vpids */
270     tmp = NULL;
271     while (NULL != (item = opal_list_remove_first(&dvpids))) {
272         rng = (orte_regex_range_t*)item;
273         if (1 < rng->cnt) {
274             if (NULL == tmp) {
275                 asprintf(&tmp, "%u(%u)", rng->vpid, rng->cnt);
276             } else {
277                 asprintf(&tmp2, "%s,%u(%u)", tmp, rng->vpid, rng->cnt);
278                 free(tmp);
279                 tmp = tmp2;
280             }
281         } else {
282             if (NULL == tmp) {
283                 asprintf(&tmp, "%u", rng->vpid);
284             } else {
285                 asprintf(&tmp2, "%s,%u", tmp, rng->vpid);
286                 free(tmp);
287                 tmp = tmp2;
288             }
289         }
290         OBJ_RELEASE(rng);
291     }
292     OPAL_LIST_DESTRUCT(&dvpids);
293 
294     /* now concatenate the results into one string */
295     asprintf(&tmp2, "%s@%s", nodenames, tmp);
296     free(nodenames);
297     free(tmp);
298     *regex = tmp2;
299     opal_output_verbose(5, orte_regx_base_framework.framework_output,
300                         "%s Final regex: <%s>",
301                         ORTE_NAME_PRINT(ORTE_PROC_MY_NAME),
302                         *regex);
303     return ORTE_SUCCESS;
304 }
305