master
c 578 lines 18.7 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "inicfg_internals.h"
4
5 const char *inicfg_get(struct config *root, const char *section, const char *name, const char *default_value) {
6 struct config_option *opt = inicfg_get_raw_value(root, section, name, default_value, CONFIG_VALUE_TYPE_TEXT, NULL);
7 if(!opt)
8 // the only way for opt to be NULL, is default_value to be NULL too
9 return NULL;
10
11 return string2str(opt->value);
12 }
13
14 const char *inicfg_set(struct config *root, const char *section, const char *name, const char *value) {
15 struct config_option *opt = inicfg_set_raw_value(root, section, name, value, CONFIG_VALUE_TYPE_TEXT);
16 return string2str(opt->value);
17 }
18
19 static STRING *reformat_path(STRING *value) {
20 #if defined(OS_WINDOWS)
21 CLEAN_CHAR_P *converted = os_translate_windows_to_msys_path(string2str(value));
22 if(string_strcmp(value, converted) != 0) {
23 string_freez(value);
24 return string_strdupz(converted);
25 }
26 // value unchanged: fall through and return as-is
27 #else
28 // no-op on non-Windows: paths are always POSIX-style
29 (void)value;
30 #endif
31
32 return value;
33 }
34
35 #if defined(OS_WINDOWS)
36 static bool log_setting_output_is_special(const char *output) {
37 return !output || !*output ||
38 strcmp(output, "none") == 0 ||
39 strcmp(output, "off") == 0 ||
40 strcmp(output, "journal") == 0 ||
41 strcmp(output, "syslog") == 0 ||
42 strcmp(output, "system") == 0 ||
43 strcmp(output, "stderr") == 0 ||
44 strcmp(output, "stdout") == 0 ||
45 strcmp(output, "/dev/null") == 0
46 #if defined(HAVE_ETW)
47 || strcmp(output, "etw") == 0
48 #endif
49 #if defined(HAVE_WEL)
50 || strcmp(output, "wel") == 0
51 #endif
52 ;
53 }
54
55 static char *transform_log_path_setting(const char *setting, bool for_display) {
56 if(!setting)
57 return strdupz("");
58
59 CLEAN_CHAR_P *copy = strdupz(setting);
60 char *output = strrchr(copy, '@');
61 size_t prefix_len = 0;
62
63 if(output) {
64 prefix_len = (size_t)(output - copy);
65 *output = '\0';
66 output++;
67 }
68 else
69 output = copy;
70
71 if(log_setting_output_is_special(output))
72 return strdupz(setting);
73
74 CLEAN_CHAR_P *translated = for_display
75 ? os_translate_msys_to_windows_path(output)
76 : os_translate_windows_to_msys_path(output);
77 // os_translate_*_path() returns an allocated non-NULL string, using ""
78 // for empty input, so no NULL fallback is needed here.
79 size_t translated_len = strnlen(translated, CONFIG_MAX_VALUE + 1);
80
81 if(output == copy)
82 return strdupz(translated);
83
84 BUFFER *wb = buffer_create(prefix_len + translated_len + 2, NULL);
85 buffer_sprintf(wb, "%s@%s", copy, translated);
86 char *result = strdupz(buffer_tostring(wb));
87 buffer_free(wb);
88
89 return result;
90 }
91
92 static bool windows_native_path_p(const char *value) {
93 if(!value || !*value)
94 return false;
95
96 return (isalpha((unsigned char)value[0]) && value[1] == ':') ||
97 ((value[0] == '\\' && value[1] == '\\') || (value[0] == '/' && value[1] == '/'));
98 }
99
100 static bool windows_path_list_needs_reformat_p(const char *value) {
101 if(!value || !*value)
102 return false;
103
104 return strchr(value, ';') || strchr(value, '\\') || windows_native_path_p(value);
105 }
106
107 static STRING *reformat_path_list(STRING *value) {
108 const char *src = string2str(value);
109 size_t src_len = string_strlen(value);
110 if(!windows_path_list_needs_reformat_p(src))
111 return value;
112
113 BUFFER *wb = buffer_create(src_len + 1, NULL);
114 bool first = true;
115 const char *segment_start = src;
116
117 while(true) {
118 const char *separator = strchr(segment_start, ';');
119 size_t segment_len = separator
120 ? (size_t)(separator - segment_start)
121 : src_len - (size_t)(segment_start - src);
122
123 CLEAN_CHAR_P *segment = strndupz(segment_start, segment_len);
124 char *trimmed = trim(segment);
125 CLEAN_CHAR_P *converted = os_translate_windows_to_msys_path(trimmed);
126
127 if(!first)
128 buffer_strcat(wb, ":");
129 buffer_strcat(wb, converted);
130 first = false;
131
132 if(!separator)
133 break;
134
135 segment_start = separator + 1;
136 }
137
138 if(string_strcmp(value, buffer_tostring(wb)) != 0) {
139 string_freez(value);
140 value = string_strdupz(buffer_tostring(wb));
141 }
142
143 buffer_free(wb);
144 return value;
145 }
146
147 static STRING *reformat_quoted_path_list(STRING *value) {
148 CLEAN_CHAR_P *copy = strdupz(string2str(value));
149 // 256 slots is far more than any realistic plugins list; entries beyond this are silently ignored.
150 char *words[256] = { 0 };
151 size_t num_words = quoted_strings_splitter_config(copy, words, _countof(words));
152 if(!num_words)
153 return value;
154
155 BUFFER *wb = buffer_create(string_strlen(value) + 1, NULL);
156 for(size_t i = 0; i < num_words; i++) {
157 CLEAN_CHAR_P *converted = os_translate_windows_to_msys_path(words[i]);
158 if(i)
159 buffer_strcat(wb, " ");
160 buffer_sprintf(wb, "\"%s\"", converted);
161 }
162
163 if(string_strcmp(value, buffer_tostring(wb)) != 0) {
164 string_freez(value);
165 value = string_strdupz(buffer_tostring(wb));
166 }
167
168 buffer_free(wb);
169 return value;
170 }
171
172 static STRING *reformat_log_path_setting(STRING *value) {
173 CLEAN_CHAR_P *converted = transform_log_path_setting(string2str(value), false);
174 if(string_strcmp(value, converted) != 0) {
175 string_freez(value);
176 return string_strdupz(converted);
177 }
178
179 return value;
180 }
181 #else
182 static STRING *reformat_path_list(STRING *value) {
183 return value;
184 }
185
186 static STRING *reformat_quoted_path_list(STRING *value) {
187 return value;
188 }
189
190 static STRING *reformat_log_path_setting(STRING *value) {
191 return value;
192 }
193 #endif
194
195 const char *inicfg_get_filename(struct config *root, const char *section, const char *name, const char *default_value) {
196 struct config_option *opt = inicfg_get_raw_value(root, section, name, default_value, CONFIG_VALUE_TYPE_FILENAME, reformat_path);
197 if(!opt)
198 return NULL;
199
200 return string2str(opt->value);
201 }
202
203 const char *inicfg_get_path(struct config *root, const char *section, const char *name, const char *default_value) {
204 struct config_option *opt = inicfg_get_raw_value(root, section, name, default_value, CONFIG_VALUE_TYPE_PATH, reformat_path);
205 if(!opt)
206 return NULL;
207
208 return string2str(opt->value);
209 }
210
211 const char *inicfg_get_path_list(struct config *root, const char *section, const char *name, const char *default_value) {
212 struct config_option *opt = inicfg_get_raw_value(root, section, name, default_value, CONFIG_VALUE_TYPE_TEXT, reformat_path_list);
213 if(!opt)
214 return NULL;
215
216 return string2str(opt->value);
217 }
218
219 const char *inicfg_get_quoted_path_list(struct config *root, const char *section, const char *name, const char *default_value) {
220 struct config_option *opt = inicfg_get_raw_value(root, section, name, default_value, CONFIG_VALUE_TYPE_TEXT, reformat_quoted_path_list);
221 if(!opt)
222 return NULL;
223
224 return string2str(opt->value);
225 }
226
227 const char *inicfg_get_log_path_setting(struct config *root, const char *section, const char *name, const char *default_value) {
228 struct config_option *opt = inicfg_get_raw_value(root, section, name, default_value, CONFIG_VALUE_TYPE_TEXT, reformat_log_path_setting);
229 if(!opt)
230 return NULL;
231
232 return string2str(opt->value);
233 }
234
235 const char *inicfg_log_path_setting_for_display(const char *value, char *dst, size_t dst_size) {
236 #if defined(OS_WINDOWS)
237 if(!dst || dst_size == 0)
238 return value ? value : "";
239
240 CLEAN_CHAR_P *converted = transform_log_path_setting(value, true);
241 snprintfz(dst, dst_size, "%s", converted);
242 return dst;
243 #else
244 (void)dst;
245 (void)dst_size;
246 return value ? value : "";
247 #endif
248 }
249
250 bool inicfg_test_boolean_value(const char *s) {
251 if(!strcasecmp(s, "yes") || !strcasecmp(s, "true") || !strcasecmp(s, "on")
252 || !strcasecmp(s, "auto") || !strcasecmp(s, "on demand"))
253 return true;
254
255 return false;
256 }
257
258 int inicfg_get_boolean_by_section(struct config_section *sect, const char *name, int value) {
259 struct config_option *opt = inicfg_get_raw_value_of_option_in_section(
260 sect, name, (!value) ? "no" : "yes", CONFIG_VALUE_TYPE_BOOLEAN, NULL);
261 if(!opt) return value;
262
263 return inicfg_test_boolean_value(string2str(opt->value));
264 }
265
266 int inicfg_get_boolean(struct config *root, const char *section, const char *name, int value) {
267 const char *s;
268 if(value) s = "yes";
269 else s = "no";
270
271 struct config_option *opt = inicfg_get_raw_value(root, section, name, s, CONFIG_VALUE_TYPE_BOOLEAN, NULL);
272 if(!opt) return value;
273 s = string2str(opt->value);
274
275 return inicfg_test_boolean_value(s);
276 }
277
278 int inicfg_get_boolean_ondemand(struct config *root, const char *section, const char *name, int value) {
279 const char *s;
280
281 if(value == CONFIG_BOOLEAN_AUTO)
282 s = "auto";
283
284 else if(value == CONFIG_BOOLEAN_NO)
285 s = "no";
286
287 else
288 s = "yes";
289
290 struct config_option *opt = inicfg_get_raw_value(root, section, name, s, CONFIG_VALUE_TYPE_BOOLEAN_ONDEMAND, NULL);
291 if(!opt) return value;
292
293 s = string2str(opt->value);
294 if(!strcmp(s, "yes") || !strcmp(s, "true") || !strcmp(s, "on"))
295 return CONFIG_BOOLEAN_YES;
296 else if(!strcmp(s, "no") || !strcmp(s, "false") || !strcmp(s, "off"))
297 return CONFIG_BOOLEAN_NO;
298 else if(!strcmp(s, "auto") || !strcmp(s, "on demand"))
299 return CONFIG_BOOLEAN_AUTO;
300
301 return value;
302 }
303
304 int inicfg_set_boolean(struct config *root, const char *section, const char *name, int value) {
305 const char *s;
306 if(value) s = "yes";
307 else s = "no";
308
309 inicfg_set_raw_value(root, section, name, s, CONFIG_VALUE_TYPE_BOOLEAN);
310
311 return value;
312 }
313
314 static STRING *reformat_duration_seconds(STRING *value) {
315 int result = 0;
316 if(!duration_parse_seconds(string2str(value), &result))
317 return value;
318
319 char buf[128];
320 if(duration_snprintf_time_t(buf, sizeof(buf), result) > 0 && string_strcmp(value, buf) != 0) {
321 string_freez(value);
322 return string_strdupz(buf);
323 }
324
325 return value;
326 }
327
328 time_t inicfg_get_duration_seconds(struct config *root, const char *section, const char *name, time_t default_value) {
329 char default_str[128];
330 duration_snprintf_time_t(default_str, sizeof(default_str), default_value);
331
332 struct config_option *opt = inicfg_get_raw_value(
333 root, section, name, default_str, CONFIG_VALUE_TYPE_DURATION_IN_SECS, reformat_duration_seconds);
334 if(!opt)
335 return default_value;
336
337 const char *s = string2str(opt->value);
338
339 int result = 0;
340 if(!duration_parse_seconds(s, &result)) {
341 inicfg_set_raw_value(root, section, name, default_str, CONFIG_VALUE_TYPE_DURATION_IN_SECS);
342 netdata_log_error("config option '[%s].%s = %s' is configured with an invalid duration", section, name, s);
343 return default_value;
344 }
345
346 return ABS(result);
347 }
348
349 time_t inicfg_set_duration_seconds(struct config *root, const char *section, const char *name, time_t value) {
350 char str[128];
351 duration_snprintf_time_t(str, sizeof(str), value);
352
353 inicfg_set_raw_value(root, section, name, str, CONFIG_VALUE_TYPE_DURATION_IN_SECS);
354 return value;
355 }
356
357 static STRING *reformat_duration_ms(STRING *value) {
358 int64_t result = 0;
359 if(!duration_parse_msec_t(string2str(value), &result))
360 return value;
361
362 char buf[128];
363 if(duration_snprintf_msec_t(buf, sizeof(buf), result) > 0 && string_strcmp(value, buf) != 0) {
364 string_freez(value);
365 return string_strdupz(buf);
366 }
367
368 return value;
369 }
370
371 msec_t inicfg_get_duration_ms(struct config *root, const char *section, const char *name, msec_t default_value) {
372 char default_str[128];
373 duration_snprintf_msec_t(default_str, sizeof(default_str), default_value);
374
375 struct config_option *opt = inicfg_get_raw_value(
376 root, section, name, default_str, CONFIG_VALUE_TYPE_DURATION_IN_MS, reformat_duration_ms);
377 if(!opt)
378 return default_value;
379
380 const char *s = string2str(opt->value);
381
382 smsec_t result = 0;
383 if(!duration_parse_msec_t(s, &result)) {
384 inicfg_set_raw_value(root, section, name, default_str, CONFIG_VALUE_TYPE_DURATION_IN_MS);
385 netdata_log_error("config option '[%s].%s = %s' is configured with an invalid duration", section, name, s);
386 return default_value;
387 }
388
389 return ABS(result);
390 }
391
392 msec_t inicfg_set_duration_ms(struct config *root, const char *section, const char *name, msec_t value) {
393 char str[128];
394 duration_snprintf_msec_t(str, sizeof(str), (smsec_t)value);
395
396 inicfg_set_raw_value(root, section, name, str, CONFIG_VALUE_TYPE_DURATION_IN_MS);
397 return value;
398 }
399
400 static STRING *reformat_duration_days_to_seconds(STRING *value) {
401 int64_t result = 0;
402 if(!duration_parse(string2str(value), &result, "d", "s"))
403 return value;
404
405 char buf[128];
406 if(duration_snprintf(buf, sizeof(buf), result, "s", false) > 0 && string_strcmp(value, buf) != 0) {
407 string_freez(value);
408 return string_strdupz(buf);
409 }
410
411 return value;
412 }
413
414 time_t inicfg_get_duration_days_to_seconds(struct config *root, const char *section, const char *name, unsigned default_value_seconds) {
415 char default_str[128];
416 duration_snprintf(default_str, sizeof(default_str), (int)default_value_seconds, "s", false);
417
418 struct config_option *opt = inicfg_get_raw_value(
419 root, section, name, default_str,
420 CONFIG_VALUE_TYPE_DURATION_IN_DAYS_TO_SECONDS,
421 reformat_duration_days_to_seconds);
422
423 if(!opt)
424 return default_value_seconds;
425
426 const char *s = string2str(opt->value);
427
428 int64_t result = 0;
429 if(!duration_parse(s, &result, "d", "s")) {
430 inicfg_set_raw_value(root, section, name, default_str, CONFIG_VALUE_TYPE_DURATION_IN_DAYS_TO_SECONDS);
431 netdata_log_error("config option '[%s].%s = %s' is configured with an invalid duration", section, name, s);
432 return default_value_seconds;
433 }
434
435 return (unsigned)ABS(result);
436 }
437
438 long long inicfg_get_number(struct config *root, const char *section, const char *name, long long value) {
439 char buffer[100];
440 sprintf(buffer, "%lld", value);
441
442 struct config_option *opt = inicfg_get_raw_value(root, section, name, buffer, CONFIG_VALUE_TYPE_INTEGER, NULL);
443 if(!opt) return value;
444
445 const char *s = string2str(opt->value);
446 return strtoll(s, NULL, 0);
447 }
448
449 long long inicfg_get_number_range(struct config *root, const char *section, const char *name, long long value, long long min, long long max) {
450 char buffer[100];
451 sprintf(buffer, "%lld", value);
452
453 struct config_option *opt = inicfg_get_raw_value(root, section, name, buffer, CONFIG_VALUE_TYPE_INTEGER, NULL);
454 if(!opt) return value;
455
456 const char *s = string2str(opt->value);
457 long long rc = strtoll(s, NULL, 0);
458 long long rc2 = FIT_IN_RANGE(rc, min, max);
459
460 if(rc != rc2) {
461 nd_log(NDLS_DAEMON, NDLP_ERR, "CONFIG: out of range [%s].%s = %lld. Acceptable values: %lld to %lld inclusive. Setting it to %lld",
462 section, name, rc, min, max, rc2);
463
464 rc = rc2;
465 inicfg_set_number(root, section, name, rc);
466 }
467
468 return rc;
469 }
470
471 NETDATA_DOUBLE inicfg_get_double(struct config *root, const char *section, const char *name, NETDATA_DOUBLE value) {
472 char buffer[100];
473 sprintf(buffer, "%0.5" NETDATA_DOUBLE_MODIFIER, value);
474
475 struct config_option *opt = inicfg_get_raw_value(root, section, name, buffer, CONFIG_VALUE_TYPE_DOUBLE, NULL);
476 if(!opt) return value;
477
478 const char *s = string2str(opt->value);
479 return str2ndd(s, NULL);
480 }
481
482 long long inicfg_set_number(struct config *root, const char *section, const char *name, long long value) {
483 char buffer[100];
484 sprintf(buffer, "%lld", value);
485
486 inicfg_set_raw_value(root, section, name, buffer, CONFIG_VALUE_TYPE_INTEGER);
487 return value;
488 }
489
490 NETDATA_DOUBLE inicfg_set_double(struct config *root, const char *section, const char *name, NETDATA_DOUBLE value) {
491 char buffer[100];
492 sprintf(buffer, "%0.5" NETDATA_DOUBLE_MODIFIER, value);
493
494 inicfg_set_raw_value(root, section, name, buffer, CONFIG_VALUE_TYPE_DOUBLE);
495 return value;
496 }
497
498 static STRING *reformat_size_bytes(STRING *value) {
499 uint64_t result = 0;
500 if(!size_parse_bytes(string2str(value), &result))
501 return value;
502
503 char buf[128];
504 if(size_snprintf_bytes(buf, sizeof(buf), result) > 0 && string_strcmp(value, buf) != 0) {
505 string_freez(value);
506 return string_strdupz(buf);
507 }
508
509 return value;
510 }
511
512 uint64_t inicfg_get_size_bytes(struct config *root, const char *section, const char *name, uint64_t default_value) {
513 char default_str[128];
514 size_snprintf_bytes(default_str, sizeof(default_str), default_value);
515
516 struct config_option *opt =
517 inicfg_get_raw_value(root, section, name, default_str, CONFIG_VALUE_TYPE_SIZE_IN_BYTES, reformat_size_bytes);
518 if(!opt)
519 return default_value;
520
521 const char *s = string2str(opt->value);
522 uint64_t result = 0;
523 if(!size_parse_bytes(s, &result)) {
524 inicfg_set_raw_value(root, section, name, default_str, CONFIG_VALUE_TYPE_SIZE_IN_BYTES);
525 netdata_log_error("config option '[%s].%s = %s' is configured with an invalid size", section, name, s);
526 return default_value;
527 }
528
529 return result;
530 }
531
532 uint64_t inicfg_set_size_bytes(struct config *root, const char *section, const char *name, uint64_t value) {
533 char str[128];
534 size_snprintf_bytes(str, sizeof(str), value);
535 inicfg_set_raw_value(root, section, name, str, CONFIG_VALUE_TYPE_SIZE_IN_BYTES);
536 return value;
537 }
538
539 static STRING *reformat_size_mb(STRING *value) {
540 uint64_t result = 0;
541 if(!size_parse_mb(string2str(value), &result))
542 return value;
543
544 char buf[128];
545 if(size_snprintf_mb(buf, sizeof(buf), result) > 0 && string_strcmp(value, buf) != 0) {
546 string_freez(value);
547 return string_strdupz(buf);
548 }
549
550 return value;
551 }
552
553 uint64_t inicfg_get_size_mb(struct config *root, const char *section, const char *name, uint64_t default_value) {
554 char default_str[128];
555 size_snprintf_mb(default_str, sizeof(default_str), default_value);
556
557 struct config_option *opt =
558 inicfg_get_raw_value(root, section, name, default_str, CONFIG_VALUE_TYPE_SIZE_IN_MB, reformat_size_mb);
559 if(!opt)
560 return default_value;
561
562 const char *s = string2str(opt->value);
563 uint64_t result = 0;
564 if(!size_parse_mb(s, &result)) {
565 inicfg_set_raw_value(root, section, name, default_str, CONFIG_VALUE_TYPE_SIZE_IN_MB);
566 netdata_log_error("config option '[%s].%s = %s' is configured with an invalid size", section, name, s);
567 return default_value;
568 }
569
570 return (unsigned)result;
571 }
572
573 uint64_t inicfg_set_size_mb(struct config *root, const char *section, const char *name, uint64_t value) {
574 char str[128];
575 size_snprintf_mb(str, sizeof(str), value);
576 inicfg_set_raw_value(root, section, name, str, CONFIG_VALUE_TYPE_SIZE_IN_MB);
577 return value;
578 }