3 #define CONFIG_FILE_LINE_MAX ((CONFIG_MAX_NAME + CONFIG_MAX_VALUE + 1024) * 2)
5 pthread_mutex_t config_mutex = PTHREAD_MUTEX_INITIALIZER;
7 // ----------------------------------------------------------------------------
10 #define CONFIG_VALUE_LOADED 0x01 // has been loaded from the config
11 #define CONFIG_VALUE_USED 0x02 // has been accessed from the program
12 #define CONFIG_VALUE_CHANGED 0x04 // has been changed from the loaded value
13 #define CONFIG_VALUE_CHECKED 0x08 // has been checked if the value is different from the default
16 avl avl; // the index - this has to be first!
19 uint32_t hash; // a simple hash to speed up searching
20 // we first compare hashes, and only if the hashes are equal we do string comparisons
25 struct config_value *next; // config->mutex protects just this
31 uint32_t hash; // a simple hash to speed up searching
32 // we first compare hashes, and only if the hashes are equal we do string comparisons
36 struct config *next; // gloabl config_mutex protects just this
38 struct config_value *values;
39 avl_tree_lock values_index;
41 pthread_mutex_t mutex; // this locks only the writers, to ensure atomic updates
42 // readers are protected using the rwlock in avl_tree_lock
43 } *config_root = NULL;
46 // ----------------------------------------------------------------------------
49 static inline void config_global_write_lock(void) {
50 pthread_mutex_lock(&config_mutex);
53 static inline void config_global_unlock(void) {
54 pthread_mutex_unlock(&config_mutex);
57 static inline void config_section_write_lock(struct config *co) {
58 pthread_mutex_lock(&co->mutex);
61 static inline void config_section_unlock(struct config *co) {
62 pthread_mutex_unlock(&co->mutex);
66 // ----------------------------------------------------------------------------
67 // config name-value index
69 static int config_value_compare(void* a, void* b) {
70 if(((struct config_value *)a)->hash < ((struct config_value *)b)->hash) return -1;
71 else if(((struct config_value *)a)->hash > ((struct config_value *)b)->hash) return 1;
72 else return strcmp(((struct config_value *)a)->name, ((struct config_value *)b)->name);
75 #define config_value_index_add(co, cv) avl_insert_lock(&((co)->values_index), (avl *)(cv))
76 #define config_value_index_del(co, cv) avl_remove_lock(&((co)->values_index), (avl *)(cv))
78 static struct config_value *config_value_index_find(struct config *co, const char *name, uint32_t hash) {
79 struct config_value tmp;
80 tmp.hash = (hash)?hash:simple_hash(name);
81 tmp.name = (char *)name;
83 return (struct config_value *)avl_search_lock(&(co->values_index), (avl *) &tmp);
87 // ----------------------------------------------------------------------------
88 // config sections index
90 static int config_compare(void* a, void* b) {
91 if(((struct config *)a)->hash < ((struct config *)b)->hash) return -1;
92 else if(((struct config *)a)->hash > ((struct config *)b)->hash) return 1;
93 else return strcmp(((struct config *)a)->name, ((struct config *)b)->name);
96 avl_tree_lock config_root_index = {
97 { NULL, config_compare },
101 #define config_index_add(cfg) avl_insert_lock(&config_root_index, (avl *)(cfg))
102 #define config_index_del(cfg) avl_remove_lock(&config_root_index, (avl *)(cfg))
104 static struct config *config_index_find(const char *name, uint32_t hash) {
106 tmp.hash = (hash)?hash:simple_hash(name);
107 tmp.name = (char *)name;
109 return (struct config *)avl_search_lock(&config_root_index, (avl *) &tmp);
113 // ----------------------------------------------------------------------------
114 // config section methods
116 static inline struct config *config_section_find(const char *section) {
117 return config_index_find(section, 0);
120 static inline struct config *config_section_create(const char *section)
122 debug(D_CONFIG, "Creating section '%s'.", section);
124 struct config *co = callocz(1, sizeof(struct config));
125 co->name = strdupz(section);
126 co->hash = simple_hash(co->name);
128 avl_init_lock(&co->values_index, config_value_compare);
130 config_index_add(co);
132 config_global_write_lock();
133 struct config *co2 = config_root;
135 while (co2->next) co2 = co2->next;
138 else config_root = co;
139 config_global_unlock();
145 // ----------------------------------------------------------------------------
146 // config name-value methods
148 static inline struct config_value *config_value_create(struct config *co, const char *name, const char *value)
150 debug(D_CONFIG, "Creating config entry for name '%s', value '%s', in section '%s'.", name, value, co->name);
152 struct config_value *cv = callocz(1, sizeof(struct config_value));
153 cv->name = strdupz(name);
154 cv->hash = simple_hash(cv->name);
155 cv->value = strdupz(value);
157 config_value_index_add(co, cv);
159 config_section_write_lock(co);
160 struct config_value *cv2 = co->values;
162 while (cv2->next) cv2 = cv2->next;
165 else co->values = cv;
166 config_section_unlock(co);
171 int config_exists(const char *section, const char *name) {
172 struct config_value *cv;
174 debug(D_CONFIG, "request to get config in section '%s', name '%s'", section, name);
176 struct config *co = config_section_find(section);
179 cv = config_value_index_find(co, name, 0);
185 int config_rename(const char *section, const char *old, const char *new) {
186 struct config_value *cv, *cv2;
188 debug(D_CONFIG, "request to rename config in section '%s', old name '%s', new name '%s'", section, old, new);
190 struct config *co = config_section_find(section);
193 config_section_write_lock(co);
195 cv = config_value_index_find(co, old, 0);
196 if(!cv) goto cleanup;
198 cv2 = config_value_index_find(co, new, 0);
199 if(cv2) goto cleanup;
201 config_value_index_del(co, cv);
204 cv->name = strdupz(new);
206 cv->hash = simple_hash(cv->name);
208 config_value_index_add(co, cv);
209 config_section_unlock(co);
214 config_section_unlock(co);
218 char *config_get(const char *section, const char *name, const char *default_value)
220 struct config_value *cv;
222 debug(D_CONFIG, "request to get config in section '%s', name '%s', default_value '%s'", section, name, default_value);
224 struct config *co = config_section_find(section);
225 if(!co) co = config_section_create(section);
227 cv = config_value_index_find(co, name, 0);
229 cv = config_value_create(co, name, default_value);
232 cv->flags |= CONFIG_VALUE_USED;
234 if((cv->flags & CONFIG_VALUE_LOADED) || (cv->flags & CONFIG_VALUE_CHANGED)) {
235 // this is a loaded value from the config file
236 // if it is different that the default, mark it
237 if(!(cv->flags & CONFIG_VALUE_CHECKED)) {
238 if(strcmp(cv->value, default_value) != 0) cv->flags |= CONFIG_VALUE_CHANGED;
239 cv->flags |= CONFIG_VALUE_CHECKED;
246 long long config_get_number(const char *section, const char *name, long long value)
248 char buffer[100], *s;
249 sprintf(buffer, "%lld", value);
251 s = config_get(section, name, buffer);
254 return strtoll(s, NULL, 0);
257 int config_get_boolean(const char *section, const char *name, int value)
263 s = config_get(section, name, s);
266 if(!strcmp(s, "yes") || !strcmp(s, "auto") || !strcmp(s, "on demand")) return 1;
270 int config_get_boolean_ondemand(const char *section, const char *name, int value)
274 if(value == CONFIG_ONDEMAND_ONDEMAND)
277 else if(value == CONFIG_ONDEMAND_NO)
283 s = config_get(section, name, s);
286 if(!strcmp(s, "yes"))
287 return CONFIG_ONDEMAND_YES;
288 else if(!strcmp(s, "no"))
289 return CONFIG_ONDEMAND_NO;
290 else if(!strcmp(s, "auto") || !strcmp(s, "on demand"))
291 return CONFIG_ONDEMAND_ONDEMAND;
296 const char *config_set_default(const char *section, const char *name, const char *value)
298 struct config_value *cv;
300 debug(D_CONFIG, "request to set config in section '%s', name '%s', value '%s'", section, name, value);
302 struct config *co = config_section_find(section);
303 if(!co) return config_set(section, name, value);
305 cv = config_value_index_find(co, name, 0);
306 if(!cv) return config_set(section, name, value);
308 cv->flags |= CONFIG_VALUE_USED;
310 if(cv->flags & CONFIG_VALUE_LOADED)
313 if(strcmp(cv->value, value) != 0) {
314 cv->flags |= CONFIG_VALUE_CHANGED;
317 cv->value = strdupz(value);
323 const char *config_set(const char *section, const char *name, const char *value)
325 struct config_value *cv;
327 debug(D_CONFIG, "request to set config in section '%s', name '%s', value '%s'", section, name, value);
329 struct config *co = config_section_find(section);
330 if(!co) co = config_section_create(section);
332 cv = config_value_index_find(co, name, 0);
333 if(!cv) cv = config_value_create(co, name, value);
334 cv->flags |= CONFIG_VALUE_USED;
336 if(strcmp(cv->value, value) != 0) {
337 cv->flags |= CONFIG_VALUE_CHANGED;
340 cv->value = strdupz(value);
346 long long config_set_number(const char *section, const char *name, long long value)
349 sprintf(buffer, "%lld", value);
351 config_set(section, name, buffer);
356 int config_set_boolean(const char *section, const char *name, int value)
362 config_set(section, name, s);
368 // ----------------------------------------------------------------------------
371 int load_config(char *filename, int overwrite_used)
374 struct config *co = NULL;
376 char buffer[CONFIG_FILE_LINE_MAX + 1], *s;
378 if(!filename) filename = CONFIG_DIR "/" CONFIG_FILENAME;
380 debug(D_CONFIG, "Opening config file '%s'", filename);
382 FILE *fp = fopen(filename, "r");
384 error("Cannot open file '%s'", filename);
388 while(fgets(buffer, CONFIG_FILE_LINE_MAX, fp) != NULL) {
389 buffer[CONFIG_FILE_LINE_MAX] = '\0';
394 debug(D_CONFIG, "Ignoring line %d, it is empty.", line);
398 int len = (int) strlen(s);
399 if(*s == '[' && s[len - 1] == ']') {
404 co = config_section_find(s);
405 if(!co) co = config_section_create(s);
411 // line outside a section
412 error("Ignoring line %d ('%s'), it is outside all sections.", line, s);
417 char *value = strchr(s, '=');
419 error("Ignoring line %d ('%s'), there is no = in it.", line, s);
429 error("Ignoring line %d, name is empty.", line);
433 debug(D_CONFIG, "Ignoring line %d, value is empty.", line);
437 struct config_value *cv = config_value_index_find(co, name, 0);
439 if(!cv) cv = config_value_create(co, name, value);
441 if(((cv->flags & CONFIG_VALUE_USED) && overwrite_used) || !(cv->flags & CONFIG_VALUE_USED)) {
442 debug(D_CONFIG, "Line %d, overwriting '%s/%s'.", line, co->name, cv->name);
444 cv->value = strdupz(value);
447 debug(D_CONFIG, "Ignoring line %d, '%s/%s' is already present and used.", line, co->name, cv->name);
449 cv->flags |= CONFIG_VALUE_LOADED;
457 void generate_config(BUFFER *wb, int only_changed)
461 struct config_value *cv;
463 for(i = 0; i < 3 ;i++) {
467 "# netdata configuration\n"
469 "# You can download the latest version of this file, using:\n"
470 "# wget -O /etc/netdata/netdata.conf http://localhost:19999/netdata.conf\n"
472 "# curl -o /etc/netdata/netdata.conf http://localhost:19999/netdata.conf\n"
474 "# You can uncomment and change any of the options below.\n"
475 "# The value shown in the commented settings, is the default value.\n"
477 "\n# global netdata configuration\n");
481 buffer_strcat(wb, "\n\n# per plugin configuration\n");
485 buffer_strcat(wb, "\n\n# per chart configuration\n");
489 config_global_write_lock();
490 for(co = config_root; co ; co = co->next) {
491 if(!strcmp(co->name, "global") ||
492 !strcmp(co->name, "plugins") ||
493 !strcmp(co->name, "registry") ||
494 !strcmp(co->name, "health"))
496 else if(!strncmp(co->name, "plugin:", 7)) pri = 1;
504 config_section_write_lock(co);
505 for(cv = co->values; cv ; cv = cv->next) {
506 used += (cv->flags & CONFIG_VALUE_USED)?1:0;
507 changed += (cv->flags & CONFIG_VALUE_CHANGED)?1:0;
510 config_section_unlock(co);
513 if(only_changed && !changed) continue;
516 buffer_sprintf(wb, "\n# node '%s' is not used.", co->name);
519 buffer_sprintf(wb, "\n[%s]\n", co->name);
521 config_section_write_lock(co);
522 for(cv = co->values; cv ; cv = cv->next) {
524 if(used && !(cv->flags & CONFIG_VALUE_USED)) {
525 buffer_sprintf(wb, "\n\t# option '%s' is not used.\n", cv->name);
527 buffer_sprintf(wb, "\t%s%s = %s\n", ((!(cv->flags & CONFIG_VALUE_CHANGED)) && (cv->flags & CONFIG_VALUE_USED))?"# ":"", cv->name, cv->value);
529 config_section_unlock(co);
532 config_global_unlock();