]> arthur.barton.de Git - netdata.git/blob - src/appconfig.c
optimization: saved 84 bytes per dictionary, avl locking removed from tc plugin and...
[netdata.git] / src / appconfig.c
1 #ifdef HAVE_CONFIG_H
2 #include <config.h>
3 #endif
4 #include <pthread.h>
5 #include <stdlib.h>
6 #include <string.h>
7
8 #include "avl.h"
9 #include "common.h"
10 #include "appconfig.h"
11 #include "log.h"
12
13 #define CONFIG_FILE_LINE_MAX ((CONFIG_MAX_NAME + CONFIG_MAX_VALUE + 1024) * 2)
14
15 pthread_rwlock_t config_rwlock = PTHREAD_RWLOCK_INITIALIZER;
16
17 // ----------------------------------------------------------------------------
18 // definitions
19
20 #define CONFIG_VALUE_LOADED  0x01 // has been loaded from the config
21 #define CONFIG_VALUE_USED    0x02 // has been accessed from the program
22 #define CONFIG_VALUE_CHANGED 0x04 // has been changed from the loaded value
23 #define CONFIG_VALUE_CHECKED 0x08 // has been checked if the value is different from the default
24
25 struct config_value {
26         avl avl;                                // the index - this has to be first!
27
28         uint32_t hash;                  // a simple hash to speed up searching
29                                                         // we first compare hashes, and only if the hashes are equal we do string comparisons
30
31         char *name;
32         char *value;
33
34         uint8_t flags;
35
36         struct config_value *next;
37 };
38
39 struct config {
40         avl avl;
41
42         uint32_t hash;                  // a simple hash to speed up searching
43                                                         // we first compare hashes, and only if the hashes are equal we do string comparisons
44
45         char *name;
46
47         struct config_value *values;
48         avl_tree_lock values_index;
49
50         struct config *next;
51
52         pthread_rwlock_t rwlock;
53 } *config_root = NULL;
54
55
56 // ----------------------------------------------------------------------------
57 // config value
58
59 static int config_value_iterator(avl *a) { if(a) {}; return 0; }
60
61 static int config_value_compare(void* a, void* b) {
62         if(((struct config_value *)a)->hash < ((struct config_value *)b)->hash) return -1;
63         else if(((struct config_value *)a)->hash > ((struct config_value *)b)->hash) return 1;
64         else return strcmp(((struct config_value *)a)->name, ((struct config_value *)b)->name);
65 }
66
67 #define config_value_index_add(co, cv) avl_insert_lock(&((co)->values_index), (avl *)(cv))
68 #define config_value_index_del(co, cv) avl_remove_lock(&((co)->values_index), (avl *)(cv))
69
70 static struct config_value *config_value_index_find(struct config *co, const char *name, uint32_t hash) {
71         struct config_value *result = NULL, tmp;
72         tmp.hash = (hash)?hash:simple_hash(name);
73         tmp.name = (char *)name;
74
75         avl_search_lock(&(co->values_index), (avl *) &tmp, config_value_iterator, (avl **) &result);
76         return result;
77 }
78
79 // ----------------------------------------------------------------------------
80 // config
81
82 static int config_iterator(avl *a) { if(a) {}; return 0; }
83
84 static int config_compare(void* a, void* b) {
85         if(((struct config *)a)->hash < ((struct config *)b)->hash) return -1;
86         else if(((struct config *)a)->hash > ((struct config *)b)->hash) return 1;
87         else return strcmp(((struct config *)a)->name, ((struct config *)b)->name);
88 }
89
90 avl_tree_lock config_root_index = {
91                 { NULL, config_compare },
92                 AVL_LOCK_INITIALIZER
93 };
94
95 #define config_index_add(cfg) avl_insert_lock(&config_root_index, (avl *)(cfg))
96 #define config_index_del(cfg) avl_remove_lock(&config_root_index, (avl *)(cfg))
97
98 static struct config *config_index_find(const char *name, uint32_t hash) {
99         struct config *result = NULL, tmp;
100         tmp.hash = (hash)?hash:simple_hash(name);
101         tmp.name = (char *)name;
102
103         avl_search_lock(&config_root_index, (avl *) &tmp, config_iterator, (avl **) &result);
104         return result;
105 }
106
107 struct config_value *config_value_create(struct config *co, const char *name, const char *value)
108 {
109         debug(D_CONFIG, "Creating config entry for name '%s', value '%s', in section '%s'.", name, value, co->name);
110
111         struct config_value *cv = calloc(1, sizeof(struct config_value));
112         if(!cv) fatal("Cannot allocate config_value");
113
114         cv->name = strdup(name);
115         if(!cv->name) fatal("Cannot allocate config.name");
116         cv->hash = simple_hash(cv->name);
117
118         cv->value = strdup(value);
119         if(!cv->value) fatal("Cannot allocate config.value");
120
121         config_value_index_add(co, cv);
122
123         // no need for string termination, due to calloc()
124
125         pthread_rwlock_wrlock(&co->rwlock);
126
127         struct config_value *cv2 = co->values;
128         if(cv2) {
129                 while (cv2->next) cv2 = cv2->next;
130                 cv2->next = cv;
131         }
132         else co->values = cv;
133
134         pthread_rwlock_unlock(&co->rwlock);
135
136         return cv;
137 }
138
139 struct config *config_create(const char *section)
140 {
141         debug(D_CONFIG, "Creating section '%s'.", section);
142
143         struct config *co = calloc(1, sizeof(struct config));
144         if(!co) fatal("Cannot allocate config");
145
146         co->name = strdup(section);
147         if(!co->name) fatal("Cannot allocate config.name");
148         co->hash = simple_hash(co->name);
149
150         pthread_rwlock_init(&co->rwlock, NULL);
151         avl_init_lock(&co->values_index, config_value_compare);
152
153         config_index_add(co);
154
155         // no need for string termination, due to calloc()
156
157         pthread_rwlock_wrlock(&config_rwlock);
158
159         struct config *co2 = config_root;
160         if(co2) {
161                 while (co2->next) co2 = co2->next;
162                 co2->next = co;
163         }
164         else config_root = co;
165
166         pthread_rwlock_unlock(&config_rwlock);
167
168         return co;
169 }
170
171 struct config *config_find_section(const char *section)
172 {
173         return config_index_find(section, 0);
174 }
175
176 int load_config(char *filename, int overwrite_used)
177 {
178         int line = 0;
179         struct config *co = NULL;
180
181         char buffer[CONFIG_FILE_LINE_MAX + 1], *s;
182
183         if(!filename) filename = CONFIG_DIR "/" CONFIG_FILENAME;
184         FILE *fp = fopen(filename, "r");
185         if(!fp) {
186                 error("Cannot open file '%s'", filename);
187                 return 0;
188         }
189
190         while(fgets(buffer, CONFIG_FILE_LINE_MAX, fp) != NULL) {
191                 buffer[CONFIG_FILE_LINE_MAX] = '\0';
192                 line++;
193
194                 s = trim(buffer);
195                 if(!s) {
196                         debug(D_CONFIG, "Ignoring line %d, it is empty.", line);
197                         continue;
198                 }
199
200                 int len = (int) strlen(s);
201                 if(*s == '[' && s[len - 1] == ']') {
202                         // new section
203                         s[len - 1] = '\0';
204                         s++;
205
206                         co = config_find_section(s);
207                         if(!co) co = config_create(s);
208
209                         continue;
210                 }
211
212                 if(!co) {
213                         // line outside a section
214                         error("Ignoring line %d ('%s'), it is outside all sections.", line, s);
215                         continue;
216                 }
217
218                 char *name = s;
219                 char *value = strchr(s, '=');
220                 if(!value) {
221                         error("Ignoring line %d ('%s'), there is no = in it.", line, s);
222                         continue;
223                 }
224                 *value = '\0';
225                 value++;
226
227                 name = trim(name);
228                 value = trim(value);
229
230                 if(!name) {
231                         error("Ignoring line %d, name is empty.", line);
232                         continue;
233                 }
234                 if(!value) {
235                         debug(D_CONFIG, "Ignoring line %d, value is empty.", line);
236                         continue;
237                 }
238
239                 struct config_value *cv = config_value_index_find(co, name, 0);
240
241                 if(!cv) cv = config_value_create(co, name, value);
242                 else {
243                         if(((cv->flags & CONFIG_VALUE_USED) && overwrite_used) || !(cv->flags & CONFIG_VALUE_USED)) {
244                                 debug(D_CONFIG, "Overwriting '%s/%s'.", line, co->name, cv->name);
245                                 free(cv->value);
246                                 cv->value = strdup(value);
247                                 if(!cv->value) fatal("Cannot allocate config.value");
248                         }
249                         else
250                                 debug(D_CONFIG, "Ignoring line %d, '%s/%s' is already present and used.", line, co->name, cv->name);
251                 }
252                 cv->flags |= CONFIG_VALUE_LOADED;
253         }
254
255         fclose(fp);
256
257         return 1;
258 }
259
260 char *config_get(const char *section, const char *name, const char *default_value)
261 {
262         struct config_value *cv;
263
264         debug(D_CONFIG, "request to get config in section '%s', name '%s', default_value '%s'", section, name, default_value);
265
266         struct config *co = config_find_section(section);
267         if(!co) co = config_create(section);
268
269         cv = config_value_index_find(co, name, 0);
270         if(!cv) {
271                 cv = config_value_create(co, name, default_value);
272                 if(!cv) return NULL;
273         }
274         cv->flags |= CONFIG_VALUE_USED;
275
276         if((cv->flags & CONFIG_VALUE_LOADED) || (cv->flags & CONFIG_VALUE_CHANGED)) {
277                 // this is a loaded value from the config file
278                 // if it is different that the default, mark it
279                 if(!(cv->flags & CONFIG_VALUE_CHECKED)) {
280                         if(strcmp(cv->value, default_value) != 0) cv->flags |= CONFIG_VALUE_CHANGED;
281                         cv->flags |= CONFIG_VALUE_CHECKED;
282                 }
283         }
284
285         return(cv->value);
286 }
287
288 long long config_get_number(const char *section, const char *name, long long value)
289 {
290         char buffer[100], *s;
291         sprintf(buffer, "%lld", value);
292
293         s = config_get(section, name, buffer);
294         if(!s) return value;
295
296         return strtoll(s, NULL, 0);
297 }
298
299 int config_get_boolean(const char *section, const char *name, int value)
300 {
301         char *s;
302         if(value) s = "yes";
303         else s = "no";
304
305         s = config_get(section, name, s);
306         if(!s) return value;
307
308         if(!strcmp(s, "yes")) return 1;
309         else return 0;
310 }
311
312 int config_get_boolean_ondemand(const char *section, const char *name, int value)
313 {
314         char *s;
315
316         if(value == CONFIG_ONDEMAND_ONDEMAND)
317                 s = "on demand";
318
319         else if(value == CONFIG_ONDEMAND_NO)
320                 s = "no";
321
322         else
323                 s = "yes";
324
325         s = config_get(section, name, s);
326         if(!s) return value;
327
328         if(!strcmp(s, "yes"))
329                 return CONFIG_ONDEMAND_YES;
330         else if(!strcmp(s, "no"))
331                 return CONFIG_ONDEMAND_NO;
332         else if(!strcmp(s, "on demand"))
333                 return CONFIG_ONDEMAND_ONDEMAND;
334
335         return value;
336 }
337
338 const char *config_set_default(const char *section, const char *name, const char *value)
339 {
340         struct config_value *cv;
341
342         debug(D_CONFIG, "request to set config in section '%s', name '%s', value '%s'", section, name, value);
343
344         struct config *co = config_find_section(section);
345         if(!co) return config_set(section, name, value);
346
347         cv = config_value_index_find(co, name, 0);
348         if(!cv) return config_set(section, name, value);
349
350         cv->flags |= CONFIG_VALUE_USED;
351
352         if(cv->flags & CONFIG_VALUE_LOADED)
353                 return cv->value;
354
355         if(strcmp(cv->value, value) != 0) {
356                 cv->flags |= CONFIG_VALUE_CHANGED;
357
358                 free(cv->value);
359                 cv->value = strdup(value);
360                 if(!cv->value) fatal("Cannot allocate config.value");
361         }
362
363         return cv->value;
364 }
365
366 const char *config_set(const char *section, const char *name, const char *value)
367 {
368         struct config_value *cv;
369
370         debug(D_CONFIG, "request to set config in section '%s', name '%s', value '%s'", section, name, value);
371
372         struct config *co = config_find_section(section);
373         if(!co) co = config_create(section);
374
375         cv = config_value_index_find(co, name, 0);
376         if(!cv) cv = config_value_create(co, name, value);
377         cv->flags |= CONFIG_VALUE_USED;
378
379         if(strcmp(cv->value, value) != 0) {
380                 cv->flags |= CONFIG_VALUE_CHANGED;
381
382                 free(cv->value);
383                 cv->value = strdup(value);
384                 if(!cv->value) fatal("Cannot allocate config.value");
385         }
386
387         return value;
388 }
389
390 long long config_set_number(const char *section, const char *name, long long value)
391 {
392         char buffer[100];
393         sprintf(buffer, "%lld", value);
394
395         config_set(section, name, buffer);
396
397         return value;
398 }
399
400 int config_set_boolean(const char *section, const char *name, int value)
401 {
402         char *s;
403         if(value) s = "yes";
404         else s = "no";
405
406         config_set(section, name, s);
407
408         return value;
409 }
410
411 void generate_config(BUFFER *wb, int only_changed)
412 {
413         int i, pri;
414         struct config *co;
415         struct config_value *cv;
416
417         for(i = 0; i < 3 ;i++) {
418                 switch(i) {
419                         case 0:
420                                 buffer_strcat(wb,
421                                         "# NetData Configuration\n"
422                                         "# You can uncomment and change any of the options below.\n"
423                                         "# The value shown in the commented settings, is the default value.\n"
424                                         "\n# global netdata configuration\n");
425                                 break;
426
427                         case 1:
428                                 buffer_strcat(wb, "\n\n# per plugin configuration\n");
429                                 break;
430
431                         case 2:
432                                 buffer_strcat(wb, "\n\n# per chart configuration\n");
433                                 break;
434                 }
435
436                 pthread_rwlock_wrlock(&config_rwlock);
437                 for(co = config_root; co ; co = co->next) {
438                         if(strcmp(co->name, "global") == 0 || strcmp(co->name, "plugins") == 0 || strcmp(co->name, "registry") == 0) pri = 0;
439                         else if(strncmp(co->name, "plugin:", 7) == 0) pri = 1;
440                         else pri = 2;
441
442                         if(i == pri) {
443                                 int used = 0;
444                                 int changed = 0;
445                                 int count = 0;
446
447                                 pthread_rwlock_wrlock(&co->rwlock);
448
449                                 for(cv = co->values; cv ; cv = cv->next) {
450                                         used += (cv->flags & CONFIG_VALUE_USED)?1:0;
451                                         changed += (cv->flags & CONFIG_VALUE_CHANGED)?1:0;
452                                         count++;
453                                 }
454
455                                 pthread_rwlock_unlock(&co->rwlock);
456
457                                 if(!count) continue;
458                                 if(only_changed && !changed) continue;
459
460                                 if(!used) {
461                                         buffer_sprintf(wb, "\n# node '%s' is not used.", co->name);
462                                 }
463
464                                 buffer_sprintf(wb, "\n[%s]\n", co->name);
465
466                                 pthread_rwlock_wrlock(&co->rwlock);
467                                 for(cv = co->values; cv ; cv = cv->next) {
468
469                                         if(used && !(cv->flags & CONFIG_VALUE_USED)) {
470                                                 buffer_sprintf(wb, "\n\t# option '%s' is not used.\n", cv->name);
471                                         }
472                                         buffer_sprintf(wb, "\t%s%s = %s\n", ((!(cv->flags & CONFIG_VALUE_CHANGED)) && (cv->flags & CONFIG_VALUE_USED))?"# ":"", cv->name, cv->value);
473                                 }
474                                 pthread_rwlock_unlock(&co->rwlock);
475                         }
476                 }
477                 pthread_rwlock_unlock(&config_rwlock);
478         }
479 }
480