| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | |
| 3 | #include "dyncfg-internals.h" |
| 4 | #include "dyncfg.h" |
| 5 | |
| 6 | struct dyncfg_call { |
| 7 | ND_UUID transaction; |
| 8 | BUFFER *payload; |
| 9 | const char *function; |
| 10 | const char *id; |
| 11 | const char *add_name; |
| 12 | const char *source; |
| 13 | DYNCFG_CMDS cmd; |
| 14 | rrd_function_result_callback_t result_cb; |
| 15 | void *result_cb_data; |
| 16 | bool from_dyncfg_echo; |
| 17 | }; |
| 18 | |
| 19 | // ---------------------------------------------------------------------------- |
| 20 | |
| 21 | ENUM_STR_MAP_DEFINE(DYNCFG_CMDS) = { |
| 22 | { DYNCFG_CMD_GET, "get" }, |
| 23 | { DYNCFG_CMD_SCHEMA, "schema" }, |
| 24 | { DYNCFG_CMD_UPDATE, "update" }, |
| 25 | { DYNCFG_CMD_ADD, "add" }, |
| 26 | { DYNCFG_CMD_TEST, "test" }, |
| 27 | { DYNCFG_CMD_REMOVE, "remove" }, |
| 28 | { DYNCFG_CMD_ENABLE, "enable" }, |
| 29 | { DYNCFG_CMD_DISABLE, "disable" }, |
| 30 | { DYNCFG_CMD_RESTART, "restart" }, |
| 31 | { DYNCFG_CMD_USERCONFIG, "userconfig" }, |
| 32 | |
| 33 | // terminator |
| 34 | { 0, NULL } |
| 35 | }; |
| 36 | |
| 37 | ENUM_STR_DEFINE_FUNCTIONS(DYNCFG_CMDS, DYNCFG_CMD_NONE, "none"); |
| 38 | |
| 39 | static void dyncfg_log_user_action(DYNCFG *df, struct dyncfg_call *dc) { |
| 40 | if(dc->cmd == DYNCFG_CMD_USERCONFIG || dc->cmd == DYNCFG_CMD_GET || dc->cmd == DYNCFG_CMD_SCHEMA) |
| 41 | return; |
| 42 | |
| 43 | const char *type; |
| 44 | switch(df->type) { |
| 45 | default: |
| 46 | case DYNCFG_TYPE_SINGLE: |
| 47 | type = "on"; |
| 48 | break; |
| 49 | |
| 50 | case DYNCFG_TYPE_TEMPLATE: |
| 51 | type = "on template"; |
| 52 | break; |
| 53 | case DYNCFG_TYPE_JOB: |
| 54 | type = "on job"; |
| 55 | break; |
| 56 | } |
| 57 | |
| 58 | USER_AUTH req; |
| 59 | if(!user_auth_from_source(dc->source, &req)) { |
| 60 | ND_LOG_STACK lgs[] = { |
| 61 | ND_LOG_FIELD_TXT(NDF_MODULE, "DYNCFG"), |
| 62 | ND_LOG_FIELD_STR(NDF_NIDL_NODE, localhost->hostname), |
| 63 | ND_LOG_FIELD_TXT(NDF_REQUEST, dc->function), |
| 64 | ND_LOG_FIELD_UUID(NDF_TRANSACTION_ID, &dc->transaction.uuid), |
| 65 | ND_LOG_FIELD_UUID(NDF_MESSAGE_ID, &dyncfg_user_action_msgid), |
| 66 | ND_LOG_FIELD_END(), |
| 67 | }; |
| 68 | ND_LOG_STACK_PUSH(lgs); |
| 69 | |
| 70 | nd_log(NDLS_DAEMON, NDLP_NOTICE, |
| 71 | "DYNCFG USER ACTION '%s' %s%s%s '%s' from source: %s", |
| 72 | DYNCFG_CMDS_2str(dc->cmd), |
| 73 | dc->add_name ? dc->add_name : "", |
| 74 | dc->add_name ? " " : "", |
| 75 | type, dc->id, dc->source); |
| 76 | |
| 77 | return; |
| 78 | } |
| 79 | |
| 80 | char access_str[1024]; |
| 81 | http_access2txt(access_str, sizeof(access_str), " ", req.access); |
| 82 | |
| 83 | ND_LOG_STACK lgs[] = { |
| 84 | ND_LOG_FIELD_TXT(NDF_MODULE, "DYNCFG"), |
| 85 | ND_LOG_FIELD_STR(NDF_NIDL_NODE, localhost->hostname), |
| 86 | ND_LOG_FIELD_TXT(NDF_REQUEST, dc->function), |
| 87 | ND_LOG_FIELD_UUID(NDF_TRANSACTION_ID, &dc->transaction.uuid), |
| 88 | ND_LOG_FIELD_UUID(NDF_MESSAGE_ID, &dyncfg_user_action_msgid), |
| 89 | |
| 90 | ND_LOG_FIELD_UUID(NDF_ACCOUNT_ID, &req.cloud_account_id.uuid), |
| 91 | ND_LOG_FIELD_TXT(NDF_SRC_IP, req.client_ip), |
| 92 | ND_LOG_FIELD_TXT(NDF_SRC_FORWARDED_FOR, req.forwarded_for), |
| 93 | ND_LOG_FIELD_TXT(NDF_USER_NAME, req.client_name), |
| 94 | ND_LOG_FIELD_TXT(NDF_USER_ROLE, http_id2user_role(req.user_role)), |
| 95 | ND_LOG_FIELD_CB(NDF_USER_ACCESS, log_cb_http_access_to_hex, &req.access), |
| 96 | ND_LOG_FIELD_END(), |
| 97 | }; |
| 98 | ND_LOG_STACK_PUSH(lgs); |
| 99 | |
| 100 | nd_log(NDLS_DAEMON, NDLP_NOTICE, |
| 101 | "DYNCFG USER ACTION '%s' %s%s%s '%s' by user '%s', IP '%s'", |
| 102 | DYNCFG_CMDS_2str(dc->cmd), |
| 103 | dc->add_name ? dc->add_name : "", |
| 104 | dc->add_name ? " " : "", |
| 105 | type, dc->id, req.client_name, |
| 106 | req.forwarded_for[0] ? req.forwarded_for : req.client_ip); |
| 107 | } |
| 108 | |
| 109 | // ---------------------------------------------------------------------------- |
| 110 | // we intercept the config function calls of the plugin |
| 111 | |
| 112 | static void dyncfg_function_intercept_job_successfully_added(DYNCFG *df_template, int code, struct dyncfg_call *dc) { |
| 113 | size_t id_size = strlen(dc->id) + strlen(dc->add_name) + 2; |
| 114 | CLEAN_CHAR_P *id = mallocz(id_size); |
| 115 | snprintfz(id, id_size, "%s:%s", dc->id, dc->add_name); |
| 116 | |
| 117 | RRDHOST *host = dyncfg_rrdhost(df_template); |
| 118 | if(!host) { |
| 119 | nd_log(NDLS_DAEMON, NDLP_ERR, |
| 120 | "DYNCFG: cannot add job '%s' because host is missing", id); |
| 121 | } |
| 122 | else { |
| 123 | const DICTIONARY_ITEM *item = dyncfg_add_internal( |
| 124 | host, |
| 125 | id, |
| 126 | string2str(df_template->path), |
| 127 | dyncfg_status_from_successful_response(code), |
| 128 | DYNCFG_TYPE_JOB, |
| 129 | DYNCFG_SOURCE_TYPE_DYNCFG, |
| 130 | dc->source, |
| 131 | (df_template->cmds & ~DYNCFG_CMD_ADD) | DYNCFG_CMD_GET | DYNCFG_CMD_UPDATE | DYNCFG_CMD_TEST | |
| 132 | DYNCFG_CMD_ENABLE | DYNCFG_CMD_DISABLE | DYNCFG_CMD_REMOVE, |
| 133 | 0, |
| 134 | 0, |
| 135 | df_template->sync, |
| 136 | df_template->view_access, |
| 137 | df_template->edit_access, |
| 138 | df_template->execute_cb, |
| 139 | df_template->execute_cb_data, |
| 140 | false); |
| 141 | |
| 142 | // adding does not create df->dyncfg |
| 143 | // we have to do it here |
| 144 | |
| 145 | DYNCFG *df = dictionary_acquired_item_value(item); |
| 146 | SWAP(df->dyncfg.payload, dc->payload); |
| 147 | dyncfg_set_dyncfg_source_from_txt(df, dc->source); |
| 148 | df->dyncfg.user_disabled = false; |
| 149 | df->dyncfg.source_type = DYNCFG_SOURCE_TYPE_DYNCFG; |
| 150 | df->dyncfg.status = dyncfg_status_from_successful_response(code); |
| 151 | |
| 152 | dyncfg_file_save(id, df); // updates also the df->dyncfg timestamps |
| 153 | dyncfg_update_status_on_successful_add_or_update(df, code); |
| 154 | |
| 155 | dictionary_acquired_item_release(dyncfg_globals.nodes, item); |
| 156 | } |
| 157 | } |
| 158 | |
| 159 | static void dyncfg_function_intercept_job_successfully_updated(DYNCFG *df, int code, struct dyncfg_call *dc) { |
| 160 | df->dyncfg.status = dyncfg_status_from_successful_response(code); |
| 161 | df->dyncfg.source_type = DYNCFG_SOURCE_TYPE_DYNCFG; |
| 162 | SWAP(df->dyncfg.payload, dc->payload); |
| 163 | dyncfg_set_dyncfg_source_from_txt(df, dc->source); |
| 164 | |
| 165 | dyncfg_update_status_on_successful_add_or_update(df, code); |
| 166 | df->cmds = dyncfg_sanitize_cmds(df->type, df->current.source_type, df->cmds); |
| 167 | } |
| 168 | |
| 169 | void dyncfg_function_intercept_result_cb(BUFFER *wb, int code, void *result_cb_data) { |
| 170 | struct dyncfg_call *dc = result_cb_data; |
| 171 | |
| 172 | bool called_from_dyncfg_echo = dc->from_dyncfg_echo; |
| 173 | |
| 174 | const DICTIONARY_ITEM *item = dictionary_get_and_acquire_item_advanced(dyncfg_globals.nodes, dc->id, -1); |
| 175 | if(item) { |
| 176 | DYNCFG *df = dictionary_acquired_item_value(item); |
| 177 | bool old_user_disabled = df->dyncfg.user_disabled; |
| 178 | bool save_required = false; |
| 179 | |
| 180 | if (!called_from_dyncfg_echo) { |
| 181 | // the command was sent by a user |
| 182 | |
| 183 | if (DYNCFG_RESP_SUCCESS(code)) { |
| 184 | if (dc->cmd == DYNCFG_CMD_ADD) { |
| 185 | dyncfg_function_intercept_job_successfully_added(df, code, dc); |
| 186 | } else if (dc->cmd == DYNCFG_CMD_UPDATE) { |
| 187 | dyncfg_function_intercept_job_successfully_updated(df, code, dc); |
| 188 | save_required = true; |
| 189 | } |
| 190 | else if (dc->cmd == DYNCFG_CMD_ENABLE) { |
| 191 | df->dyncfg.user_disabled = false; |
| 192 | } |
| 193 | else if (dc->cmd == DYNCFG_CMD_DISABLE) { |
| 194 | df->dyncfg.user_disabled = true; |
| 195 | } |
| 196 | else if (dc->cmd == DYNCFG_CMD_REMOVE) { |
| 197 | dyncfg_file_delete(dc->id); |
| 198 | dictionary_del(dyncfg_globals.nodes, dc->id); |
| 199 | } |
| 200 | |
| 201 | if (save_required || old_user_disabled != df->dyncfg.user_disabled) |
| 202 | dyncfg_file_save(dc->id, df); |
| 203 | |
| 204 | dyncfg_log_user_action(df, dc); |
| 205 | } |
| 206 | else |
| 207 | nd_log(NDLS_DAEMON, NDLP_ERR, |
| 208 | "DYNCFG: plugin returned code %d to user initiated call: %s", code, dc->function); |
| 209 | } |
| 210 | else { |
| 211 | // the command was sent by dyncfg |
| 212 | // these are handled by the echo callback, we don't need to do anything here |
| 213 | ; |
| 214 | } |
| 215 | |
| 216 | dictionary_acquired_item_release(dyncfg_globals.nodes, item); |
| 217 | } |
| 218 | |
| 219 | if(dc->result_cb) |
| 220 | dc->result_cb(wb, code, dc->result_cb_data); |
| 221 | |
| 222 | buffer_free(dc->payload); |
| 223 | freez((void *)dc->function); |
| 224 | freez((void *)dc->id); |
| 225 | freez((void *)dc->source); |
| 226 | freez((void *)dc->add_name); |
| 227 | freez(dc); |
| 228 | } |
| 229 | |
| 230 | // ---------------------------------------------------------------------------- |
| 231 | |
| 232 | static void dyncfg_apply_action_on_all_template_jobs(struct rrd_function_execute *rfe, const char *template_id, DYNCFG_CMDS c) { |
| 233 | STRING *template = string_strdupz(template_id); |
| 234 | DYNCFG *df; |
| 235 | |
| 236 | size_t all = 0, done = 0; |
| 237 | dfe_start_read(dyncfg_globals.nodes, df) { |
| 238 | if(df->template == template && df->type == DYNCFG_TYPE_JOB) |
| 239 | all++; |
| 240 | } |
| 241 | dfe_done(df); |
| 242 | |
| 243 | if(rfe->progress.cb) |
| 244 | rfe->progress.cb(rfe->transaction, rfe->progress.data, done, all); |
| 245 | |
| 246 | dfe_start_reentrant(dyncfg_globals.nodes, df) { |
| 247 | if(df->template == template && df->type == DYNCFG_TYPE_JOB) { |
| 248 | DYNCFG_CMDS cmd_to_send_to_plugin = c; |
| 249 | |
| 250 | if(c == DYNCFG_CMD_ENABLE) |
| 251 | cmd_to_send_to_plugin = df->dyncfg.user_disabled ? DYNCFG_CMD_DISABLE : DYNCFG_CMD_ENABLE; |
| 252 | else if(c == DYNCFG_CMD_DISABLE) |
| 253 | cmd_to_send_to_plugin = DYNCFG_CMD_DISABLE; |
| 254 | |
| 255 | dyncfg_echo(df_dfe.item, df, df_dfe.name, cmd_to_send_to_plugin); |
| 256 | |
| 257 | if(rfe->progress.cb) |
| 258 | rfe->progress.cb(rfe->transaction, rfe->progress.data, ++done, all); |
| 259 | } |
| 260 | } |
| 261 | dfe_done(df); |
| 262 | |
| 263 | string_freez(template); |
| 264 | } |
| 265 | |
| 266 | // ---------------------------------------------------------------------------- |
| 267 | // the callback for all config functions |
| 268 | |
| 269 | static int dyncfg_intercept_early_error(struct rrd_function_execute *rfe, int rc, const char *msg) { |
| 270 | rc = dyncfg_default_response(rfe->result.wb, rc, msg); |
| 271 | |
| 272 | if(rfe->result.cb) |
| 273 | rfe->result.cb(rfe->result.wb, rc, rfe->result.data); |
| 274 | |
| 275 | return rc; |
| 276 | } |
| 277 | |
| 278 | const DICTIONARY_ITEM *dyncfg_get_template_of_new_job(const char *job_id) { |
| 279 | CLEAN_CHAR_P *id_copy = strdupz(job_id); |
| 280 | |
| 281 | char *colon = strrchr(id_copy, ':'); |
| 282 | if(!colon) return NULL; |
| 283 | |
| 284 | *colon = '\0'; |
| 285 | const DICTIONARY_ITEM *item = dictionary_get_and_acquire_item(dyncfg_globals.nodes, id_copy); |
| 286 | if(!item) return NULL; |
| 287 | |
| 288 | DYNCFG *df = dictionary_acquired_item_value(item); |
| 289 | if(df->type != DYNCFG_TYPE_TEMPLATE) { |
| 290 | dictionary_acquired_item_release(dyncfg_globals.nodes, item); |
| 291 | return NULL; |
| 292 | } |
| 293 | |
| 294 | return item; |
| 295 | } |
| 296 | |
| 297 | int dyncfg_function_intercept_cb(struct rrd_function_execute *rfe, void *data __maybe_unused) { |
| 298 | |
| 299 | // IMPORTANT: this function MUST call the result_cb even on failures |
| 300 | |
| 301 | bool called_from_dyncfg_echo = rrd_function_has_this_original_result_callback(rfe->transaction, dyncfg_echo_cb); |
| 302 | bool has_payload = rfe->payload && buffer_strlen(rfe->payload) ? true : false; |
| 303 | bool make_the_call_to_plugin = true; |
| 304 | |
| 305 | int rc = HTTP_RESP_INTERNAL_SERVER_ERROR; |
| 306 | DYNCFG_CMDS cmd; |
| 307 | const DICTIONARY_ITEM *item = NULL; |
| 308 | |
| 309 | CLEAN_CHAR_P *buf = strdupz(rfe->function); |
| 310 | |
| 311 | char *words[20]; |
| 312 | size_t num_words = quoted_strings_splitter_whitespace(buf, words, 20); |
| 313 | |
| 314 | size_t i = 0; |
| 315 | char *config = get_word(words, num_words, i++); |
| 316 | char *id = get_word(words, num_words, i++); |
| 317 | char *cmd_str = get_word(words, num_words, i++); |
| 318 | char *add_name = get_word(words, num_words, i++); |
| 319 | |
| 320 | if(!config || !*config || strcmp(config, PLUGINSD_FUNCTION_CONFIG) != 0) |
| 321 | return dyncfg_intercept_early_error( |
| 322 | rfe, HTTP_RESP_BAD_REQUEST, |
| 323 | "dyncfg functions intercept: this is not a dyncfg request"); |
| 324 | |
| 325 | cmd = dyncfg_cmds2id(cmd_str); |
| 326 | if(cmd == DYNCFG_CMD_NONE) |
| 327 | return dyncfg_intercept_early_error( |
| 328 | rfe, HTTP_RESP_BAD_REQUEST, |
| 329 | "dyncfg functions intercept: invalid command received"); |
| 330 | |
| 331 | if(cmd == DYNCFG_CMD_ADD || cmd == DYNCFG_CMD_TEST || cmd == DYNCFG_CMD_USERCONFIG) { |
| 332 | if(cmd == DYNCFG_CMD_TEST && (!add_name || !*add_name)) { |
| 333 | // backwards compatibility for TEST without a name |
| 334 | char *colon = strrchr(id, ':'); |
| 335 | if(colon) { |
| 336 | *colon = '\0'; |
| 337 | add_name = ++colon; |
| 338 | } |
| 339 | else |
| 340 | add_name = "test"; |
| 341 | } |
| 342 | |
| 343 | if(!add_name || !*add_name) |
| 344 | return dyncfg_intercept_early_error( |
| 345 | rfe, HTTP_RESP_BAD_REQUEST, |
| 346 | "dyncfg functions intercept: this action requires a name"); |
| 347 | |
| 348 | if(!called_from_dyncfg_echo) { |
| 349 | size_t nid_size = strlen(id) + strlen(add_name) + 2; |
| 350 | CLEAN_CHAR_P *nid = mallocz(nid_size); |
| 351 | snprintfz(nid, nid_size, "%s:%s", id, add_name); |
| 352 | |
| 353 | if (cmd == DYNCFG_CMD_ADD && dictionary_get(dyncfg_globals.nodes, nid)) |
| 354 | return dyncfg_intercept_early_error( |
| 355 | rfe, HTTP_RESP_BAD_REQUEST, |
| 356 | "dyncfg functions intercept: a configuration with this name already exists"); |
| 357 | } |
| 358 | } |
| 359 | |
| 360 | if((cmd == DYNCFG_CMD_ADD || cmd == DYNCFG_CMD_UPDATE || cmd == DYNCFG_CMD_TEST || cmd == DYNCFG_CMD_USERCONFIG) && !has_payload) |
| 361 | return dyncfg_intercept_early_error( |
| 362 | rfe, HTTP_RESP_BAD_REQUEST, |
| 363 | "dyncfg functions intercept: this action requires a payload"); |
| 364 | |
| 365 | if((cmd != DYNCFG_CMD_ADD && cmd != DYNCFG_CMD_UPDATE && cmd != DYNCFG_CMD_TEST && cmd != DYNCFG_CMD_USERCONFIG) && has_payload) |
| 366 | return dyncfg_intercept_early_error( |
| 367 | rfe, HTTP_RESP_BAD_REQUEST, |
| 368 | "dyncfg functions intercept: this action does not require a payload"); |
| 369 | |
| 370 | item = dictionary_get_and_acquire_item(dyncfg_globals.nodes, id); |
| 371 | if(!item) { |
| 372 | if(cmd == DYNCFG_CMD_TEST || cmd == DYNCFG_CMD_USERCONFIG) { |
| 373 | // this may be a test on a new job |
| 374 | item = dyncfg_get_template_of_new_job(id); |
| 375 | } |
| 376 | |
| 377 | if(!item) |
| 378 | return dyncfg_intercept_early_error( |
| 379 | rfe, HTTP_RESP_NOT_FOUND, |
| 380 | "dyncfg functions intercept: id is not found"); |
| 381 | } |
| 382 | |
| 383 | DYNCFG *df = dictionary_acquired_item_value(item); |
| 384 | |
| 385 | // 1. check the permissions of the request |
| 386 | |
| 387 | switch(cmd) { |
| 388 | case DYNCFG_CMD_GET: |
| 389 | case DYNCFG_CMD_SCHEMA: |
| 390 | case DYNCFG_CMD_USERCONFIG: |
| 391 | if(!http_access_user_has_enough_access_level_for_endpoint(rfe->user_access, df->view_access)) { |
| 392 | make_the_call_to_plugin = false; |
| 393 | rc = dyncfg_default_response( |
| 394 | rfe->result.wb, HTTP_RESP_FORBIDDEN, |
| 395 | "dyncfg: you don't have enough view permissions to execute this command"); |
| 396 | } |
| 397 | break; |
| 398 | |
| 399 | case DYNCFG_CMD_ENABLE: |
| 400 | case DYNCFG_CMD_DISABLE: |
| 401 | case DYNCFG_CMD_ADD: |
| 402 | case DYNCFG_CMD_TEST: |
| 403 | case DYNCFG_CMD_UPDATE: |
| 404 | case DYNCFG_CMD_REMOVE: |
| 405 | case DYNCFG_CMD_RESTART: |
| 406 | if(!http_access_user_has_enough_access_level_for_endpoint(rfe->user_access, df->edit_access)) { |
| 407 | make_the_call_to_plugin = false; |
| 408 | rc = dyncfg_default_response( |
| 409 | rfe->result.wb, HTTP_RESP_FORBIDDEN, |
| 410 | "dyncfg: you don't have enough edit permissions to execute this command"); |
| 411 | } |
| 412 | break; |
| 413 | |
| 414 | default: { |
| 415 | make_the_call_to_plugin = false; |
| 416 | rc = dyncfg_default_response( |
| 417 | rfe->result.wb, HTTP_RESP_INTERNAL_SERVER_ERROR, |
| 418 | "dyncfg: permissions for this command are not set"); |
| 419 | } |
| 420 | break; |
| 421 | } |
| 422 | |
| 423 | // 2. validate the request parameters |
| 424 | |
| 425 | if(make_the_call_to_plugin) { |
| 426 | if (!(df->cmds & cmd)) { |
| 427 | nd_log(NDLS_DAEMON, NDLP_ERR, |
| 428 | "DYNCFG: this command is not supported by the configuration node: %s", rfe->function); |
| 429 | |
| 430 | make_the_call_to_plugin = false; |
| 431 | rc = dyncfg_default_response( |
| 432 | rfe->result.wb, HTTP_RESP_BAD_REQUEST, |
| 433 | "dyncfg functions intercept: this command is not supported by this configuration node"); |
| 434 | } |
| 435 | else if (cmd == DYNCFG_CMD_ADD) { |
| 436 | if (df->type != DYNCFG_TYPE_TEMPLATE) { |
| 437 | make_the_call_to_plugin = false; |
| 438 | rc = dyncfg_default_response( |
| 439 | rfe->result.wb, HTTP_RESP_BAD_REQUEST, |
| 440 | "dyncfg functions intercept: add command is only allowed in templates"); |
| 441 | |
| 442 | nd_log(NDLS_DAEMON, NDLP_ERR, |
| 443 | "DYNCFG: add command can only be applied on templates, not %s: %s", |
| 444 | dyncfg_id2type(df->type), rfe->function); |
| 445 | } |
| 446 | } |
| 447 | else if ( |
| 448 | cmd == DYNCFG_CMD_ENABLE && df->type == DYNCFG_TYPE_JOB && |
| 449 | dyncfg_is_user_disabled(string2str(df->template))) { |
| 450 | nd_log(NDLS_DAEMON, NDLP_ERR, |
| 451 | "DYNCFG: cannot enable a job of a disabled template: %s", |
| 452 | rfe->function); |
| 453 | |
| 454 | make_the_call_to_plugin = false; |
| 455 | rc = dyncfg_default_response( |
| 456 | rfe->result.wb, HTTP_RESP_BAD_REQUEST, |
| 457 | "dyncfg functions intercept: this job belongs to disabled template"); |
| 458 | } |
| 459 | } |
| 460 | |
| 461 | // 3. check if it is one of the commands we should execute |
| 462 | |
| 463 | if(make_the_call_to_plugin) { |
| 464 | if (cmd & (DYNCFG_CMD_ENABLE | DYNCFG_CMD_DISABLE | DYNCFG_CMD_RESTART) && df->type == DYNCFG_TYPE_TEMPLATE) { |
| 465 | if (!called_from_dyncfg_echo) { |
| 466 | bool old_user_disabled = df->dyncfg.user_disabled; |
| 467 | if (cmd == DYNCFG_CMD_ENABLE) |
| 468 | df->dyncfg.user_disabled = false; |
| 469 | else if (cmd == DYNCFG_CMD_DISABLE) |
| 470 | df->dyncfg.user_disabled = true; |
| 471 | |
| 472 | if (df->dyncfg.user_disabled != old_user_disabled) |
| 473 | dyncfg_file_save(id, df); |
| 474 | |
| 475 | // log it |
| 476 | { |
| 477 | struct dyncfg_call dc = { |
| 478 | .function = rfe->function, |
| 479 | .id = id, |
| 480 | .source = rfe->source, |
| 481 | .add_name = add_name, |
| 482 | .cmd = cmd, |
| 483 | .result_cb = NULL, |
| 484 | .result_cb_data = NULL, |
| 485 | .payload = rfe->payload, |
| 486 | .from_dyncfg_echo = called_from_dyncfg_echo, |
| 487 | }; |
| 488 | uuid_copy(dc.transaction.uuid, *rfe->transaction); |
| 489 | |
| 490 | dyncfg_log_user_action(df, &dc); |
| 491 | } |
| 492 | } |
| 493 | |
| 494 | dyncfg_apply_action_on_all_template_jobs(rfe, id, cmd); |
| 495 | |
| 496 | rc = dyncfg_default_response(rfe->result.wb, HTTP_RESP_OK, "applied to all template job"); |
| 497 | make_the_call_to_plugin = false; |
| 498 | } |
| 499 | else if (cmd == DYNCFG_CMD_SCHEMA) { |
| 500 | bool loaded = false; |
| 501 | if (df->type == DYNCFG_TYPE_JOB) { |
| 502 | if (df->template) |
| 503 | loaded = dyncfg_get_schema(string2str(df->template), rfe->result.wb); |
| 504 | } else |
| 505 | loaded = dyncfg_get_schema(id, rfe->result.wb); |
| 506 | |
| 507 | if (loaded) { |
| 508 | rfe->result.wb->content_type = CT_APPLICATION_JSON; |
| 509 | rfe->result.wb->expires = now_realtime_sec(); |
| 510 | rc = HTTP_RESP_OK; |
| 511 | make_the_call_to_plugin = false; |
| 512 | } |
| 513 | } |
| 514 | } |
| 515 | |
| 516 | // 4. execute the command |
| 517 | |
| 518 | if(make_the_call_to_plugin) { |
| 519 | struct dyncfg_call *dc = callocz(1, sizeof(*dc)); |
| 520 | uuid_copy(dc->transaction.uuid, *rfe->transaction); |
| 521 | dc->function = strdupz(rfe->function); |
| 522 | dc->id = strdupz(id); |
| 523 | dc->source = rfe->source ? strdupz(rfe->source) : NULL; |
| 524 | dc->add_name = (add_name) ? strdupz(add_name) : NULL; |
| 525 | dc->cmd = cmd; |
| 526 | dc->result_cb = rfe->result.cb; |
| 527 | dc->result_cb_data = rfe->result.data; |
| 528 | dc->payload = buffer_dup(rfe->payload); |
| 529 | dc->from_dyncfg_echo = called_from_dyncfg_echo; |
| 530 | |
| 531 | rfe->result.cb = dyncfg_function_intercept_result_cb; |
| 532 | rfe->result.data = dc; |
| 533 | |
| 534 | rc = df->execute_cb(rfe, df->execute_cb_data); |
| 535 | } |
| 536 | else if(rfe->result.cb) |
| 537 | rfe->result.cb(rfe->result.wb, rc, rfe->result.data); |
| 538 | |
| 539 | dictionary_acquired_item_release(dyncfg_globals.nodes, item); |
| 540 | return rc; |
| 541 | } |