123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351 |
- // SPDX-License-Identifier: GPL-3.0-or-later
- #include "dyncfg-internals.h"
- #include "dyncfg.h"
- // ----------------------------------------------------------------------------
- // we intercept the config function calls of the plugin
- struct dyncfg_call {
- BUFFER *payload;
- char *function;
- char *id;
- char *add_name;
- char *source;
- DYNCFG_CMDS cmd;
- rrd_function_result_callback_t result_cb;
- void *result_cb_data;
- bool from_dyncfg_echo;
- };
- DYNCFG_STATUS dyncfg_status_from_successful_response(int code) {
- DYNCFG_STATUS status;
- if(code == DYNCFG_RESP_RUNNING)
- status = DYNCFG_STATUS_RUNNING;
- else if(code == DYNCFG_RESP_ACCEPTED || code == DYNCFG_RESP_ACCEPTED_RESTART_REQUIRED)
- status = DYNCFG_STATUS_ACCEPTED;
- return status;
- }
- void dyncfg_function_intercept_result_cb(BUFFER *wb, int code, void *result_cb_data) {
- struct dyncfg_call *dc = result_cb_data;
- bool called_from_dyncfg_echo = dc->from_dyncfg_echo;
- const DICTIONARY_ITEM *item = dictionary_get_and_acquire_item_advanced(dyncfg_globals.nodes, dc->id, -1);
- if(item) {
- DYNCFG *df = dictionary_acquired_item_value(item);
- bool old_user_disabled = df->user_disabled;
- bool save_required = false;
- if (!called_from_dyncfg_echo) {
- // the command was sent by a user
- if (DYNCFG_RESP_SUCCESS(code)) {
- if (dc->cmd == DYNCFG_CMD_ADD) {
- char id[strlen(dc->id) + 1 + strlen(dc->add_name) + 1];
- snprintfz(id, sizeof(id), "%s:%s", dc->id, dc->add_name);
- const DICTIONARY_ITEM *new_item = dyncfg_add_internal(
- df->host,
- id,
- string2str(df->path),
- dyncfg_status_from_successful_response(code),
- DYNCFG_TYPE_JOB,
- DYNCFG_SOURCE_TYPE_DYNCFG,
- dc->source,
- (df->cmds & ~DYNCFG_CMD_ADD) | DYNCFG_CMD_GET | DYNCFG_CMD_UPDATE | DYNCFG_CMD_TEST | DYNCFG_CMD_ENABLE | DYNCFG_CMD_DISABLE | DYNCFG_CMD_REMOVE,
- 0,
- 0,
- df->sync,
- df->execute_cb, df->execute_cb_data, false);
- DYNCFG *new_df = dictionary_acquired_item_value(new_item);
- SWAP(new_df->payload, dc->payload);
- if(code == DYNCFG_RESP_ACCEPTED_RESTART_REQUIRED)
- new_df->restart_required = true;
- dyncfg_file_save(id, new_df);
- dictionary_acquired_item_release(dyncfg_globals.nodes, new_item);
- } else if (dc->cmd == DYNCFG_CMD_UPDATE) {
- df->source_type = DYNCFG_SOURCE_TYPE_DYNCFG;
- string_freez(df->source);
- df->source = string_strdupz(dc->source);
- df->status = dyncfg_status_from_successful_response(code);
- SWAP(df->payload, dc->payload);
- save_required = true;
- } else if (dc->cmd == DYNCFG_CMD_ENABLE) {
- df->user_disabled = false;
- } else if (dc->cmd == DYNCFG_CMD_DISABLE) {
- df->user_disabled = true;
- } else if (dc->cmd == DYNCFG_CMD_REMOVE) {
- dyncfg_file_delete(dc->id);
- }
- if(dc->cmd != DYNCFG_CMD_ADD && code == DYNCFG_RESP_ACCEPTED_RESTART_REQUIRED)
- df->restart_required = true;
- }
- else
- nd_log(NDLS_DAEMON, NDLP_ERR,
- "DYNCFG: plugin returned code %d to user initiated call: %s", code, dc->function);
- }
- else {
- // the command was sent by dyncfg
- if(DYNCFG_RESP_SUCCESS(code)) {
- if(dc->cmd == DYNCFG_CMD_ADD) {
- char id[strlen(dc->id) + 1 + strlen(dc->add_name) + 1];
- snprintfz(id, sizeof(id), "%s:%s", dc->id, dc->add_name);
- const DICTIONARY_ITEM *new_item = dictionary_get_and_acquire_item(dyncfg_globals.nodes, id);
- if(new_item) {
- DYNCFG *new_df = dictionary_acquired_item_value(new_item);
- new_df->status = dyncfg_status_from_successful_response(code);
- if(code == DYNCFG_RESP_ACCEPTED_RESTART_REQUIRED)
- new_df->restart_required = true;
- dictionary_acquired_item_release(dyncfg_globals.nodes, new_item);
- }
- }
- else if(dc->cmd == DYNCFG_CMD_UPDATE) {
- df->status = dyncfg_status_from_successful_response(code);
- df->plugin_rejected = false;
- }
- else if(dc->cmd == DYNCFG_CMD_DISABLE)
- df->status = DYNCFG_STATUS_DISABLED;
- else if(dc->cmd == DYNCFG_CMD_ENABLE)
- df->status = dyncfg_status_from_successful_response(code);
- if(dc->cmd != DYNCFG_CMD_ADD && code == DYNCFG_RESP_ACCEPTED_RESTART_REQUIRED)
- df->restart_required = true;
- }
- else {
- nd_log(NDLS_DAEMON, NDLP_ERR,
- "DYNCFG: plugin returned code %d to dyncfg initiated call: %s", code, dc->function);
- if(dc->cmd & (DYNCFG_CMD_UPDATE | DYNCFG_CMD_ADD))
- df->plugin_rejected = true;
- }
- }
- if (save_required || old_user_disabled != df->user_disabled)
- dyncfg_file_save(dc->id, df);
- dictionary_acquired_item_release(dyncfg_globals.nodes, item);
- }
- if(dc->result_cb)
- dc->result_cb(wb, code, dc->result_cb_data);
- buffer_free(dc->payload);
- freez(dc->function);
- freez(dc->id);
- freez(dc->source);
- freez(dc->add_name);
- freez(dc);
- }
- // ----------------------------------------------------------------------------
- static void dyncfg_apply_action_on_all_template_jobs(const char *template_id, DYNCFG_CMDS c) {
- STRING *template = string_strdupz(template_id);
- DYNCFG *df;
- dfe_start_reentrant(dyncfg_globals.nodes, df) {
- if(df->template == template && df->type == DYNCFG_TYPE_JOB) {
- DYNCFG_CMDS cmd_to_send_to_plugin = c;
- if(c == DYNCFG_CMD_ENABLE)
- cmd_to_send_to_plugin = df->user_disabled ? DYNCFG_CMD_DISABLE : DYNCFG_CMD_ENABLE;
- else if(c == DYNCFG_CMD_DISABLE)
- cmd_to_send_to_plugin = DYNCFG_CMD_DISABLE;
- dyncfg_echo(df_dfe.item, df, df_dfe.name, cmd_to_send_to_plugin);
- }
- }
- dfe_done(df);
- string_freez(template);
- }
- // ----------------------------------------------------------------------------
- // the callback for all config functions
- int dyncfg_function_intercept_cb(struct rrd_function_execute *rfe, void *data __maybe_unused) {
- // IMPORTANT: this function MUST call the result_cb even on failures
- bool called_from_dyncfg_echo = rrd_function_has_this_original_result_callback(rfe->transaction, dyncfg_echo_cb);
- DYNCFG_CMDS c = DYNCFG_CMD_NONE;
- const DICTIONARY_ITEM *item = NULL;
- const char *add_name = NULL;
- size_t add_name_len = 0;
- if(strncmp(rfe->function, PLUGINSD_FUNCTION_CONFIG " ", sizeof(PLUGINSD_FUNCTION_CONFIG)) == 0) {
- const char *id = &rfe->function[sizeof(PLUGINSD_FUNCTION_CONFIG)];
- while(isspace(*id)) id++;
- const char *space = id;
- while(*space && !isspace(*space)) space++;
- size_t id_len = space - id;
- const char *cmd = space;
- while(isspace(*cmd)) cmd++;
- space = cmd;
- while(*space && !isspace(*space)) space++;
- size_t cmd_len = space - cmd;
- char cmd_copy[cmd_len + 1];
- strncpyz(cmd_copy, cmd, cmd_len);
- c = dyncfg_cmds2id(cmd_copy);
- if(c == DYNCFG_CMD_ADD) {
- add_name = space;
- while(isspace(*add_name)) add_name++;
- space = add_name;
- while(*space && !isspace(*space)) space++;
- add_name_len = space - add_name;
- }
- item = dictionary_get_and_acquire_item_advanced(dyncfg_globals.nodes, id, (ssize_t)id_len);
- }
- int rc = HTTP_RESP_INTERNAL_SERVER_ERROR;
- if(!item) {
- rc = HTTP_RESP_NOT_FOUND;
- dyncfg_default_response(rfe->result.wb, rc, "dyncfg functions intercept: id is not found");
- if(rfe->result.cb)
- rfe->result.cb(rfe->result.wb, rc, rfe->result.data);
- return HTTP_RESP_NOT_FOUND;
- }
- DYNCFG *df = dictionary_acquired_item_value(item);
- const char *id = dictionary_acquired_item_name(item);
- bool has_payload = rfe->payload && buffer_strlen(rfe->payload) ? true : false;
- bool make_the_call_to_plugin = true;
- if((c & (DYNCFG_CMD_GET | DYNCFG_CMD_ENABLE | DYNCFG_CMD_DISABLE | DYNCFG_CMD_REMOVE | DYNCFG_CMD_RESTART)) && has_payload)
- nd_log(NDLS_DAEMON, NDLP_ERR, "DYNCFG: command has a payload, but it is not going to be used: %s", rfe->function);
- if(c == DYNCFG_CMD_NONE) {
- nd_log(NDLS_DAEMON, NDLP_ERR, "DYNCFG: this command is unknown: %s", rfe->function);
- rc = HTTP_RESP_BAD_REQUEST;
- dyncfg_default_response(rfe->result.wb, rc,
- "dyncfg functions intercept: unknown command");
- make_the_call_to_plugin = false;
- }
- else if(!(df->cmds & c)) {
- nd_log(NDLS_DAEMON, NDLP_ERR, "DYNCFG: this command is not supported by the configuration node: %s", rfe->function);
- rc = HTTP_RESP_BAD_REQUEST;
- dyncfg_default_response(rfe->result.wb, rc,
- "dyncfg functions intercept: this command is not supported by this configuration node");
- make_the_call_to_plugin = false;
- }
- else if((c & (DYNCFG_CMD_ADD | DYNCFG_CMD_UPDATE | DYNCFG_CMD_TEST)) && !has_payload) {
- nd_log(NDLS_DAEMON, NDLP_ERR, "DYNCFG: command requires a payload, but no payload given: %s", rfe->function);
- rc = HTTP_RESP_BAD_REQUEST;
- dyncfg_default_response(rfe->result.wb, rc,
- "dyncfg functions intercept: payload is required");
- make_the_call_to_plugin = false;
- }
- else if(c == DYNCFG_CMD_SCHEMA) {
- bool loaded = false;
- if(df->type == DYNCFG_TYPE_JOB) {
- char template[strlen(id) + 1];
- memcpy(template, id, sizeof(template));
- char *colon = strrchr(template, ':');
- if(colon) *colon = '\0';
- if(template[0])
- loaded = dyncfg_get_schema(template, rfe->result.wb);
- }
- else
- loaded = dyncfg_get_schema(id, rfe->result.wb);
- if(loaded) {
- rfe->result.wb->content_type = CT_APPLICATION_JSON;
- rfe->result.wb->expires = now_realtime_sec();
- rc = HTTP_RESP_OK;
- make_the_call_to_plugin = false;
- }
- }
- else if(c & (DYNCFG_CMD_ENABLE | DYNCFG_CMD_DISABLE | DYNCFG_CMD_RESTART) && df->type == DYNCFG_TYPE_TEMPLATE) {
- if(!called_from_dyncfg_echo) {
- bool old_user_disabled = df->user_disabled;
- if (c == DYNCFG_CMD_ENABLE)
- df->user_disabled = false;
- else if (c == DYNCFG_CMD_DISABLE)
- df->user_disabled = true;
- if (df->user_disabled != old_user_disabled)
- dyncfg_file_save(id, df);
- }
- dyncfg_apply_action_on_all_template_jobs(id, c);
- rc = HTTP_RESP_OK;
- dyncfg_default_response(rfe->result.wb, rc, "applied");
- make_the_call_to_plugin = false;
- }
- else if(c == DYNCFG_CMD_ADD) {
- if (df->type != DYNCFG_TYPE_TEMPLATE) {
- nd_log(NDLS_DAEMON, NDLP_ERR, "DYNCFG: add command can only be applied on templates, not %s: %s",
- dyncfg_id2type(df->type), rfe->function);
- rc = HTTP_RESP_BAD_REQUEST;
- dyncfg_default_response(rfe->result.wb, rc,
- "dyncfg functions intercept: add command is only allowed in templates");
- make_the_call_to_plugin = false;
- }
- else if (!add_name || !*add_name || !add_name_len) {
- nd_log(NDLS_DAEMON, NDLP_ERR, "DYNCFG: add command does not specify a name: %s", rfe->function);
- rc = HTTP_RESP_BAD_REQUEST;
- dyncfg_default_response(rfe->result.wb, rc,
- "dyncfg functions intercept: command add requires a name, which is missing");
- make_the_call_to_plugin = false;
- }
- }
- else if(c == DYNCFG_CMD_ENABLE && df->type == DYNCFG_TYPE_JOB && dyncfg_is_user_disabled(string2str(df->template))) {
- nd_log(NDLS_DAEMON, NDLP_ERR, "DYNCFG: cannot enable a job of a disabled template: %s", rfe->function);
- rc = HTTP_RESP_BAD_REQUEST;
- dyncfg_default_response(rfe->result.wb, rc,
- "dyncfg functions intercept: this job belongs to disabled template");
- make_the_call_to_plugin = false;
- }
- if(make_the_call_to_plugin) {
- struct dyncfg_call *dc = callocz(1, sizeof(*dc));
- dc->function = strdupz(rfe->function);
- dc->id = strdupz(id);
- dc->source = rfe->source ? strdupz(rfe->source) : NULL;
- dc->add_name = (c == DYNCFG_CMD_ADD) ? strndupz(add_name, add_name_len) : NULL;
- dc->cmd = c;
- dc->result_cb = rfe->result.cb;
- dc->result_cb_data = rfe->result.data;
- dc->payload = buffer_dup(rfe->payload);
- dc->from_dyncfg_echo = called_from_dyncfg_echo;
- rfe->result.cb = dyncfg_function_intercept_result_cb;
- rfe->result.data = dc;
- rc = df->execute_cb(rfe, df->execute_cb_data);
- }
- else if(rfe->result.cb)
- rfe->result.cb(rfe->result.wb, rc, rfe->result.data);
- dictionary_acquired_item_release(dyncfg_globals.nodes, item);
- return rc;
- }
|