]> arthur.barton.de Git - netdata.git/blob - src/appconfig.c
ab-debian 0.20170311.01-0ab1, upstream v1.5.0-573-g0fba967b
[netdata.git] / src / appconfig.c
1 #include "common.h"
2
3 #define CONFIG_FILE_LINE_MAX ((CONFIG_MAX_NAME + CONFIG_MAX_VALUE + 1024) * 2)
4
5 // ----------------------------------------------------------------------------
6 // definitions
7
8 #define CONFIG_VALUE_LOADED  0x01 // has been loaded from the config
9 #define CONFIG_VALUE_USED    0x02 // has been accessed from the program
10 #define CONFIG_VALUE_CHANGED 0x04 // has been changed from the loaded value
11 #define CONFIG_VALUE_CHECKED 0x08 // has been checked if the value is different from the default
12
13 struct config_option {
14     avl avl;                // the index - this has to be first!
15
16     uint8_t flags;
17     uint32_t hash;          // a simple hash to speed up searching
18                             // we first compare hashes, and only if the hashes are equal we do string comparisons
19
20     char *name;
21     char *value;
22
23     struct config_option *next; // config->mutex protects just this
24 };
25
26 struct section {
27     avl avl;
28
29     uint32_t hash;          // a simple hash to speed up searching
30                             // we first compare hashes, and only if the hashes are equal we do string comparisons
31
32     char *name;
33
34     struct section *next;    // gloabl config_mutex protects just this
35
36     struct config_option *values;
37     avl_tree_lock values_index;
38
39     pthread_mutex_t mutex;  // this locks only the writers, to ensure atomic updates
40                             // readers are protected using the rwlock in avl_tree_lock
41 };
42
43 static int appconfig_section_compare(void *a, void *b);
44
45 struct config netdata_config = {
46         .sections = NULL,
47         .mutex = PTHREAD_MUTEX_INITIALIZER,
48         .index = {
49             { NULL, appconfig_section_compare },
50             AVL_LOCK_INITIALIZER
51         }
52 };
53
54 struct config stream_config = {
55         .sections = NULL,
56         .mutex = PTHREAD_MUTEX_INITIALIZER,
57         .index = {
58                 { NULL, appconfig_section_compare },
59                 AVL_LOCK_INITIALIZER
60         }
61 };
62
63 // ----------------------------------------------------------------------------
64 // locking
65
66 static inline void appconfig_wrlock(struct config *root) {
67     pthread_mutex_lock(&root->mutex);
68 }
69
70 static inline void appconfig_unlock(struct config *root) {
71     pthread_mutex_unlock(&root->mutex);
72 }
73
74 static inline void config_section_wrlock(struct section *co) {
75     pthread_mutex_lock(&co->mutex);
76 }
77
78 static inline void config_section_unlock(struct section *co) {
79     pthread_mutex_unlock(&co->mutex);
80 }
81
82
83 // ----------------------------------------------------------------------------
84 // config name-value index
85
86 static int appconfig_option_compare(void *a, void *b) {
87     if(((struct config_option *)a)->hash < ((struct config_option *)b)->hash) return -1;
88     else if(((struct config_option *)a)->hash > ((struct config_option *)b)->hash) return 1;
89     else return strcmp(((struct config_option *)a)->name, ((struct config_option *)b)->name);
90 }
91
92 #define appconfig_option_index_add(co, cv) (struct config_option *)avl_insert_lock(&((co)->values_index), (avl *)(cv))
93 #define appconfig_option_index_del(co, cv) (struct config_option *)avl_remove_lock(&((co)->values_index), (avl *)(cv))
94
95 static struct config_option *appconfig_option_index_find(struct section *co, const char *name, uint32_t hash) {
96     struct config_option tmp;
97     tmp.hash = (hash)?hash:simple_hash(name);
98     tmp.name = (char *)name;
99
100     return (struct config_option *)avl_search_lock(&(co->values_index), (avl *) &tmp);
101 }
102
103
104 // ----------------------------------------------------------------------------
105 // config sections index
106
107 static int appconfig_section_compare(void *a, void *b) {
108     if(((struct section *)a)->hash < ((struct section *)b)->hash) return -1;
109     else if(((struct section *)a)->hash > ((struct section *)b)->hash) return 1;
110     else return strcmp(((struct section *)a)->name, ((struct section *)b)->name);
111 }
112
113 #define appconfig_index_add(root, cfg) (struct section *)avl_insert_lock(&root->index, (avl *)(cfg))
114 #define appconfig_index_del(root, cfg) (struct section *)avl_remove_lock(&root->index, (avl *)(cfg))
115
116 static struct section *appconfig_index_find(struct config *root, const char *name, uint32_t hash) {
117     struct section tmp;
118     tmp.hash = (hash)?hash:simple_hash(name);
119     tmp.name = (char *)name;
120
121     return (struct section *)avl_search_lock(&root->index, (avl *) &tmp);
122 }
123
124
125 // ----------------------------------------------------------------------------
126 // config section methods
127
128 static inline struct section *appconfig_section_find(struct config *root, const char *section) {
129     return appconfig_index_find(root, section, 0);
130 }
131
132 static inline struct section *appconfig_section_create(struct config *root, const char *section) {
133     debug(D_CONFIG, "Creating section '%s'.", section);
134
135     struct section *co = callocz(1, sizeof(struct section));
136     co->name = strdupz(section);
137     co->hash = simple_hash(co->name);
138
139     avl_init_lock(&co->values_index, appconfig_option_compare);
140
141     if(unlikely(appconfig_index_add(root, co) != co))
142         error("INTERNAL ERROR: indexing of section '%s', already exists.", co->name);
143
144     appconfig_wrlock(root);
145     struct section *co2 = root->sections;
146     if(co2) {
147         while (co2->next) co2 = co2->next;
148         co2->next = co;
149     }
150     else root->sections = co;
151     appconfig_unlock(root);
152
153     return co;
154 }
155
156
157 // ----------------------------------------------------------------------------
158 // config name-value methods
159
160 static inline struct config_option *appconfig_value_create(struct section *co, const char *name, const char *value) {
161     debug(D_CONFIG, "Creating config entry for name '%s', value '%s', in section '%s'.", name, value, co->name);
162
163     struct config_option *cv = callocz(1, sizeof(struct config_option));
164     cv->name = strdupz(name);
165     cv->hash = simple_hash(cv->name);
166     cv->value = strdupz(value);
167
168     struct config_option *found = appconfig_option_index_add(co, cv);
169     if(found != cv) {
170         error("indexing of config '%s' in section '%s': already exists - using the existing one.", cv->name, co->name);
171         freez(cv->value);
172         freez(cv->name);
173         freez(cv);
174         return found;
175     }
176
177     config_section_wrlock(co);
178     struct config_option *cv2 = co->values;
179     if(cv2) {
180         while (cv2->next) cv2 = cv2->next;
181         cv2->next = cv;
182     }
183     else co->values = cv;
184     config_section_unlock(co);
185
186     return cv;
187 }
188
189 int appconfig_exists(struct config *root, const char *section, const char *name) {
190     struct config_option *cv;
191
192     debug(D_CONFIG, "request to get config in section '%s', name '%s'", section, name);
193
194     struct section *co = appconfig_section_find(root, section);
195     if(!co) return 0;
196
197     cv = appconfig_option_index_find(co, name, 0);
198     if(!cv) return 0;
199
200     return 1;
201 }
202
203 int appconfig_move(struct config *root, const char *section_old, const char *name_old, const char *section_new, const char *name_new) {
204     struct config_option *cv_old, *cv_new;
205     int ret = -1;
206
207     debug(D_CONFIG, "request to rename config in section '%s', old name '%s', to section '%s', new name '%s'", section_old, name_old, section_new, name_new);
208
209     struct section *co_old = appconfig_section_find(root, section_old);
210     if(!co_old) return ret;
211
212     struct section *co_new = appconfig_section_find(root, section_new);
213     if(!co_new) co_new = appconfig_section_create(root, section_new);
214
215     config_section_wrlock(co_old);
216     config_section_wrlock(co_new);
217
218     cv_old = appconfig_option_index_find(co_old, name_old, 0);
219     if(!cv_old) goto cleanup;
220
221     cv_new = appconfig_option_index_find(co_new, name_new, 0);
222     if(cv_new) goto cleanup;
223
224     if(unlikely(appconfig_option_index_del(co_old, cv_old) != cv_old))
225         error("INTERNAL ERROR: deletion of config '%s' from section '%s', deleted tge wrong config entry.", cv_old->name, co_old->name);
226
227     if(co_old->values == cv_old) {
228         co_old->values = cv_old->next;
229     }
230     else {
231         struct config_option *t;
232         for(t = co_old->values; t && t->next != cv_old ;t = t->next) ;
233         if(!t || t->next != cv_old)
234             error("INTERNAL ERROR: cannot find variable '%s' in section '%s' of the config - but it should be there.", cv_old->name, co_old->name);
235         else
236             t->next = cv_old->next;
237     }
238
239     freez(cv_old->name);
240     cv_old->name = strdupz(name_new);
241     cv_old->hash = simple_hash(cv_old->name);
242
243     cv_new = cv_old;
244     cv_new->next = co_new->values;
245     co_new->values = cv_new;
246
247     if(unlikely(appconfig_option_index_add(co_new, cv_old) != cv_old))
248         error("INTERNAL ERROR: re-indexing of config '%s' in section '%s', already exists.", cv_old->name, co_new->name);
249
250     ret = 0;
251
252 cleanup:
253     config_section_unlock(co_new);
254     config_section_unlock(co_old);
255     return ret;
256 }
257
258 char *appconfig_get(struct config *root, const char *section, const char *name, const char *default_value)
259 {
260     struct config_option *cv;
261
262     debug(D_CONFIG, "request to get config in section '%s', name '%s', default_value '%s'", section, name, default_value);
263
264     struct section *co = appconfig_section_find(root, section);
265     if(!co) co = appconfig_section_create(root, section);
266
267     cv = appconfig_option_index_find(co, name, 0);
268     if(!cv) {
269         cv = appconfig_value_create(co, name, default_value);
270         if(!cv) return NULL;
271     }
272     cv->flags |= CONFIG_VALUE_USED;
273
274     if((cv->flags & CONFIG_VALUE_LOADED) || (cv->flags & CONFIG_VALUE_CHANGED)) {
275         // this is a loaded value from the config file
276         // if it is different that the default, mark it
277         if(!(cv->flags & CONFIG_VALUE_CHECKED)) {
278             if(strcmp(cv->value, default_value) != 0) cv->flags |= CONFIG_VALUE_CHANGED;
279             cv->flags |= CONFIG_VALUE_CHECKED;
280         }
281     }
282
283     return(cv->value);
284 }
285
286 long long appconfig_get_number(struct config *root, const char *section, const char *name, long long value)
287 {
288     char buffer[100], *s;
289     sprintf(buffer, "%lld", value);
290
291     s = appconfig_get(root, section, name, buffer);
292     if(!s) return value;
293
294     return strtoll(s, NULL, 0);
295 }
296
297 int appconfig_get_boolean(struct config *root, const char *section, const char *name, int value)
298 {
299     char *s;
300     if(value) s = "yes";
301     else s = "no";
302
303     s = appconfig_get(root, section, name, s);
304     if(!s) return value;
305
306     if(!strcmp(s, "yes") || !strcmp(s, "auto") || !strcmp(s, "on demand")) return 1;
307     return 0;
308 }
309
310 int appconfig_get_boolean_ondemand(struct config *root, const char *section, const char *name, int value)
311 {
312     char *s;
313
314     if(value == CONFIG_BOOLEAN_AUTO)
315         s = "auto";
316
317     else if(value == CONFIG_BOOLEAN_NO)
318         s = "no";
319
320     else
321         s = "yes";
322
323     s = appconfig_get(root, section, name, s);
324     if(!s) return value;
325
326     if(!strcmp(s, "yes"))
327         return CONFIG_BOOLEAN_YES;
328     else if(!strcmp(s, "no"))
329         return CONFIG_BOOLEAN_NO;
330     else if(!strcmp(s, "auto") || !strcmp(s, "on demand"))
331         return CONFIG_BOOLEAN_AUTO;
332
333     return value;
334 }
335
336 const char *appconfig_set_default(struct config *root, const char *section, const char *name, const char *value)
337 {
338     struct config_option *cv;
339
340     debug(D_CONFIG, "request to set config in section '%s', name '%s', value '%s'", section, name, value);
341
342     struct section *co = appconfig_section_find(root, section);
343     if(!co) return appconfig_set(root, section, name, value);
344
345     cv = appconfig_option_index_find(co, name, 0);
346     if(!cv) return appconfig_set(root, section, name, value);
347
348     cv->flags |= CONFIG_VALUE_USED;
349
350     if(cv->flags & CONFIG_VALUE_LOADED)
351         return cv->value;
352
353     if(strcmp(cv->value, value) != 0) {
354         cv->flags |= CONFIG_VALUE_CHANGED;
355
356         freez(cv->value);
357         cv->value = strdupz(value);
358     }
359
360     return cv->value;
361 }
362
363 const char *appconfig_set(struct config *root, const char *section, const char *name, const char *value)
364 {
365     struct config_option *cv;
366
367     debug(D_CONFIG, "request to set config in section '%s', name '%s', value '%s'", section, name, value);
368
369     struct section *co = appconfig_section_find(root, section);
370     if(!co) co = appconfig_section_create(root, section);
371
372     cv = appconfig_option_index_find(co, name, 0);
373     if(!cv) cv = appconfig_value_create(co, name, value);
374     cv->flags |= CONFIG_VALUE_USED;
375
376     if(strcmp(cv->value, value) != 0) {
377         cv->flags |= CONFIG_VALUE_CHANGED;
378
379         freez(cv->value);
380         cv->value = strdupz(value);
381     }
382
383     return value;
384 }
385
386 long long appconfig_set_number(struct config *root, const char *section, const char *name, long long value)
387 {
388     char buffer[100];
389     sprintf(buffer, "%lld", value);
390
391     appconfig_set(root, section, name, buffer);
392
393     return value;
394 }
395
396 int appconfig_set_boolean(struct config *root, const char *section, const char *name, int value)
397 {
398     char *s;
399     if(value) s = "yes";
400     else s = "no";
401
402     appconfig_set(root, section, name, s);
403
404     return value;
405 }
406
407
408 // ----------------------------------------------------------------------------
409 // config load/save
410
411 int appconfig_load(struct config *root, char *filename, int overwrite_used)
412 {
413     int line = 0;
414     struct section *co = NULL;
415
416     char buffer[CONFIG_FILE_LINE_MAX + 1], *s;
417
418     if(!filename) filename = CONFIG_DIR "/" CONFIG_FILENAME;
419
420     debug(D_CONFIG, "Opening config file '%s'", filename);
421
422     FILE *fp = fopen(filename, "r");
423     if(!fp) {
424         error("Cannot open file '%s'", filename);
425         return 0;
426     }
427
428     while(fgets(buffer, CONFIG_FILE_LINE_MAX, fp) != NULL) {
429         buffer[CONFIG_FILE_LINE_MAX] = '\0';
430         line++;
431
432         s = trim(buffer);
433         if(!s) {
434             debug(D_CONFIG, "Ignoring line %d, it is empty.", line);
435             continue;
436         }
437
438         int len = (int) strlen(s);
439         if(*s == '[' && s[len - 1] == ']') {
440             // new section
441             s[len - 1] = '\0';
442             s++;
443
444             co = appconfig_section_find(root, s);
445             if(!co) co = appconfig_section_create(root, s);
446
447             continue;
448         }
449
450         if(!co) {
451             // line outside a section
452             error("Ignoring line %d ('%s'), it is outside all sections.", line, s);
453             continue;
454         }
455
456         char *name = s;
457         char *value = strchr(s, '=');
458         if(!value) {
459             error("Ignoring line %d ('%s'), there is no = in it.", line, s);
460             continue;
461         }
462         *value = '\0';
463         value++;
464
465         name = trim(name);
466         value = trim(value);
467
468         if(!name) {
469             error("Ignoring line %d, name is empty.", line);
470             continue;
471         }
472         if(!value) {
473             debug(D_CONFIG, "Ignoring line %d, value is empty.", line);
474             continue;
475         }
476
477         struct config_option *cv = appconfig_option_index_find(co, name, 0);
478
479         if(!cv) cv = appconfig_value_create(co, name, value);
480         else {
481             if(((cv->flags & CONFIG_VALUE_USED) && overwrite_used) || !(cv->flags & CONFIG_VALUE_USED)) {
482                 debug(D_CONFIG, "Line %d, overwriting '%s/%s'.", line, co->name, cv->name);
483                 freez(cv->value);
484                 cv->value = strdupz(value);
485             }
486             else
487                 debug(D_CONFIG, "Ignoring line %d, '%s/%s' is already present and used.", line, co->name, cv->name);
488         }
489         cv->flags |= CONFIG_VALUE_LOADED;
490     }
491
492     fclose(fp);
493
494     return 1;
495 }
496
497 void appconfig_generate(struct config *root, BUFFER *wb, int only_changed)
498 {
499     int i, pri;
500     struct section *co;
501     struct config_option *cv;
502
503     for(i = 0; i < 3 ;i++) {
504         switch(i) {
505             case 0:
506                 buffer_strcat(wb,
507                     "# netdata configuration\n"
508                     "#\n"
509                     "# You can download the latest version of this file, using:\n"
510                     "#\n"
511                     "#  wget -O /etc/netdata/netdata.conf http://localhost:19999/netdata.conf\n"
512                     "# or\n"
513                     "#  curl -o /etc/netdata/netdata.conf http://localhost:19999/netdata.conf\n"
514                     "#\n"
515                     "# You can uncomment and change any of the options below.\n"
516                     "# The value shown in the commented settings, is the default value.\n"
517                     "#\n"
518                     "\n# global netdata configuration\n");
519                 break;
520
521             case 1:
522                 buffer_strcat(wb, "\n\n# per plugin configuration\n");
523                 break;
524
525             case 2:
526                 buffer_strcat(wb, "\n\n# per chart configuration\n");
527                 break;
528         }
529
530         appconfig_wrlock(root);
531         for(co = root->sections; co ; co = co->next) {
532             if(!strcmp(co->name, CONFIG_SECTION_GLOBAL)
533                || !strcmp(co->name, CONFIG_SECTION_WEB)
534                || !strcmp(co->name, CONFIG_SECTION_PLUGINS)
535                || !strcmp(co->name, CONFIG_SECTION_REGISTRY)
536                || !strcmp(co->name, CONFIG_SECTION_HEALTH)
537                || !strcmp(co->name, CONFIG_SECTION_BACKEND)
538                || !strcmp(co->name, CONFIG_SECTION_STREAM)
539                     )
540                 pri = 0;
541             else if(!strncmp(co->name, "plugin:", 7)) pri = 1;
542             else pri = 2;
543
544             if(i == pri) {
545                 int used = 0;
546                 int changed = 0;
547                 int count = 0;
548
549                 config_section_wrlock(co);
550                 for(cv = co->values; cv ; cv = cv->next) {
551                     used += (cv->flags & CONFIG_VALUE_USED)?1:0;
552                     changed += (cv->flags & CONFIG_VALUE_CHANGED)?1:0;
553                     count++;
554                 }
555                 config_section_unlock(co);
556
557                 if(!count) continue;
558                 if(only_changed && !changed) continue;
559
560                 if(!used) {
561                     buffer_sprintf(wb, "\n# node '%s' is not used.", co->name);
562                 }
563
564                 buffer_sprintf(wb, "\n[%s]\n", co->name);
565
566                 config_section_wrlock(co);
567                 for(cv = co->values; cv ; cv = cv->next) {
568
569                     if(used && !(cv->flags & CONFIG_VALUE_USED)) {
570                         buffer_sprintf(wb, "\n\t# option '%s' is not used.\n", cv->name);
571                     }
572                     buffer_sprintf(wb, "\t%s%s = %s\n", ((!(cv->flags & CONFIG_VALUE_CHANGED)) && (cv->flags & CONFIG_VALUE_USED))?"# ":"", cv->name, cv->value);
573                 }
574                 config_section_unlock(co);
575             }
576         }
577         appconfig_unlock(root);
578     }
579 }