123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405 |
- // SPDX-License-Identifier: GPL-3.0-or-later
- #include "dyncfg-internals.h"
- #include "dyncfg.h"
- struct dyncfg_globals dyncfg_globals = { 0 };
- void dyncfg_cleanup(DYNCFG *v) {
- buffer_free(v->payload);
- v->payload = NULL;
- string_freez(v->path);
- v->path = NULL;
- string_freez(v->source);
- v->source = NULL;
- string_freez(v->function);
- v->function = NULL;
- string_freez(v->template);
- v->template = NULL;
- }
- static void dyncfg_normalize(DYNCFG *df) {
- usec_t now_ut = now_realtime_usec();
- if(!df->created_ut)
- df->created_ut = now_ut;
- if(!df->modified_ut)
- df->modified_ut = now_ut;
- }
- static void dyncfg_delete_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) {
- DYNCFG *df = value;
- dyncfg_cleanup(df);
- }
- static void dyncfg_insert_cb(const DICTIONARY_ITEM *item, void *value, void *data __maybe_unused) {
- DYNCFG *df = value;
- dyncfg_normalize(df);
- const char *id = dictionary_acquired_item_name(item);
- char buf[strlen(id) + 20];
- snprintfz(buf, sizeof(buf), PLUGINSD_FUNCTION_CONFIG " %s", id);
- df->function = string_strdupz(buf);
- if(df->type == DYNCFG_TYPE_JOB && !df->template) {
- const char *last_colon = strrchr(id, ':');
- if(last_colon)
- df->template = string_strndupz(id, last_colon - id);
- else
- nd_log(NDLS_DAEMON, NDLP_WARNING,
- "DYNCFG: id '%s' is a job, but does not contain a colon to find the template", id);
- }
- }
- static void dyncfg_react_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) {
- DYNCFG *df = value; (void)df;
- ;
- }
- static bool dyncfg_conflict_cb(const DICTIONARY_ITEM *item __maybe_unused, void *old_value, void *new_value, void *data __maybe_unused) {
- DYNCFG *v = old_value;
- DYNCFG *nv = new_value;
- size_t changes = 0;
- dyncfg_normalize(nv);
- if(v->host != nv->host) {
- SWAP(v->host, nv->host);
- changes++;
- }
- if(v->path != nv->path) {
- SWAP(v->path, nv->path);
- changes++;
- }
- if(v->status != nv->status) {
- SWAP(v->status, nv->status);
- changes++;
- }
- if(v->type != nv->type) {
- SWAP(v->type, nv->type);
- changes++;
- }
- if(v->source_type != nv->source_type) {
- SWAP(v->source_type, nv->source_type);
- changes++;
- }
- if(v->cmds != nv->cmds) {
- SWAP(v->cmds, nv->cmds);
- changes++;
- }
- if(v->source != nv->source) {
- SWAP(v->source, nv->source);
- changes++;
- }
- if(nv->created_ut < v->created_ut) {
- SWAP(v->created_ut, nv->created_ut);
- changes++;
- }
- if(nv->modified_ut > v->modified_ut) {
- SWAP(v->modified_ut, nv->modified_ut);
- changes++;
- }
- if(v->sync != nv->sync) {
- SWAP(v->sync, nv->sync);
- changes++;
- }
- if(nv->payload) {
- SWAP(v->payload, nv->payload);
- changes++;
- }
- if(!v->execute_cb || (nv->overwrite_cb && nv->execute_cb && (v->execute_cb != nv->execute_cb || v->execute_cb_data != nv->execute_cb_data))) {
- v->execute_cb = nv->execute_cb;
- v->execute_cb_data = nv->execute_cb_data;
- changes++;
- }
- dyncfg_cleanup(nv);
- return changes > 0;
- }
- // ----------------------------------------------------------------------------
- void dyncfg_init_low_level(bool load_saved) {
- if(!dyncfg_globals.nodes) {
- dyncfg_globals.nodes = dictionary_create_advanced(DICT_OPTION_FIXED_SIZE | DICT_OPTION_DONT_OVERWRITE_VALUE, NULL, sizeof(DYNCFG));
- dictionary_register_insert_callback(dyncfg_globals.nodes, dyncfg_insert_cb, NULL);
- dictionary_register_react_callback(dyncfg_globals.nodes, dyncfg_react_cb, NULL);
- dictionary_register_conflict_callback(dyncfg_globals.nodes, dyncfg_conflict_cb, NULL);
- dictionary_register_delete_callback(dyncfg_globals.nodes, dyncfg_delete_cb, NULL);
- char path[PATH_MAX];
- snprintfz(path, sizeof(path), "%s/%s", netdata_configured_varlib_dir, "config");
- if(mkdir(path, 0755) == -1) {
- if(errno != EEXIST)
- nd_log(NDLS_DAEMON, NDLP_CRIT, "DYNCFG: failed to create dynamic configuration directory '%s'", path);
- }
- dyncfg_globals.dir = strdupz(path);
- if(load_saved)
- dyncfg_load_all();
- }
- }
- // ----------------------------------------------------------------------------
- const DICTIONARY_ITEM *dyncfg_add_internal(RRDHOST *host, const char *id, const char *path, DYNCFG_STATUS status, DYNCFG_TYPE type, DYNCFG_SOURCE_TYPE source_type, const char *source, DYNCFG_CMDS cmds, usec_t created_ut, usec_t modified_ut, bool sync, rrd_function_execute_cb_t execute_cb, void *execute_cb_data, bool overwrite_cb) {
- DYNCFG tmp = {
- .host = host,
- .path = string_strdupz(path),
- .status = status,
- .type = type,
- .cmds = cmds,
- .source_type = source_type,
- .source = string_strdupz(source),
- .created_ut = created_ut,
- .modified_ut = modified_ut,
- .sync = sync,
- .user_disabled = false,
- .restart_required = false,
- .payload = NULL,
- .execute_cb = execute_cb,
- .execute_cb_data = execute_cb_data,
- .overwrite_cb = overwrite_cb,
- };
- uuid_copy(tmp.host_uuid, host->host_uuid);
- return dictionary_set_and_acquire_item_advanced(dyncfg_globals.nodes, id, -1, &tmp, sizeof(tmp), NULL);
- }
- static void dyncfg_send_updates(const char *id) {
- const DICTIONARY_ITEM *item = dictionary_get_and_acquire_item_advanced(dyncfg_globals.nodes, id, -1);
- if(!item) {
- nd_log(NDLS_DAEMON, NDLP_ERR, "DYNCFG: asked to update plugin for configuration '%s', but it is not found.", id);
- return;
- }
- DYNCFG *df = dictionary_acquired_item_value(item);
- if(df->type == DYNCFG_TYPE_SINGLE || df->type == DYNCFG_TYPE_JOB) {
- if (df->cmds & DYNCFG_CMD_UPDATE)
- dyncfg_echo_update(item, df, id);
- }
- else if(df->type == DYNCFG_TYPE_TEMPLATE && (df->cmds & DYNCFG_CMD_ADD)) {
- STRING *template = string_strdupz(id);
- size_t len = strlen(id);
- DYNCFG *tf;
- dfe_start_reentrant(dyncfg_globals.nodes, tf) {
- const char *t_id = tf_dfe.name;
- if(tf->type == DYNCFG_TYPE_JOB && tf->template == template && strncmp(t_id, id, len) == 0 && t_id[len] == ':' && t_id[len + 1]) {
- dyncfg_echo_add(item, df, id, &t_id[len + 1]);
- }
- }
- dfe_done(tf);
- string_freez(template);
- }
- dictionary_acquired_item_release(dyncfg_globals.nodes, item);
- }
- bool dyncfg_is_user_disabled(const char *id) {
- const DICTIONARY_ITEM *item = dictionary_get_and_acquire_item(dyncfg_globals.nodes, id);
- if(!item)
- return false;
- DYNCFG *df = dictionary_acquired_item_value(item);
- bool ret = df->user_disabled;
- dictionary_acquired_item_release(dyncfg_globals.nodes, item);
- return ret;
- }
- bool dyncfg_job_has_registered_template(const char *id) {
- char buf[strlen(id) + 1];
- memcpy(buf, id, sizeof(buf));
- char *colon = strrchr(buf, ':');
- if(!colon)
- return false;
- *colon = '\0';
- const DICTIONARY_ITEM *item = dictionary_get_and_acquire_item(dyncfg_globals.nodes, buf);
- if(!item)
- return false;
- DYNCFG *df = dictionary_acquired_item_value(item);
- bool ret = df->type == DYNCFG_TYPE_TEMPLATE;
- dictionary_acquired_item_release(dyncfg_globals.nodes, item);
- return ret;
- }
- bool dyncfg_add_low_level(RRDHOST *host, const char *id, const char *path, DYNCFG_STATUS status, DYNCFG_TYPE type, DYNCFG_SOURCE_TYPE source_type, const char *source, DYNCFG_CMDS cmds, usec_t created_ut, usec_t modified_ut, bool sync, rrd_function_execute_cb_t execute_cb, void *execute_cb_data) {
- if(!dyncfg_is_valid_id(id)) {
- nd_log(NDLS_DAEMON, NDLP_ERR, "DYNCFG: id '%s' is invalid. Ignoring dynamic configuration for it.", id);
- return false;
- }
- if(type == DYNCFG_TYPE_JOB && !dyncfg_job_has_registered_template(id)) {
- nd_log(NDLS_DAEMON, NDLP_ERR, "DYNCFG: job id '%s' does not have a registered template. Ignoring dynamic configuration for it.", id);
- return false;
- }
- DYNCFG_CMDS old_cmds = cmds;
- // all configurations support schema
- cmds |= DYNCFG_CMD_SCHEMA;
- // if there is either enable or disable, both are supported
- if(cmds & (DYNCFG_CMD_ENABLE | DYNCFG_CMD_DISABLE))
- cmds |= DYNCFG_CMD_ENABLE | DYNCFG_CMD_DISABLE;
- // add
- if(type == DYNCFG_TYPE_TEMPLATE) {
- // templates must always support "add"
- cmds |= DYNCFG_CMD_ADD;
- }
- else {
- // only templates can have "add"
- cmds &= ~DYNCFG_CMD_ADD;
- }
- // remove
- if(source_type == DYNCFG_SOURCE_TYPE_DYNCFG && type == DYNCFG_TYPE_JOB) {
- // remove is only available for dyncfg jobs
- cmds |= DYNCFG_CMD_REMOVE;
- }
- else {
- // remove is only available for dyncfg jobs
- cmds &= ~DYNCFG_CMD_REMOVE;
- }
- // data
- if(type == DYNCFG_TYPE_TEMPLATE) {
- // templates do not have data
- cmds &= ~(DYNCFG_CMD_GET | DYNCFG_CMD_UPDATE | DYNCFG_CMD_TEST);
- }
- if(cmds != old_cmds) {
- CLEAN_BUFFER *t = buffer_create(1024, NULL);
- buffer_sprintf(t, "DYNCFG: id '%s' was declared with cmds: ", id);
- dyncfg_cmds2buffer(old_cmds, t);
- buffer_strcat(t, ", but they have sanitized to: ");
- dyncfg_cmds2buffer(cmds, t);
- nd_log(NDLS_DAEMON, NDLP_NOTICE, "%s", buffer_tostring(t));
- }
- const DICTIONARY_ITEM *item = dyncfg_add_internal(host, id, path, status, type, source_type, source, cmds, created_ut, modified_ut, sync, execute_cb, execute_cb_data, true);
- DYNCFG *df = dictionary_acquired_item_value(item);
- // if(df->source_type == DYNCFG_SOURCE_TYPE_DYNCFG && !df->saves)
- // nd_log(NDLS_DAEMON, NDLP_WARNING, "DYNCFG: configuration '%s' is created with source type dyncfg, but we don't have a saved configuration for it", id);
- rrd_collector_started();
- rrd_function_add(
- host,
- NULL,
- string2str(df->function),
- 120,
- 1000,
- "Dynamic configuration",
- "config",
- HTTP_ACCESS_ADMIN,
- sync,
- dyncfg_function_intercept_cb,
- NULL);
- DYNCFG_CMDS status_to_send_to_plugin = df->user_disabled ? DYNCFG_CMD_DISABLE : DYNCFG_CMD_ENABLE;
- if(status_to_send_to_plugin == DYNCFG_CMD_ENABLE && dyncfg_is_user_disabled(string2str(df->template)))
- status_to_send_to_plugin = DYNCFG_CMD_DISABLE;
- dyncfg_echo(item, df, id, status_to_send_to_plugin);
- dyncfg_send_updates(id);
- dictionary_acquired_item_release(dyncfg_globals.nodes, item);
- return true;
- }
- void dyncfg_del_low_level(RRDHOST *host, const char *id) {
- if(!dyncfg_is_valid_id(id)) {
- nd_log(NDLS_DAEMON, NDLP_ERR, "DYNCFG: id '%s' is invalid. Ignoring dynamic configuration for it.", id);
- return;
- }
- const DICTIONARY_ITEM *item = dictionary_get_and_acquire_item(dyncfg_globals.nodes, id);
- if(item) {
- DYNCFG *df = dictionary_acquired_item_value(item);
- rrd_function_del(host, NULL, string2str(df->function));
- bool garbage_collect = false;
- if(df->saves == 0) {
- dictionary_del(dyncfg_globals.nodes, id);
- garbage_collect = true;
- }
- dictionary_acquired_item_release(dyncfg_globals.nodes, item);
- if(garbage_collect)
- dictionary_garbage_collect(dyncfg_globals.nodes);
- }
- }
- void dyncfg_status_low_level(RRDHOST *host __maybe_unused, const char *id, DYNCFG_STATUS status) {
- if(!dyncfg_is_valid_id(id)) {
- nd_log(NDLS_DAEMON, NDLP_ERR, "DYNCFG: id '%s' is invalid. Ignoring dynamic configuration for it.", id);
- return;
- }
- if(status == DYNCFG_STATUS_NONE) {
- nd_log(NDLS_DAEMON, NDLP_ERR, "DYNCFG: status provided to id '%s' is invalid. Ignoring it.", id);
- return;
- }
- const DICTIONARY_ITEM *item = dictionary_get_and_acquire_item(dyncfg_globals.nodes, id);
- if(item) {
- DYNCFG *df = dictionary_acquired_item_value(item);
- df->status = status;
- dictionary_acquired_item_release(dyncfg_globals.nodes, item);
- }
- }
- // ----------------------------------------------------------------------------
- void dyncfg_add_streaming(BUFFER *wb) {
- // when sending config functions to parents, we send only 1 function called 'config';
- // the parent will send the command to the child, and the child will validate it;
- // this way the parent does not need to receive removals of config functions;
- buffer_sprintf(wb
- , PLUGINSD_KEYWORD_FUNCTION " GLOBAL " PLUGINSD_FUNCTION_CONFIG " %d \"%s\" \"%s\" \"%s\" %d\n"
- , 120
- , "Dynamic configuration"
- , "config"
- , http_id2access(HTTP_ACCESS_ADMIN)
- , 1000
- );
- }
- bool dyncfg_available_for_rrdhost(RRDHOST *host) {
- if(host == localhost || rrdhost_option_check(host, RRDHOST_OPTION_VIRTUAL_HOST))
- return true;
- return rrd_function_available(host, PLUGINSD_FUNCTION_CONFIG);
- }
- // ----------------------------------------------------------------------------
|