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