1 /**
2 * Copyright (C) Mellanox Technologies Ltd. 2001-2019. ALL RIGHTS RESERVED.
3 *
4 * See file LICENSE for terms.
5 */
6 
7 #ifdef HAVE_CONFIG_H
8 #  include "config.h"
9 #endif
10 #include "parser.h"
11 
12 #include <ucs/arch/atomic.h>
13 #include <ucs/sys/sys.h>
14 #include <ucs/sys/string.h>
15 #include <ucs/datastruct/list.h>
16 #include <ucs/datastruct/khash.h>
17 #include <ucs/debug/assert.h>
18 #include <ucs/debug/log.h>
19 #include <ucs/debug/debug.h>
20 #include <ucs/time/time.h>
21 #include <fnmatch.h>
22 #include <ctype.h>
23 
24 
25 /* width of titles in docstring */
26 #define UCS_CONFIG_PARSER_DOCSTR_WIDTH         10
27 
28 
29 /* list of prefixes for a configuration variable, used to dump all possible
30  * aliases.
31  */
32 typedef struct ucs_config_parser_prefix_list {
33     const char                  *prefix;
34     ucs_list_link_t             list;
35 } ucs_config_parser_prefix_t;
36 
37 
38 typedef UCS_CONFIG_ARRAY_FIELD(void, data) ucs_config_array_field_t;
39 
40 KHASH_SET_INIT_STR(ucs_config_env_vars)
41 
42 
43 /* Process environment variables */
44 extern char **environ;
45 
46 
47 UCS_LIST_HEAD(ucs_config_global_list);
48 static khash_t(ucs_config_env_vars) ucs_config_parser_env_vars = {0};
49 static pthread_mutex_t ucs_config_parser_env_vars_hash_lock    = PTHREAD_MUTEX_INITIALIZER;
50 
51 
52 const char *ucs_async_mode_names[] = {
53     [UCS_ASYNC_MODE_SIGNAL]          = "signal",
54     [UCS_ASYNC_MODE_THREAD_SPINLOCK] = "thread_spinlock",
55     [UCS_ASYNC_MODE_THREAD_MUTEX]    = "thread_mutex",
56     [UCS_ASYNC_MODE_POLL]            = "poll",
57     [UCS_ASYNC_MODE_LAST]            = NULL
58 };
59 
60 UCS_CONFIG_DEFINE_ARRAY(string, sizeof(char*), UCS_CONFIG_TYPE_STRING);
61 
62 /* Fwd */
63 static ucs_status_t
64 ucs_config_parser_set_value_internal(void *opts, ucs_config_field_t *fields,
65                                      const char *name, const char *value,
66                                      const char *table_prefix, int recurse);
67 
68 
__find_string_in_list(const char * str,const char ** list)69 static int __find_string_in_list(const char *str, const char **list)
70 {
71     int i;
72 
73     for (i = 0; *list; ++list, ++i) {
74         if (strcasecmp(*list, str) == 0) {
75             return i;
76         }
77     }
78     return -1;
79 }
80 
ucs_config_sscanf_string(const char * buf,void * dest,const void * arg)81 int ucs_config_sscanf_string(const char *buf, void *dest, const void *arg)
82 {
83     *((char**)dest) = strdup(buf);
84     return 1;
85 }
86 
ucs_config_sprintf_string(char * buf,size_t max,const void * src,const void * arg)87 int ucs_config_sprintf_string(char *buf, size_t max,
88                               const void *src, const void *arg)
89 {
90     strncpy(buf, *((char**)src), max);
91     return 1;
92 }
93 
ucs_config_clone_string(const void * src,void * dest,const void * arg)94 ucs_status_t ucs_config_clone_string(const void *src, void *dest, const void *arg)
95 {
96     char *new_str = strdup(*(char**)src);
97     if (new_str == NULL) {
98         return UCS_ERR_NO_MEMORY;
99     }
100 
101     *((char**)dest) = new_str;
102     return UCS_OK;
103 }
104 
ucs_config_release_string(void * ptr,const void * arg)105 void ucs_config_release_string(void *ptr, const void *arg)
106 {
107     free(*(char**)ptr);
108 }
109 
ucs_config_sscanf_int(const char * buf,void * dest,const void * arg)110 int ucs_config_sscanf_int(const char *buf, void *dest, const void *arg)
111 {
112     return sscanf(buf, "%i", (unsigned*)dest);
113 }
114 
ucs_config_clone_int(const void * src,void * dest,const void * arg)115 ucs_status_t ucs_config_clone_int(const void *src, void *dest, const void *arg)
116 {
117     *(int*)dest = *(int*)src;
118     return UCS_OK;
119 }
120 
ucs_config_sprintf_int(char * buf,size_t max,const void * src,const void * arg)121 int ucs_config_sprintf_int(char *buf, size_t max,
122                            const void *src, const void *arg)
123 {
124     return snprintf(buf, max, "%i", *(unsigned*)src);
125 }
126 
ucs_config_sscanf_uint(const char * buf,void * dest,const void * arg)127 int ucs_config_sscanf_uint(const char *buf, void *dest, const void *arg)
128 {
129     if (!strcasecmp(buf, UCS_NUMERIC_INF_STR)) {
130         *(unsigned*)dest = UINT_MAX;
131         return 1;
132     } else {
133         return sscanf(buf, "%u", (unsigned*)dest);
134     }
135 }
136 
ucs_config_clone_uint(const void * src,void * dest,const void * arg)137 ucs_status_t ucs_config_clone_uint(const void *src, void *dest, const void *arg)
138 {
139     *(unsigned*)dest = *(unsigned*)src;
140     return UCS_OK;
141 }
142 
ucs_config_sprintf_uint(char * buf,size_t max,const void * src,const void * arg)143 int ucs_config_sprintf_uint(char *buf, size_t max,
144                             const void *src, const void *arg)
145 {
146     unsigned value = *(unsigned*)src;
147     if (value == UINT_MAX) {
148         snprintf(buf, max, UCS_NUMERIC_INF_STR);
149         return 1;
150     } else {
151         return snprintf(buf, max, "%u", value);
152     }
153 }
154 
ucs_config_sscanf_ulong(const char * buf,void * dest,const void * arg)155 int ucs_config_sscanf_ulong(const char *buf, void *dest, const void *arg)
156 {
157     return sscanf(buf, "%lu", (unsigned long*)dest);
158 }
159 
ucs_config_sprintf_ulong(char * buf,size_t max,const void * src,const void * arg)160 int ucs_config_sprintf_ulong(char *buf, size_t max,
161                              const void *src, const void *arg)
162 {
163     return snprintf(buf, max, "%lu", *(unsigned long*)src);
164 }
165 
ucs_config_clone_ulong(const void * src,void * dest,const void * arg)166 ucs_status_t ucs_config_clone_ulong(const void *src, void *dest, const void *arg)
167 {
168     *(unsigned long*)dest = *(unsigned long*)src;
169     return UCS_OK;
170 }
171 
ucs_config_sscanf_double(const char * buf,void * dest,const void * arg)172 int ucs_config_sscanf_double(const char *buf, void *dest, const void *arg)
173 {
174     return sscanf(buf, "%lf", (double*)dest);
175 }
176 
ucs_config_sprintf_double(char * buf,size_t max,const void * src,const void * arg)177 int ucs_config_sprintf_double(char *buf, size_t max,
178                               const void *src, const void *arg)
179 {
180     return snprintf(buf, max, "%.3f", *(double*)src);
181 }
182 
ucs_config_clone_double(const void * src,void * dest,const void * arg)183 ucs_status_t ucs_config_clone_double(const void *src, void *dest, const void *arg)
184 {
185     *(double*)dest = *(double*)src;
186     return UCS_OK;
187 }
188 
ucs_config_sscanf_hex(const char * buf,void * dest,const void * arg)189 int ucs_config_sscanf_hex(const char *buf, void *dest, const void *arg)
190 {
191     /* Special value: auto */
192     if (!strcasecmp(buf, UCS_VALUE_AUTO_STR)) {
193         *(size_t*)dest = UCS_HEXUNITS_AUTO;
194         return 1;
195     } else if (strncasecmp(buf, "0x", 2) == 0) {
196         return (sscanf(buf + 2, "%x", (unsigned int*)dest));
197     } else {
198         return 0;
199     }
200 }
201 
ucs_config_sprintf_hex(char * buf,size_t max,const void * src,const void * arg)202 int ucs_config_sprintf_hex(char *buf, size_t max,
203                            const void *src, const void *arg)
204 {
205     uint16_t val = *(uint16_t*)src;
206 
207     if (val == UCS_HEXUNITS_AUTO) {
208         return snprintf(buf, max, UCS_VALUE_AUTO_STR);
209     }
210 
211     return snprintf(buf, max, "0x%x", *(unsigned int*)src);
212 }
213 
ucs_config_sscanf_bool(const char * buf,void * dest,const void * arg)214 int ucs_config_sscanf_bool(const char *buf, void *dest, const void *arg)
215 {
216     if (!strcasecmp(buf, "y") || !strcasecmp(buf, "yes") || !strcmp(buf, "1")) {
217         *(int*)dest = 1;
218         return 1;
219     } else if (!strcasecmp(buf, "n") || !strcasecmp(buf, "no") || !strcmp(buf, "0")) {
220         *(int*)dest = 0;
221         return 1;
222     } else {
223         return 0;
224     }
225 }
226 
ucs_config_sprintf_bool(char * buf,size_t max,const void * src,const void * arg)227 int ucs_config_sprintf_bool(char *buf, size_t max, const void *src, const void *arg)
228 {
229     return snprintf(buf, max, "%c", *(int*)src ? 'y' : 'n');
230 }
231 
ucs_config_sscanf_ternary(const char * buf,void * dest,const void * arg)232 int ucs_config_sscanf_ternary(const char *buf, void *dest, const void *arg)
233 {
234     UCS_STATIC_ASSERT(UCS_NO  == 0);
235     UCS_STATIC_ASSERT(UCS_YES == 1);
236     if (!strcasecmp(buf, "try") || !strcasecmp(buf, "maybe")) {
237         *(int*)dest = UCS_TRY;
238         return 1;
239     } else {
240         return ucs_config_sscanf_bool(buf, dest, arg);
241     }
242 }
243 
ucs_config_sprintf_ternary(char * buf,size_t max,const void * src,const void * arg)244 int ucs_config_sprintf_ternary(char *buf, size_t max,
245                                const void *src, const void *arg)
246 {
247     if (*(int*)src == UCS_TRY) {
248         return snprintf(buf, max, "try");
249     } else {
250         return ucs_config_sprintf_bool(buf, max, src, arg);
251     }
252 }
253 
ucs_config_sscanf_on_off(const char * buf,void * dest,const void * arg)254 int ucs_config_sscanf_on_off(const char *buf, void *dest, const void *arg)
255 {
256     if (!strcasecmp(buf, "on") || !strcmp(buf, "1")) {
257         *(int*)dest = UCS_CONFIG_ON;
258         return 1;
259     } else if (!strcasecmp(buf, "off") || !strcmp(buf, "0")) {
260         *(int*)dest = UCS_CONFIG_OFF;
261         return 1;
262     } else {
263         return 0;
264     }
265 }
266 
ucs_config_sscanf_on_off_auto(const char * buf,void * dest,const void * arg)267 int ucs_config_sscanf_on_off_auto(const char *buf, void *dest, const void *arg)
268 {
269     if (!strcasecmp(buf, "try")   ||
270         !strcasecmp(buf, "maybe") ||
271         !strcasecmp(buf, "auto")) {
272         *(int*)dest = UCS_CONFIG_AUTO;
273         return 1;
274     } else {
275         return ucs_config_sscanf_on_off(buf, dest, arg);
276     }
277 }
278 
ucs_config_sprintf_on_off_auto(char * buf,size_t max,const void * src,const void * arg)279 int ucs_config_sprintf_on_off_auto(char *buf, size_t max,
280                                    const void *src, const void *arg)
281 {
282     switch (*(int*)src) {
283     case UCS_CONFIG_AUTO:
284         return snprintf(buf, max, "auto");
285     case UCS_CONFIG_ON:
286         return snprintf(buf, max, "on");
287     case UCS_CONFIG_OFF:
288         return snprintf(buf, max, "off");
289     default:
290         return snprintf(buf, max, "%d", *(int*)src);
291     }
292 }
293 
ucs_config_sscanf_enum(const char * buf,void * dest,const void * arg)294 int ucs_config_sscanf_enum(const char *buf, void *dest, const void *arg)
295 {
296     int i;
297 
298     i = __find_string_in_list(buf, (const char**)arg);
299     if (i < 0) {
300         return 0;
301     }
302 
303     *(unsigned*)dest = i;
304     return 1;
305 }
306 
ucs_config_sprintf_enum(char * buf,size_t max,const void * src,const void * arg)307 int ucs_config_sprintf_enum(char *buf, size_t max,
308                             const void *src, const void *arg)
309 {
310     char * const *table = arg;
311     strncpy(buf, table[*(unsigned*)src], max);
312     return 1;
313 }
314 
__print_table_values(char * const * table,char * buf,size_t max)315 static void __print_table_values(char * const *table, char *buf, size_t max)
316 {
317     char *ptr = buf, *end = buf + max;
318 
319     for (; *table; ++table) {
320         snprintf(ptr, end - ptr, "|%s", *table);
321         ptr += strlen(ptr);
322     }
323 
324     snprintf(ptr, end - ptr, "]");
325 
326     *buf = '[';
327 }
328 
ucs_config_help_enum(char * buf,size_t max,const void * arg)329 void ucs_config_help_enum(char *buf, size_t max, const void *arg)
330 {
331     __print_table_values(arg, buf, max);
332 }
333 
ucs_config_clone_log_comp(const void * src,void * dst,const void * arg)334 ucs_status_t ucs_config_clone_log_comp(const void *src, void *dst, const void *arg)
335 {
336     const ucs_log_component_config_t *src_comp = src;
337     ucs_log_component_config_t       *dst_comp = dst;
338 
339     dst_comp->log_level = src_comp->log_level;
340     ucs_strncpy_safe(dst_comp->name, src_comp->name, sizeof(dst_comp->name));
341 
342     return UCS_OK;
343 }
344 
ucs_config_sscanf_bitmap(const char * buf,void * dest,const void * arg)345 int ucs_config_sscanf_bitmap(const char *buf, void *dest, const void *arg)
346 {
347     char *str = strdup(buf);
348     char *p, *saveptr;
349     int ret, i;
350 
351     if (str == NULL) {
352         return 0;
353     }
354 
355     ret = 1;
356     *((unsigned*)dest) = 0;
357     p = strtok_r(str, ",", &saveptr);
358     while (p != NULL) {
359         i = __find_string_in_list(p, (const char**)arg);
360         if (i < 0) {
361             ret = 0;
362             break;
363         }
364         *((unsigned*)dest) |= UCS_BIT(i);
365         p = strtok_r(NULL, ",", &saveptr);
366     }
367 
368     free(str);
369     return ret;
370 }
371 
ucs_config_sprintf_bitmap(char * buf,size_t max,const void * src,const void * arg)372 int ucs_config_sprintf_bitmap(char *buf, size_t max,
373                               const void *src, const void *arg)
374 {
375     ucs_flags_str(buf, max, *((unsigned*)src), (const char**)arg);
376     return 1;
377 }
378 
ucs_config_help_bitmap(char * buf,size_t max,const void * arg)379 void ucs_config_help_bitmap(char *buf, size_t max, const void *arg)
380 {
381     snprintf(buf, max, "comma-separated list of: ");
382     __print_table_values(arg, buf + strlen(buf), max - strlen(buf));
383 }
384 
ucs_config_sscanf_bitmask(const char * buf,void * dest,const void * arg)385 int ucs_config_sscanf_bitmask(const char *buf, void *dest, const void *arg)
386 {
387     int ret = sscanf(buf, "%u", (unsigned*)dest);
388     if (*(unsigned*)dest != 0) {
389         *(unsigned*)dest = UCS_BIT(*(unsigned*)dest) - 1;
390     }
391     return ret;
392 }
393 
ucs_config_sprintf_bitmask(char * buf,size_t max,const void * src,const void * arg)394 int ucs_config_sprintf_bitmask(char *buf, size_t max,
395                                const void *src, const void *arg)
396 {
397     return snprintf(buf, max, "%u", __builtin_popcount(*(unsigned*)src));
398 }
399 
ucs_config_sscanf_time(const char * buf,void * dest,const void * arg)400 int ucs_config_sscanf_time(const char *buf, void *dest, const void *arg)
401 {
402     char units[3];
403     int num_fields;
404     double value;
405     double per_sec;
406 
407     memset(units, 0, sizeof(units));
408     num_fields = sscanf(buf, "%lf%c%c", &value, &units[0], &units[1]);
409     if (num_fields == 1) {
410         per_sec = 1;
411     } else if (num_fields == 2 || num_fields == 3) {
412         if (!strcmp(units, "m")) {
413             per_sec = 1.0 / 60.0;
414         } else if (!strcmp(units, "s")) {
415             per_sec = 1;
416         } else if (!strcmp(units, "ms")) {
417             per_sec = UCS_MSEC_PER_SEC;
418         } else if (!strcmp(units, "us")) {
419             per_sec = UCS_USEC_PER_SEC;
420         } else if (!strcmp(units, "ns")) {
421             per_sec = UCS_NSEC_PER_SEC;
422         } else {
423             return 0;
424         }
425     } else {
426         return 0;
427     }
428 
429     *(double*)dest = value / per_sec;
430     return 1;
431 }
432 
ucs_config_sprintf_time(char * buf,size_t max,const void * src,const void * arg)433 int ucs_config_sprintf_time(char *buf, size_t max,
434                             const void *src, const void *arg)
435 {
436     snprintf(buf, max, "%.2fus", *(double*)src * UCS_USEC_PER_SEC);
437     return 1;
438 }
439 
ucs_config_sscanf_bw(const char * buf,void * dest,const void * arg)440 int ucs_config_sscanf_bw(const char *buf, void *dest, const void *arg)
441 {
442     double *dst     = (double*)dest;
443     char    str[16] = {0};
444     int     offset  = 0;
445     size_t  divider;
446     size_t  units;
447     double  value;
448     int     num_fields;
449 
450     if (!strcasecmp(buf, UCS_VALUE_AUTO_STR)) {
451         *dst = UCS_CONFIG_BW_AUTO;
452         return 1;
453     }
454 
455     num_fields = sscanf(buf, "%lf%15s", &value, str);
456     if (num_fields < 2) {
457         return 0;
458     }
459 
460     ucs_assert(num_fields == 2);
461 
462     units = (str[0] == 'b') ? 1 : ucs_string_quantity_prefix_value(str[0]);
463     if (!units) {
464         return 0;
465     }
466 
467     offset = (units == 1) ? 0 : 1;
468 
469     switch (str[offset]) {
470     case 'B':
471         divider = 1;
472         break;
473     case 'b':
474         divider = 8;
475         break;
476     default:
477         return 0;
478     }
479 
480     offset++;
481     if (strcmp(str + offset, "ps") &&
482         strcmp(str + offset, "/s") &&
483         strcmp(str + offset, "s")) {
484         return 0;
485     }
486 
487     ucs_assert((divider == 1) || (divider == 8)); /* bytes or bits */
488     *dst = value * units / divider;
489     return 1;
490 }
491 
ucs_config_sprintf_bw(char * buf,size_t max,const void * src,const void * arg)492 int ucs_config_sprintf_bw(char *buf, size_t max, const void *src,
493                           const void *arg)
494 {
495     static const double max_value = 50000.0;
496     double value                  = *(double*)src;
497     const char **suffix;
498 
499     if (UCS_CONFIG_BW_IS_AUTO(value)) {
500         ucs_strncpy_safe(buf, UCS_VALUE_AUTO_STR, max);
501         return 1;
502     }
503 
504     suffix = &ucs_memunits_suffixes[0];
505     while ((value > max_value) && (*(suffix + 1) != NULL)) {
506         value /= 1024;
507         ++suffix;
508     }
509 
510     ucs_snprintf_safe(buf, max, "%.2f%sBps", value, *suffix);
511     return 1;
512 }
513 
ucs_config_sscanf_bw_spec(const char * buf,void * dest,const void * arg)514 int ucs_config_sscanf_bw_spec(const char *buf, void *dest, const void *arg)
515 {
516     ucs_config_bw_spec_t *dst = (ucs_config_bw_spec_t*)dest;
517     char                 *delim;
518 
519     delim = strchr(buf, ':');
520     if (!delim) {
521         return 0;
522     }
523 
524     if (!ucs_config_sscanf_bw(delim + 1, &dst->bw, arg)) {
525         return 0;
526     }
527 
528     dst->name = ucs_strndup(buf, delim - buf, __func__);
529     return dst->name != NULL;
530 }
531 
ucs_config_sprintf_bw_spec(char * buf,size_t max,const void * src,const void * arg)532 int ucs_config_sprintf_bw_spec(char *buf, size_t max,
533                                const void *src, const void *arg)
534 {
535     ucs_config_bw_spec_t *bw  = (ucs_config_bw_spec_t*)src;
536     int                   len;
537 
538     if (max) {
539         snprintf(buf, max, "%s:", bw->name);
540         len = strlen(buf);
541         ucs_config_sprintf_bw(buf + len, max - len, &bw->bw, arg);
542     }
543 
544     return 1;
545 }
546 
ucs_config_clone_bw_spec(const void * src,void * dest,const void * arg)547 ucs_status_t ucs_config_clone_bw_spec(const void *src, void *dest, const void *arg)
548 {
549     ucs_config_bw_spec_t *s = (ucs_config_bw_spec_t*)src;
550     ucs_config_bw_spec_t *d = (ucs_config_bw_spec_t*)dest;
551 
552     d->bw   = s->bw;
553     d->name = ucs_strdup(s->name, __func__);
554 
555     return d->name ? UCS_OK : UCS_ERR_NO_MEMORY;
556 }
557 
ucs_config_release_bw_spec(void * ptr,const void * arg)558 void ucs_config_release_bw_spec(void *ptr, const void *arg)
559 {
560     ucs_free(((ucs_config_bw_spec_t*)ptr)->name);
561 }
562 
ucs_config_sscanf_signo(const char * buf,void * dest,const void * arg)563 int ucs_config_sscanf_signo(const char *buf, void *dest, const void *arg)
564 {
565     char *endptr;
566     int signo;
567 
568     signo = strtol(buf, &endptr, 10);
569     if (*endptr == '\0') {
570         *(int*)dest = signo;
571         return 1;
572     }
573 
574     if (!strncmp(buf, "SIG", 3)) {
575         buf += 3;
576     }
577 
578     return ucs_config_sscanf_enum(buf, dest, ucs_signal_names);
579 }
580 
ucs_config_sprintf_signo(char * buf,size_t max,const void * src,const void * arg)581 int ucs_config_sprintf_signo(char *buf, size_t max,
582                              const void *src, const void *arg)
583 {
584     return ucs_config_sprintf_enum(buf, max, src, ucs_signal_names);
585 }
586 
ucs_config_sscanf_memunits(const char * buf,void * dest,const void * arg)587 int ucs_config_sscanf_memunits(const char *buf, void *dest, const void *arg)
588 {
589     if (ucs_str_to_memunits(buf, dest) != UCS_OK) {
590         return 0;
591     }
592     return 1;
593 }
594 
ucs_config_sprintf_memunits(char * buf,size_t max,const void * src,const void * arg)595 int ucs_config_sprintf_memunits(char *buf, size_t max,
596                                 const void *src, const void *arg)
597 {
598     ucs_memunits_to_str(*(size_t*)src, buf, max);
599     return 1;
600 }
601 
ucs_config_sscanf_ulunits(const char * buf,void * dest,const void * arg)602 int ucs_config_sscanf_ulunits(const char *buf, void *dest, const void *arg)
603 {
604     /* Special value: auto */
605     if (!strcasecmp(buf, UCS_VALUE_AUTO_STR)) {
606         *(unsigned long*)dest = UCS_ULUNITS_AUTO;
607         return 1;
608     } else if (!strcasecmp(buf, UCS_NUMERIC_INF_STR)) {
609         *(unsigned long*)dest = UCS_ULUNITS_INF;
610         return 1;
611     }
612 
613     return ucs_config_sscanf_ulong(buf, dest, arg);
614 }
615 
ucs_config_sprintf_ulunits(char * buf,size_t max,const void * src,const void * arg)616 int ucs_config_sprintf_ulunits(char *buf, size_t max,
617                                const void *src, const void *arg)
618 {
619     unsigned long val = *(unsigned long*)src;
620 
621     if (val == UCS_ULUNITS_AUTO) {
622         return snprintf(buf, max, UCS_VALUE_AUTO_STR);
623     } else if (val == UCS_ULUNITS_INF) {
624         return snprintf(buf, max, UCS_NUMERIC_INF_STR);
625     }
626 
627     return ucs_config_sprintf_ulong(buf, max, src, arg);
628 }
629 
ucs_config_sscanf_range_spec(const char * buf,void * dest,const void * arg)630 int ucs_config_sscanf_range_spec(const char *buf, void *dest, const void *arg)
631 {
632     ucs_range_spec_t *range_spec = dest;
633     unsigned first, last;
634     char *p, *str;
635     int ret = 1;
636 
637     str = strdup(buf);
638     if (str == NULL) {
639         return 0;
640     }
641 
642     /* Check if got a range or a single number */
643     p = strchr(str, '-');
644     if (p == NULL) {
645         /* got only one value (not a range) */
646         if (1 != sscanf(buf, "%u", &first)) {
647             ret = 0;
648             goto out;
649         }
650         last = first;
651     } else {
652         /* got a range of numbers */
653         *p = 0;      /* split str */
654 
655         if ((1 != sscanf(str, "%u", &first))
656             || (1 != sscanf(p + 1, "%u", &last))) {
657             ret = 0;
658             goto out;
659         }
660     }
661 
662     range_spec->first = first;
663     range_spec->last = last;
664 
665 out:
666     free (str);
667     return ret;
668 }
669 
ucs_config_sprintf_range_spec(char * buf,size_t max,const void * src,const void * arg)670 int ucs_config_sprintf_range_spec(char *buf, size_t max,
671                                   const void *src, const void *arg)
672 {
673     const ucs_range_spec_t *range_spec = src;
674 
675     if (range_spec->first == range_spec->last) {
676         snprintf(buf, max, "%d", range_spec->first);
677     } else {
678         snprintf(buf, max, "%d-%d", range_spec->first, range_spec->last);
679     }
680 
681     return 1;
682 }
683 
ucs_config_clone_range_spec(const void * src,void * dest,const void * arg)684 ucs_status_t ucs_config_clone_range_spec(const void *src, void *dest, const void *arg)
685 {
686     const ucs_range_spec_t *src_range_spec = src;
687     ucs_range_spec_t *dest_ragne_spec      = dest;
688 
689     dest_ragne_spec->first = src_range_spec->first;
690     dest_ragne_spec->last = src_range_spec->last;
691 
692     return UCS_OK;
693 }
694 
ucs_config_sscanf_array(const char * buf,void * dest,const void * arg)695 int ucs_config_sscanf_array(const char *buf, void *dest, const void *arg)
696 {
697     ucs_config_array_field_t *field = dest;
698     void *temp_field;
699     const ucs_config_array_t *array = arg;
700     char *str_dup, *token, *saveptr;
701     int ret;
702     unsigned i;
703 
704     str_dup = strdup(buf);
705     if (str_dup == NULL) {
706         return 0;
707     }
708 
709     saveptr = NULL;
710     token = strtok_r(str_dup, ",", &saveptr);
711     temp_field = ucs_calloc(UCS_CONFIG_ARRAY_MAX, array->elem_size, "config array");
712     i = 0;
713     while (token != NULL) {
714         ret = array->parser.read(token, (char*)temp_field + i * array->elem_size,
715                                  array->parser.arg);
716         if (!ret) {
717             ucs_free(temp_field);
718             free(str_dup);
719             return 0;
720         }
721 
722         ++i;
723         if (i >= UCS_CONFIG_ARRAY_MAX) {
724             break;
725         }
726         token = strtok_r(NULL, ",", &saveptr);
727     }
728 
729     field->data = temp_field;
730     field->count = i;
731     free(str_dup);
732     return 1;
733 }
734 
ucs_config_sprintf_array(char * buf,size_t max,const void * src,const void * arg)735 int ucs_config_sprintf_array(char *buf, size_t max,
736                              const void *src, const void *arg)
737 {
738     const ucs_config_array_field_t *field = src;
739     const ucs_config_array_t *array       = arg;
740     size_t offset;
741     unsigned i;
742     int ret;
743 
744     offset = 0;
745     for (i = 0; i < field->count; ++i) {
746         if (i > 0 && offset < max) {
747             buf[offset++] = ',';
748         }
749         ret = array->parser.write(buf + offset, max - offset,
750                                   (char*)field->data + i * array->elem_size,
751                                   array->parser.arg);
752         if (!ret) {
753             return 0;
754         }
755 
756         offset += strlen(buf + offset);
757     }
758     return 1;
759 }
760 
ucs_config_clone_array(const void * src,void * dest,const void * arg)761 ucs_status_t ucs_config_clone_array(const void *src, void *dest, const void *arg)
762 {
763     const ucs_config_array_field_t *src_array = src;
764     const ucs_config_array_t *array           = arg;
765     ucs_config_array_field_t *dest_array      = dest;
766     ucs_status_t status;
767     unsigned i;
768 
769     dest_array->data = ucs_calloc(src_array->count, array->elem_size,
770                                   "config array");
771     if (dest_array->data == NULL) {
772         return UCS_ERR_NO_MEMORY;
773     }
774 
775     dest_array->count = src_array->count;
776     for (i = 0; i < src_array->count; ++i) {
777         status = array->parser.clone((const char*)src_array->data  + i * array->elem_size,
778                                     (char*)dest_array->data + i * array->elem_size,
779                                     array->parser.arg);
780         if (status != UCS_OK) {
781             ucs_free(dest_array->data);
782             return status;
783         }
784     }
785 
786     return UCS_OK;
787 }
788 
ucs_config_release_array(void * ptr,const void * arg)789 void ucs_config_release_array(void *ptr, const void *arg)
790 {
791     ucs_config_array_field_t *array_field = ptr;
792     const ucs_config_array_t *array = arg;
793     unsigned i;
794 
795     for (i = 0; i < array_field->count; ++i) {
796         array->parser.release((char*)array_field->data  + i * array->elem_size,
797                               array->parser.arg);
798     }
799     ucs_free(array_field->data);
800 }
801 
ucs_config_help_array(char * buf,size_t max,const void * arg)802 void ucs_config_help_array(char *buf, size_t max, const void *arg)
803 {
804     const ucs_config_array_t *array = arg;
805 
806     snprintf(buf, max, "comma-separated list of: ");
807     array->parser.help(buf + strlen(buf), max - strlen(buf), array->parser.arg);
808 }
809 
ucs_config_sscanf_table(const char * buf,void * dest,const void * arg)810 int ucs_config_sscanf_table(const char *buf, void *dest, const void *arg)
811 {
812     char *tokens;
813     char *token, *saveptr1;
814     char *name, *value, *saveptr2;
815     ucs_status_t status;
816 
817     tokens = strdup(buf);
818     if (tokens == NULL) {
819         return 0;
820     }
821 
822     saveptr1 = NULL;
823     saveptr2 = NULL;
824     token = strtok_r(tokens, ";", &saveptr1);
825     while (token != NULL) {
826         name  = strtok_r(token, "=", &saveptr2);
827         value = strtok_r(NULL,  "=", &saveptr2);
828         if (name == NULL || value == NULL) {
829             free(tokens);
830             ucs_error("Could not parse list of values in '%s' (token: '%s')", buf, token);
831             return 0;
832         }
833 
834         status = ucs_config_parser_set_value_internal(dest, (ucs_config_field_t*)arg,
835                                                      name, value, NULL, 1);
836         if (status != UCS_OK) {
837             if (status == UCS_ERR_NO_ELEM) {
838                 ucs_error("Field '%s' does not exist", name);
839             } else {
840                 ucs_debug("Failed to set %s to '%s': %s", name, value,
841                           ucs_status_string(status));
842             }
843             free(tokens);
844             return 0;
845         }
846 
847         token = strtok_r(NULL, ";", &saveptr1);
848     }
849 
850     free(tokens);
851     return 1;
852 }
853 
ucs_config_clone_table(const void * src,void * dst,const void * arg)854 ucs_status_t ucs_config_clone_table(const void *src, void *dst, const void *arg)
855 {
856     return ucs_config_parser_clone_opts(src, dst, (ucs_config_field_t*)arg);
857 }
858 
ucs_config_release_table(void * ptr,const void * arg)859 void ucs_config_release_table(void *ptr, const void *arg)
860 {
861     ucs_config_parser_release_opts(ptr, (ucs_config_field_t*)arg);
862 }
863 
ucs_config_help_table(char * buf,size_t max,const void * arg)864 void ucs_config_help_table(char *buf, size_t max, const void *arg)
865 {
866     snprintf(buf, max, "Table");
867 }
868 
ucs_config_release_nop(void * ptr,const void * arg)869 void ucs_config_release_nop(void *ptr, const void *arg)
870 {
871 }
872 
ucs_config_help_generic(char * buf,size_t max,const void * arg)873 void ucs_config_help_generic(char *buf, size_t max, const void *arg)
874 {
875     strncpy(buf, (char*)arg, max);
876 }
877 
ucs_config_is_deprecated_field(const ucs_config_field_t * field)878 static inline int ucs_config_is_deprecated_field(const ucs_config_field_t *field)
879 {
880     return (field->offset == UCS_CONFIG_DEPRECATED_FIELD_OFFSET);
881 }
882 
ucs_config_is_alias_field(const ucs_config_field_t * field)883 static inline int ucs_config_is_alias_field(const ucs_config_field_t *field)
884 {
885     return (field->dfl_value == NULL);
886 }
887 
ucs_config_is_table_field(const ucs_config_field_t * field)888 static inline int ucs_config_is_table_field(const ucs_config_field_t *field)
889 {
890     return (field->parser.read == ucs_config_sscanf_table);
891 }
892 
ucs_config_print_doc_line_by_line(const ucs_config_field_t * field,void (* cb)(int num,const char * line,void * arg),void * arg)893 static void ucs_config_print_doc_line_by_line(const ucs_config_field_t *field,
894                                               void (*cb)(int num, const char *line, void *arg),
895                                               void *arg)
896 {
897     char *doc, *line, *p;
898     int num;
899 
900     line = doc = strdup(field->doc);
901     p = strchr(line, '\n');
902     num = 0;
903     while (p != NULL) {
904         *p = '\0';
905         cb(num, line, arg);
906         line = p + 1;
907         p = strchr(line, '\n');
908         ++num;
909     }
910     cb(num, line, arg);
911     free(doc);
912 }
913 
914 static ucs_status_t
ucs_config_parser_parse_field(ucs_config_field_t * field,const char * value,void * var)915 ucs_config_parser_parse_field(ucs_config_field_t *field, const char *value, void *var)
916 {
917     char syntax_buf[256];
918     int ret;
919 
920     ret = field->parser.read(value, var, field->parser.arg);
921     if (ret != 1) {
922         if (ucs_config_is_table_field(field)) {
923             ucs_error("Could not set table value for %s: '%s'", field->name, value);
924 
925         } else {
926             field->parser.help(syntax_buf, sizeof(syntax_buf) - 1, field->parser.arg);
927             ucs_error("Invalid value for %s: '%s'. Expected: %s", field->name,
928                       value, syntax_buf);
929         }
930         return UCS_ERR_INVALID_PARAM;
931     }
932 
933     return UCS_OK;
934 }
935 
ucs_config_parser_release_field(ucs_config_field_t * field,void * var)936 static void ucs_config_parser_release_field(ucs_config_field_t *field, void *var)
937 {
938     field->parser.release(var, field->parser.arg);
939 }
940 
ucs_config_field_is_last(const ucs_config_field_t * field)941 static int ucs_config_field_is_last(const ucs_config_field_t *field)
942 {
943     return field->name == NULL;
944 }
945 
946 ucs_status_t
ucs_config_parser_set_default_values(void * opts,ucs_config_field_t * fields)947 ucs_config_parser_set_default_values(void *opts, ucs_config_field_t *fields)
948 {
949     ucs_config_field_t *field, *sub_fields;
950     ucs_status_t status;
951     void *var;
952 
953     for (field = fields; !ucs_config_field_is_last(field); ++field) {
954         if (ucs_config_is_alias_field(field) ||
955             ucs_config_is_deprecated_field(field)) {
956             continue;
957         }
958 
959         var = (char*)opts + field->offset;
960 
961         /* If this field is a sub-table, recursively set the values for it.
962          * Defaults can be subsequently set by parser.read(). */
963         if (ucs_config_is_table_field(field)) {
964             sub_fields = (ucs_config_field_t*)field->parser.arg;
965             status = ucs_config_parser_set_default_values(var, sub_fields);
966             if (status != UCS_OK) {
967                 return status;
968             }
969         }
970 
971         status = ucs_config_parser_parse_field(field, field->dfl_value, var);
972         if (status != UCS_OK) {
973             return status;
974         }
975     }
976 
977     return UCS_OK;
978 }
979 
980 /**
981  * table_prefix == NULL  -> unused
982  */
983 static ucs_status_t
ucs_config_parser_set_value_internal(void * opts,ucs_config_field_t * fields,const char * name,const char * value,const char * table_prefix,int recurse)984 ucs_config_parser_set_value_internal(void *opts, ucs_config_field_t *fields,
985                                      const char *name, const char *value,
986                                      const char *table_prefix, int recurse)
987 {
988     ucs_config_field_t *field, *sub_fields;
989     size_t prefix_len;
990     ucs_status_t status;
991     unsigned count;
992     void *var;
993 
994     prefix_len = (table_prefix == NULL) ? 0 : strlen(table_prefix);
995 
996     count = 0;
997     for (field = fields; !ucs_config_field_is_last(field); ++field) {
998 
999         var = (char*)opts + field->offset;
1000 
1001         if (ucs_config_is_table_field(field)) {
1002             sub_fields = (ucs_config_field_t*)field->parser.arg;
1003 
1004             /* Check with sub-table prefix */
1005             if (recurse) {
1006                 status = ucs_config_parser_set_value_internal(var, sub_fields,
1007                                                              name, value,
1008                                                              field->name, 1);
1009                 if (status == UCS_OK) {
1010                     ++count;
1011                 } else if (status != UCS_ERR_NO_ELEM) {
1012                     return status;
1013                 }
1014             }
1015 
1016             /* Possible override with my prefix */
1017             if (table_prefix != NULL) {
1018                 status = ucs_config_parser_set_value_internal(var, sub_fields,
1019                                                              name, value,
1020                                                              table_prefix, 0);
1021                 if (status == UCS_OK) {
1022                     ++count;
1023                 } else if (status != UCS_ERR_NO_ELEM) {
1024                     return status;
1025                 }
1026             }
1027         } else if (((table_prefix == NULL) || !strncmp(name, table_prefix, prefix_len)) &&
1028                    !strcmp(name + prefix_len, field->name))
1029         {
1030             if (ucs_config_is_deprecated_field(field)) {
1031                 return UCS_ERR_NO_ELEM;
1032             }
1033 
1034             ucs_config_parser_release_field(field, var);
1035             status = ucs_config_parser_parse_field(field, value, var);
1036             if (status != UCS_OK) {
1037                 return status;
1038             }
1039             ++count;
1040         }
1041     }
1042 
1043     return (count == 0) ? UCS_ERR_NO_ELEM : UCS_OK;
1044 }
1045 
ucs_config_parser_mark_env_var_used(const char * name,int * added)1046 static void ucs_config_parser_mark_env_var_used(const char *name, int *added)
1047 {
1048     khiter_t iter;
1049     char *key;
1050     int ret;
1051 
1052     *added = 0;
1053 
1054     if (!ucs_global_opts.warn_unused_env_vars) {
1055         return;
1056     }
1057 
1058     pthread_mutex_lock(&ucs_config_parser_env_vars_hash_lock);
1059 
1060     iter = kh_get(ucs_config_env_vars, &ucs_config_parser_env_vars, name);
1061     if (iter != kh_end(&ucs_config_parser_env_vars)) {
1062         goto out; /* already exists */
1063     }
1064 
1065     key = ucs_strdup(name, "config_parser_env_var");
1066     if (key == NULL) {
1067         ucs_error("strdup(%s) failed", name);
1068         goto out;
1069     }
1070 
1071 #ifndef __clang_analyzer__
1072     /* Exclude this code from Clang examination as it generates
1073      * false-postive warning about potential leak of memory
1074      * pointed to by 'key' variable */
1075     iter = kh_put(ucs_config_env_vars, &ucs_config_parser_env_vars, key, &ret);
1076     if ((ret <= 0) || (iter == kh_end(&ucs_config_parser_env_vars))) {
1077         ucs_warn("kh_put(key=%s) failed", key);
1078         ucs_free(key);
1079         goto out;
1080     }
1081 #else
1082     ucs_free(key);
1083 #endif
1084 
1085     *added = 1;
1086 
1087 out:
1088     pthread_mutex_unlock(&ucs_config_parser_env_vars_hash_lock);
1089 }
1090 
ucs_config_apply_env_vars(void * opts,ucs_config_field_t * fields,const char * prefix,const char * table_prefix,int recurse,int ignore_errors)1091 static ucs_status_t ucs_config_apply_env_vars(void *opts, ucs_config_field_t *fields,
1092                                              const char *prefix, const char *table_prefix,
1093                                              int recurse, int ignore_errors)
1094 {
1095     ucs_config_field_t *field, *sub_fields;
1096     ucs_status_t status;
1097     size_t prefix_len;
1098     const char *env_value;
1099     void *var;
1100     char buf[256];
1101     int added;
1102 
1103     /* Put prefix in the buffer. Later we replace only the variable name part */
1104     snprintf(buf, sizeof(buf) - 1, "%s%s", prefix, table_prefix ? table_prefix : "");
1105     prefix_len = strlen(buf);
1106 
1107     /* Parse environment variables */
1108     for (field = fields; !ucs_config_field_is_last(field); ++field) {
1109 
1110         var = (char*)opts + field->offset;
1111 
1112         if (ucs_config_is_table_field(field)) {
1113             sub_fields = (ucs_config_field_t*)field->parser.arg;
1114 
1115             /* Parse with sub-table prefix */
1116             if (recurse) {
1117                 status = ucs_config_apply_env_vars(var, sub_fields, prefix,
1118                                                    field->name, 1, ignore_errors);
1119                 if (status != UCS_OK) {
1120                     return status;
1121                 }
1122             }
1123 
1124             /* Possible override with my prefix */
1125             if (table_prefix) {
1126                 status = ucs_config_apply_env_vars(var, sub_fields, prefix,
1127                                                    table_prefix, 0, ignore_errors);
1128                 if (status != UCS_OK) {
1129                     return status;
1130                 }
1131             }
1132         } else {
1133             /* Read and parse environment variable */
1134             strncpy(buf + prefix_len, field->name, sizeof(buf) - prefix_len - 1);
1135             env_value = getenv(buf);
1136             if (env_value == NULL) {
1137                 continue;
1138             }
1139 
1140             ucs_config_parser_mark_env_var_used(buf, &added);
1141 
1142             if (ucs_config_is_deprecated_field(field)) {
1143                 if (added && !ignore_errors) {
1144                     ucs_warn("%s is deprecated (set %s%s=n to suppress this warning)",
1145                              buf, UCS_DEFAULT_ENV_PREFIX,
1146                              UCS_GLOBAL_OPTS_WARN_UNUSED_CONFIG);
1147                 }
1148             } else {
1149                 ucs_config_parser_release_field(field, var);
1150                 status = ucs_config_parser_parse_field(field, env_value, var);
1151                 if (status != UCS_OK) {
1152                     /* If set to ignore errors, restore the default value */
1153                     ucs_status_t tmp_status =
1154                         ucs_config_parser_parse_field(field, field->dfl_value,
1155                                                       var);
1156                     if (ignore_errors) {
1157                         status = tmp_status;
1158                     }
1159                 }
1160                 if (status != UCS_OK) {
1161                     return status;
1162                 }
1163             }
1164         }
1165     }
1166 
1167     return UCS_OK;
1168 }
1169 
1170 /* Find if env_prefix consists of multiple prefixes and returns pointer
1171  * to rightmost in this case, otherwise returns NULL
1172  */
ucs_config_parser_get_sub_prefix(const char * env_prefix,const char ** sub_prefix_p)1173 static ucs_status_t ucs_config_parser_get_sub_prefix(const char *env_prefix,
1174                                                      const char **sub_prefix_p)
1175 {
1176     size_t len;
1177 
1178     /* env_prefix always has "_" at the end and we want to find the last but one
1179      * "_" in the env_prefix */
1180     len = strlen(env_prefix);
1181     if (len < 2) {
1182         ucs_error("Invalid value of env_prefix: '%s'", env_prefix);
1183         return UCS_ERR_INVALID_PARAM;
1184     }
1185 
1186     len -= 2;
1187     while ((len > 0) && (env_prefix[len - 1] != '_')) {
1188         len -= 1;
1189     }
1190     *sub_prefix_p = (len > 0) ? (env_prefix + len): NULL;
1191 
1192     return UCS_OK;
1193 }
1194 
ucs_config_parser_fill_opts(void * opts,ucs_config_field_t * fields,const char * env_prefix,const char * table_prefix,int ignore_errors)1195 ucs_status_t ucs_config_parser_fill_opts(void *opts, ucs_config_field_t *fields,
1196                                          const char *env_prefix,
1197                                          const char *table_prefix,
1198                                          int ignore_errors)
1199 {
1200     const char   *sub_prefix = NULL;
1201     ucs_status_t status;
1202 
1203     /* Set default values */
1204     status = ucs_config_parser_set_default_values(opts, fields);
1205     if (status != UCS_OK) {
1206         goto err;
1207     }
1208 
1209     ucs_assert(env_prefix != NULL);
1210     status = ucs_config_parser_get_sub_prefix(env_prefix, &sub_prefix);
1211     if (status != UCS_OK) {
1212         goto err;
1213     }
1214 
1215     /* Apply environment variables */
1216     if (sub_prefix != NULL) {
1217         status = ucs_config_apply_env_vars(opts, fields, sub_prefix, table_prefix,
1218                                            1, ignore_errors);
1219         if (status != UCS_OK) {
1220             goto err_free;
1221         }
1222     }
1223 
1224     /* Apply environment variables with custom prefix */
1225     status = ucs_config_apply_env_vars(opts, fields, env_prefix, table_prefix,
1226                                         1, ignore_errors);
1227     if (status != UCS_OK) {
1228         goto err_free;
1229     }
1230 
1231     return UCS_OK;
1232 
1233 err_free:
1234     ucs_config_parser_release_opts(opts, fields); /* Release default values */
1235 err:
1236     return status;
1237 }
1238 
ucs_config_parser_set_value(void * opts,ucs_config_field_t * fields,const char * name,const char * value)1239 ucs_status_t ucs_config_parser_set_value(void *opts, ucs_config_field_t *fields,
1240                                         const char *name, const char *value)
1241 {
1242     return ucs_config_parser_set_value_internal(opts, fields, name, value, NULL, 1);
1243 }
1244 
ucs_config_parser_get_value(void * opts,ucs_config_field_t * fields,const char * name,char * value,size_t max)1245 ucs_status_t ucs_config_parser_get_value(void *opts, ucs_config_field_t *fields,
1246                                          const char *name, char *value,
1247                                          size_t max)
1248 {
1249     ucs_config_field_t  *field;
1250     ucs_config_field_t  *sub_fields;
1251     void                *sub_opts;
1252     void                *value_ptr;
1253     size_t              name_len;
1254     ucs_status_t        status;
1255 
1256     if (!opts || !fields || !name || (!value && (max > 0))) {
1257         return UCS_ERR_INVALID_PARAM;
1258     }
1259 
1260     for (field = fields, status = UCS_ERR_NO_ELEM;
1261          !ucs_config_field_is_last(field) && (status == UCS_ERR_NO_ELEM); ++field) {
1262 
1263         name_len = strlen(field->name);
1264 
1265         ucs_trace("compare name \"%s\" with field \"%s\" which is %s subtable",
1266                   name, field->name,
1267                   ucs_config_is_table_field(field) ? "a" : "NOT a");
1268 
1269         if (ucs_config_is_table_field(field) &&
1270             !strncmp(field->name, name, name_len)) {
1271 
1272             sub_fields = (ucs_config_field_t*)field->parser.arg;
1273             sub_opts   = (char*)opts + field->offset;
1274             status     = ucs_config_parser_get_value(sub_opts, sub_fields,
1275                                                      name + name_len,
1276                                                      value, max);
1277         } else if (!strncmp(field->name, name, strlen(name))) {
1278             if (value) {
1279                 value_ptr = (char *)opts + field->offset;
1280                 field->parser.write(value, max, value_ptr, field->parser.arg);
1281             }
1282             status = UCS_OK;
1283         }
1284     }
1285 
1286     return status;
1287 }
1288 
ucs_config_parser_clone_opts(const void * src,void * dst,ucs_config_field_t * fields)1289 ucs_status_t ucs_config_parser_clone_opts(const void *src, void *dst,
1290                                          ucs_config_field_t *fields)
1291 {
1292     ucs_status_t status;
1293 
1294     ucs_config_field_t *field;
1295     for (field = fields; !ucs_config_field_is_last(field); ++field) {
1296         if (ucs_config_is_alias_field(field) ||
1297             ucs_config_is_deprecated_field(field)) {
1298             continue;
1299         }
1300 
1301         status = field->parser.clone((const char*)src + field->offset,
1302                                     (char*)dst + field->offset,
1303                                     field->parser.arg);
1304         if (status != UCS_OK) {
1305             ucs_error("Failed to clone the filed '%s': %s", field->name,
1306                       ucs_status_string(status));
1307             return status;
1308         }
1309     }
1310 
1311     return UCS_OK;
1312 }
1313 
ucs_config_parser_release_opts(void * opts,ucs_config_field_t * fields)1314 void ucs_config_parser_release_opts(void *opts, ucs_config_field_t *fields)
1315 {
1316     ucs_config_field_t *field;
1317 
1318     for (field = fields; !ucs_config_field_is_last(field); ++field) {
1319         if (ucs_config_is_alias_field(field) ||
1320             ucs_config_is_deprecated_field(field)) {
1321             continue;
1322         }
1323 
1324         ucs_config_parser_release_field(field, (char*)opts + field->offset);
1325     }
1326 }
1327 
1328 /*
1329  * Finds the "real" field, which the given field is alias of.
1330  * *p_alias_table_offset is filled with the offset of the sub-table containing
1331  * the field, it may be non-0 if the alias is found in a sub-table.
1332  */
1333 static const ucs_config_field_t *
ucs_config_find_aliased_field(const ucs_config_field_t * fields,const ucs_config_field_t * alias,size_t * p_alias_table_offset)1334 ucs_config_find_aliased_field(const ucs_config_field_t *fields,
1335                               const ucs_config_field_t *alias,
1336                               size_t *p_alias_table_offset)
1337 {
1338     const ucs_config_field_t *field, *result;
1339     size_t offset;
1340 
1341     for (field = fields; !ucs_config_field_is_last(field); ++field) {
1342         if (field == alias) {
1343             /* skip */
1344             continue;
1345         } else if (ucs_config_is_table_field(field)) {
1346             result = ucs_config_find_aliased_field(field->parser.arg, alias,
1347                                                    &offset);
1348             if (result != NULL) {
1349                 *p_alias_table_offset = offset + field->offset;
1350                 return result;
1351             }
1352         } else if (field->offset == alias->offset) {
1353             *p_alias_table_offset = 0;
1354             return field;
1355         }
1356     }
1357 
1358     return NULL;
1359 }
1360 
__print_stream_cb(int num,const char * line,void * arg)1361 static void __print_stream_cb(int num, const char *line, void *arg)
1362 {
1363     FILE *stream = arg;
1364     fprintf(stream, "# %s\n", line);
1365 }
1366 
1367 static void
ucs_config_parser_print_field(FILE * stream,const void * opts,const char * env_prefix,ucs_list_link_t * prefix_list,const char * name,const ucs_config_field_t * field,unsigned long flags,const char * docstr,...)1368 ucs_config_parser_print_field(FILE *stream, const void *opts, const char *env_prefix,
1369                               ucs_list_link_t *prefix_list, const char *name,
1370                               const ucs_config_field_t *field, unsigned long flags,
1371                               const char *docstr, ...)
1372 {
1373     ucs_config_parser_prefix_t *prefix, *head;
1374     char value_buf[128]  = {0};
1375     char syntax_buf[256] = {0};
1376     va_list ap;
1377 
1378     ucs_assert(!ucs_list_is_empty(prefix_list));
1379     head = ucs_list_head(prefix_list, ucs_config_parser_prefix_t, list);
1380 
1381     if (ucs_config_is_deprecated_field(field)) {
1382         snprintf(value_buf, sizeof(value_buf), " (deprecated)");
1383         snprintf(syntax_buf, sizeof(syntax_buf), "N/A");
1384     } else {
1385         snprintf(value_buf, sizeof(value_buf), "=");
1386         field->parser.write(value_buf + 1, sizeof(value_buf) - 2,
1387                             (char*)opts + field->offset,
1388                             field->parser.arg);
1389         field->parser.help(syntax_buf, sizeof(syntax_buf) - 1, field->parser.arg);
1390     }
1391 
1392     if (flags & UCS_CONFIG_PRINT_DOC) {
1393         fprintf(stream, "#\n");
1394         ucs_config_print_doc_line_by_line(field, __print_stream_cb, stream);
1395         fprintf(stream, "#\n");
1396         fprintf(stream, "# %-*s %s\n", UCS_CONFIG_PARSER_DOCSTR_WIDTH, "syntax:",
1397                 syntax_buf);
1398 
1399         /* Extra docstring */
1400         if (docstr != NULL) {
1401             fprintf(stream, "# ");
1402             va_start(ap, docstr);
1403             vfprintf(stream, docstr, ap);
1404             va_end(ap);
1405             fprintf(stream, "\n");
1406         }
1407 
1408         /* Parents in configuration hierarchy */
1409         if (prefix_list->next != prefix_list->prev) {
1410             fprintf(stream, "# %-*s", UCS_CONFIG_PARSER_DOCSTR_WIDTH, "inherits:");
1411             ucs_list_for_each(prefix, prefix_list, list) {
1412                 if (prefix == head) {
1413                     continue;
1414                 }
1415 
1416                 fprintf(stream, " %s%s%s", env_prefix, prefix->prefix, name);
1417                 if (prefix != ucs_list_tail(prefix_list, ucs_config_parser_prefix_t, list)) {
1418                     fprintf(stream, ",");
1419                 }
1420             }
1421             fprintf(stream, "\n");
1422         }
1423 
1424         fprintf(stream, "#\n");
1425     }
1426 
1427     fprintf(stream, "%s%s%s%s\n", env_prefix, head->prefix, name, value_buf);
1428 
1429     if (flags & UCS_CONFIG_PRINT_DOC) {
1430         fprintf(stream, "\n");
1431     }
1432 }
1433 
1434 static void
ucs_config_parser_print_opts_recurs(FILE * stream,const void * opts,const ucs_config_field_t * fields,unsigned flags,const char * prefix,ucs_list_link_t * prefix_list)1435 ucs_config_parser_print_opts_recurs(FILE *stream, const void *opts,
1436                                     const ucs_config_field_t *fields,
1437                                     unsigned flags, const char *prefix,
1438                                     ucs_list_link_t *prefix_list)
1439 {
1440     const ucs_config_field_t *field, *aliased_field;
1441     ucs_config_parser_prefix_t *head;
1442     ucs_config_parser_prefix_t inner_prefix;
1443     size_t alias_table_offset;
1444 
1445     for (field = fields; !ucs_config_field_is_last(field); ++field) {
1446         if (ucs_config_is_table_field(field)) {
1447             /* Parse with sub-table prefix.
1448              * We start the leaf prefix and continue up the hierarchy.
1449              */
1450             /* Do not add the same prefix several times in a sequence. It can
1451              * happen when similiar prefix names were used during config
1452              * table inheritance, e.g. "IB_" -> "RC_" -> "RC_". We check the
1453              * previous entry only, since it is currently impossible if
1454              * something like "RC_" -> "IB_" -> "RC_" will be used. */
1455             if (ucs_list_is_empty(prefix_list) ||
1456                 strcmp(ucs_list_tail(prefix_list,
1457                                      ucs_config_parser_prefix_t,
1458                                      list)->prefix, field->name)) {
1459                 inner_prefix.prefix = field->name;
1460                 ucs_list_add_tail(prefix_list, &inner_prefix.list);
1461             } else {
1462                 inner_prefix.prefix = NULL;
1463             }
1464 
1465             ucs_config_parser_print_opts_recurs(stream,
1466                                                 UCS_PTR_BYTE_OFFSET(opts, field->offset),
1467                                                 field->parser.arg, flags,
1468                                                 prefix, prefix_list);
1469 
1470             if (inner_prefix.prefix != NULL) {
1471                 ucs_list_del(&inner_prefix.list);
1472             }
1473         } else if (ucs_config_is_alias_field(field)) {
1474             if (flags & UCS_CONFIG_PRINT_HIDDEN) {
1475                 aliased_field =
1476                     ucs_config_find_aliased_field(fields, field,
1477                                                   &alias_table_offset);
1478                 if (aliased_field == NULL) {
1479                     ucs_fatal("could not find aliased field of %s", field->name);
1480                 }
1481 
1482                 head = ucs_list_head(prefix_list, ucs_config_parser_prefix_t, list);
1483 
1484                 ucs_config_parser_print_field(stream,
1485                                               UCS_PTR_BYTE_OFFSET(opts, alias_table_offset),
1486                                               prefix, prefix_list,
1487                                               field->name, aliased_field,
1488                                               flags, "%-*s %s%s%s",
1489                                               UCS_CONFIG_PARSER_DOCSTR_WIDTH,
1490                                               "alias of:", prefix,
1491                                               head->prefix,
1492                                               aliased_field->name);
1493             }
1494         } else {
1495             if (ucs_config_is_deprecated_field(field) &&
1496                 !(flags & UCS_CONFIG_PRINT_HIDDEN)) {
1497                 continue;
1498             }
1499             ucs_config_parser_print_field(stream, opts, prefix, prefix_list,
1500                                           field->name, field, flags, NULL);
1501         }
1502     }
1503 }
1504 
ucs_config_parser_print_opts(FILE * stream,const char * title,const void * opts,ucs_config_field_t * fields,const char * table_prefix,const char * prefix,ucs_config_print_flags_t flags)1505 void ucs_config_parser_print_opts(FILE *stream, const char *title, const void *opts,
1506                                   ucs_config_field_t *fields, const char *table_prefix,
1507                                   const char *prefix, ucs_config_print_flags_t flags)
1508 {
1509     ucs_config_parser_prefix_t table_prefix_elem;
1510     UCS_LIST_HEAD(prefix_list);
1511 
1512     if (flags & UCS_CONFIG_PRINT_HEADER) {
1513         fprintf(stream, "\n");
1514         fprintf(stream, "#\n");
1515         fprintf(stream, "# %s\n", title);
1516         fprintf(stream, "#\n");
1517         fprintf(stream, "\n");
1518     }
1519 
1520     if (flags & UCS_CONFIG_PRINT_CONFIG) {
1521         table_prefix_elem.prefix = table_prefix ? table_prefix : "";
1522         ucs_list_add_tail(&prefix_list, &table_prefix_elem.list);
1523         ucs_config_parser_print_opts_recurs(stream, opts, fields, flags,
1524                                             prefix, &prefix_list);
1525     }
1526 
1527     if (flags & UCS_CONFIG_PRINT_HEADER) {
1528         fprintf(stream, "\n");
1529     }
1530 }
1531 
ucs_config_parser_print_all_opts(FILE * stream,const char * prefix,ucs_config_print_flags_t flags)1532 void ucs_config_parser_print_all_opts(FILE *stream, const char *prefix,
1533                                       ucs_config_print_flags_t flags)
1534 {
1535     const ucs_config_global_list_entry_t *entry;
1536     ucs_status_t status;
1537     char title[64];
1538     void *opts;
1539 
1540     ucs_list_for_each(entry, &ucs_config_global_list, list) {
1541         if ((entry->table == NULL) ||
1542             (ucs_config_field_is_last(&entry->table[0]))) {
1543             /* don't print title for an empty configuration table */
1544             continue;
1545         }
1546 
1547         opts = ucs_malloc(entry->size, "tmp_opts");
1548         if (opts == NULL) {
1549             ucs_error("could not allocate configuration of size %zu", entry->size);
1550             continue;
1551         }
1552 
1553         status = ucs_config_parser_fill_opts(opts, entry->table, prefix,
1554                                              entry->prefix, 0);
1555         if (status != UCS_OK) {
1556             ucs_free(opts);
1557             continue;
1558         }
1559 
1560         snprintf(title, sizeof(title), "%s configuration", entry->name);
1561         ucs_config_parser_print_opts(stream, title, opts, entry->table,
1562                                      entry->prefix, prefix, flags);
1563 
1564         ucs_config_parser_release_opts(opts, entry->table);
1565         ucs_free(opts);
1566     }
1567 }
1568 
ucs_config_parser_warn_unused_env_vars(const char * prefix)1569 static void ucs_config_parser_warn_unused_env_vars(const char *prefix)
1570 {
1571     char unused_env_vars_names[40];
1572     int num_unused_vars;
1573     char **envp, *envstr;
1574     size_t prefix_len;
1575     char *var_name;
1576     char *p, *endp;
1577     khiter_t iter;
1578     char *saveptr;
1579     int truncated;
1580     int ret;
1581 
1582     if (!ucs_global_opts.warn_unused_env_vars) {
1583         return;
1584     }
1585 
1586     pthread_mutex_lock(&ucs_config_parser_env_vars_hash_lock);
1587 
1588     prefix_len      = strlen(prefix);
1589     p               = unused_env_vars_names;
1590     endp            = p + sizeof(unused_env_vars_names) - 1;
1591     *endp           = '\0';
1592     truncated       = 0;
1593     num_unused_vars = 0;
1594 
1595     for (envp = environ; !truncated && (*envp != NULL); ++envp) {
1596         envstr = ucs_strdup(*envp, "env_str");
1597         if (envstr == NULL) {
1598             continue;
1599         }
1600 
1601         var_name = strtok_r(envstr, "=", &saveptr);
1602         if (!var_name || strncmp(var_name, prefix, prefix_len)) {
1603             ucs_free(envstr);
1604             continue; /* Not UCX */
1605         }
1606 
1607         iter = kh_get(ucs_config_env_vars, &ucs_config_parser_env_vars, var_name);
1608         if (iter == kh_end(&ucs_config_parser_env_vars)) {
1609             ret = snprintf(p, endp - p, " %s,", var_name);
1610             if (ret > endp - p) {
1611                 truncated = 1;
1612                 *p = '\0';
1613             } else {
1614                 p += strlen(p);
1615                 ++num_unused_vars;
1616             }
1617         }
1618 
1619         ucs_free(envstr);
1620     }
1621 
1622     if (num_unused_vars > 0) {
1623         if (!truncated) {
1624             p[-1] = '\0'; /* remove trailing comma */
1625         }
1626         ucs_warn("unused env variable%s:%s%s (set %s%s=n to suppress this warning)",
1627                  (num_unused_vars > 1) ? "s" : "", unused_env_vars_names,
1628                  truncated ? "..." : "", UCS_DEFAULT_ENV_PREFIX,
1629                  UCS_GLOBAL_OPTS_WARN_UNUSED_CONFIG);
1630     }
1631 
1632     pthread_mutex_unlock(&ucs_config_parser_env_vars_hash_lock);
1633 }
1634 
ucs_config_parser_warn_unused_env_vars_once(const char * env_prefix)1635 void ucs_config_parser_warn_unused_env_vars_once(const char *env_prefix)
1636 {
1637     const char   *sub_prefix = NULL;
1638     int          added;
1639     ucs_status_t status;
1640 
1641     /* Although env_prefix is not real environment variable put it
1642      * into table anyway to save prefixes which was already checked.
1643      * Need to save both env_prefix and base_prefix */
1644     ucs_config_parser_mark_env_var_used(env_prefix, &added);
1645     if (!added) {
1646         return;
1647     }
1648 
1649     ucs_config_parser_warn_unused_env_vars(env_prefix);
1650 
1651     status = ucs_config_parser_get_sub_prefix(env_prefix, &sub_prefix);
1652     if (status != UCS_OK) {
1653         return;
1654     }
1655 
1656     if (sub_prefix == NULL) {
1657         return;
1658     }
1659 
1660     ucs_config_parser_mark_env_var_used(sub_prefix, &added);
1661     if (!added) {
1662         return;
1663     }
1664 
1665     ucs_config_parser_warn_unused_env_vars(sub_prefix);
1666 }
1667 
ucs_config_memunits_get(size_t config_size,size_t auto_size,size_t max_size)1668 size_t ucs_config_memunits_get(size_t config_size, size_t auto_size,
1669                                size_t max_size)
1670 {
1671     if (config_size == UCS_MEMUNITS_AUTO) {
1672         return auto_size;
1673     } else {
1674         return ucs_min(config_size, max_size);
1675     }
1676 }
1677 
ucs_config_names_search(ucs_config_names_array_t config_names,const char * str)1678 int ucs_config_names_search(ucs_config_names_array_t config_names,
1679                             const char *str)
1680 {
1681     unsigned i;
1682 
1683     for (i = 0; i < config_names.count; ++i) {
1684         if (!fnmatch(config_names.names[i], str, 0)) {
1685            return i;
1686         }
1687     }
1688 
1689     return -1;
1690 }
1691 
1692 UCS_STATIC_CLEANUP {
1693     const char *key;
1694 
1695     kh_foreach_key(&ucs_config_parser_env_vars, key, {
1696         ucs_free((void*)key);
1697     })
1698     kh_destroy_inplace(ucs_config_env_vars, &ucs_config_parser_env_vars);
1699 }
1700