dyn_conf.c 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. #include "dyn_conf.h"
  3. #define DYN_CONF_PATH_MAX (4096)
  4. #define DYN_CONF_DIR VARLIB_DIR "/dynconf"
  5. #define DYN_CONF_JOB_SCHEMA "job_schema"
  6. #define DYN_CONF_SCHEMA "schema"
  7. #define DYN_CONF_MODULE_LIST "modules"
  8. #define DYN_CONF_JOB_LIST "jobs"
  9. #define DYN_CONF_CFG_EXT ".cfg"
  10. void job_flags_wallkthrough(dyncfg_job_flg_t flags, void (*cb)(const char *str, void *data), void *data)
  11. {
  12. if (flags & JOB_FLG_PS_LOADED)
  13. cb("JOB_FLG_PS_LOADED", data);
  14. if (flags & JOB_FLG_PLUGIN_PUSHED)
  15. cb("JOB_FLG_PLUGIN_PUSHED", data);
  16. if (flags & JOB_FLG_STREAMING_PUSHED)
  17. cb("JOB_FLG_STREAMING_PUSHED", data);
  18. if (flags & JOB_FLG_USER_CREATED)
  19. cb("JOB_FLG_USER_CREATED", data);
  20. }
  21. struct deferred_cfg_send {
  22. DICTIONARY *plugins_dict;
  23. char *plugin_name;
  24. char *module_name;
  25. char *job_name;
  26. struct deferred_cfg_send *next;
  27. };
  28. bool dyncfg_shutdown = false;
  29. struct deferred_cfg_send *deferred_configs = NULL;
  30. pthread_mutex_t deferred_configs_lock = PTHREAD_MUTEX_INITIALIZER;
  31. pthread_cond_t deferred_configs_cond = PTHREAD_COND_INITIALIZER;
  32. static void deferred_config_free(struct deferred_cfg_send *dcs)
  33. {
  34. freez(dcs->plugin_name);
  35. freez(dcs->module_name);
  36. freez(dcs->job_name);
  37. freez(dcs);
  38. }
  39. static void deferred_config_push_back(DICTIONARY *plugins_dict, const char *plugin_name, const char *module_name, const char *job_name)
  40. {
  41. struct deferred_cfg_send *deferred = callocz(1, sizeof(struct deferred_cfg_send));
  42. deferred->plugin_name = strdupz(plugin_name);
  43. if (module_name != NULL) {
  44. deferred->module_name = strdupz(module_name);
  45. if (job_name != NULL)
  46. deferred->job_name = strdupz(job_name);
  47. }
  48. deferred->plugins_dict = plugins_dict;
  49. pthread_mutex_lock(&deferred_configs_lock);
  50. if (dyncfg_shutdown) {
  51. pthread_mutex_unlock(&deferred_configs_lock);
  52. deferred_config_free(deferred);
  53. return;
  54. }
  55. struct deferred_cfg_send *last = deferred_configs;
  56. if (last == NULL)
  57. deferred_configs = deferred;
  58. else {
  59. while (last->next != NULL)
  60. last = last->next;
  61. last->next = deferred;
  62. }
  63. pthread_cond_signal(&deferred_configs_cond);
  64. pthread_mutex_unlock(&deferred_configs_lock);
  65. }
  66. static void deferred_configs_unlock()
  67. {
  68. dyncfg_shutdown = true;
  69. // if we get cancelled in pthread_cond_wait
  70. // we will arrive at cancelled cleanup handler
  71. // with mutex locked we need to unlock it
  72. pthread_mutex_unlock(&deferred_configs_lock);
  73. }
  74. static struct deferred_cfg_send *deferred_config_pop(void *ptr)
  75. {
  76. pthread_mutex_lock(&deferred_configs_lock);
  77. while (deferred_configs == NULL) {
  78. netdata_thread_cleanup_push(deferred_configs_unlock, ptr);
  79. pthread_cond_wait(&deferred_configs_cond, &deferred_configs_lock);
  80. netdata_thread_cleanup_pop(0);
  81. }
  82. struct deferred_cfg_send *deferred = deferred_configs;
  83. deferred_configs = deferred_configs->next;
  84. pthread_mutex_unlock(&deferred_configs_lock);
  85. return deferred;
  86. }
  87. static int _get_list_of_plugins_json_cb(const DICTIONARY_ITEM *item, void *entry, void *data)
  88. {
  89. UNUSED(item);
  90. json_object *obj = (json_object *)data;
  91. struct configurable_plugin *plugin = (struct configurable_plugin *)entry;
  92. json_object *plugin_name = json_object_new_string(plugin->name);
  93. json_object_array_add(obj, plugin_name);
  94. return 0;
  95. }
  96. json_object *get_list_of_plugins_json(DICTIONARY *plugins_dict)
  97. {
  98. json_object *obj = json_object_new_array();
  99. dictionary_walkthrough_read(plugins_dict, _get_list_of_plugins_json_cb, obj);
  100. return obj;
  101. }
  102. static int _get_list_of_modules_json_cb(const DICTIONARY_ITEM *item, void *entry, void *data)
  103. {
  104. UNUSED(item);
  105. json_object *obj = (json_object *)data;
  106. struct module *module = (struct module *)entry;
  107. json_object *json_module = json_object_new_object();
  108. json_object *json_item = json_object_new_string(module->name);
  109. json_object_object_add(json_module, "name", json_item);
  110. const char *module_type = module_type2str(module->type);
  111. json_item = json_object_new_string(module_type);
  112. json_object_object_add(json_module, "type", json_item);
  113. json_object_array_add(obj, json_module);
  114. return 0;
  115. }
  116. json_object *get_list_of_modules_json(struct configurable_plugin *plugin)
  117. {
  118. json_object *obj = json_object_new_array();
  119. pthread_mutex_lock(&plugin->lock);
  120. dictionary_walkthrough_read(plugin->modules, _get_list_of_modules_json_cb, obj);
  121. pthread_mutex_unlock(&plugin->lock);
  122. return obj;
  123. }
  124. const char *job_status2str(enum job_status status)
  125. {
  126. switch (status) {
  127. case JOB_STATUS_UNKNOWN:
  128. return "unknown";
  129. case JOB_STATUS_STOPPED:
  130. return "stopped";
  131. case JOB_STATUS_RUNNING:
  132. return "running";
  133. case JOB_STATUS_ERROR:
  134. return "error";
  135. default:
  136. return "unknown";
  137. }
  138. }
  139. static void _job_flags2str_cb(const char *str, void *data)
  140. {
  141. json_object *json_item = json_object_new_string(str);
  142. json_object_array_add((json_object *)data, json_item);
  143. }
  144. json_object *job2json(struct job *job) {
  145. json_object *json_job = json_object_new_object();
  146. json_object *json_item = json_object_new_string(job->name);
  147. json_object_object_add(json_job, "name", json_item);
  148. json_item = json_object_new_string(job_type2str(job->type));
  149. json_object_object_add(json_job, "type", json_item);
  150. netdata_mutex_lock(&job->lock);
  151. json_item = json_object_new_string(job_status2str(job->status));
  152. json_object_object_add(json_job, "status", json_item);
  153. json_item = json_object_new_int(job->state);
  154. json_object_object_add(json_job, "state", json_item);
  155. json_item = job->reason == NULL ? NULL : json_object_new_string(job->reason);
  156. json_object_object_add(json_job, "reason", json_item);
  157. int64_t last_state_update_s = job->last_state_update / USEC_PER_SEC;
  158. int64_t last_state_update_us = job->last_state_update % USEC_PER_SEC;
  159. json_item = json_object_new_int64(last_state_update_s);
  160. json_object_object_add(json_job, "last_state_update_s", json_item);
  161. json_item = json_object_new_int64(last_state_update_us);
  162. json_object_object_add(json_job, "last_state_update_us", json_item);
  163. json_item = json_object_new_array();
  164. job_flags_wallkthrough(job->flags, _job_flags2str_cb, json_item);
  165. json_object_object_add(json_job, "flags", json_item);
  166. netdata_mutex_unlock(&job->lock);
  167. return json_job;
  168. }
  169. static int _get_list_of_jobs_json_cb(const DICTIONARY_ITEM *item, void *entry, void *data)
  170. {
  171. UNUSED(item);
  172. json_object *obj = (json_object *)data;
  173. json_object *json_job = job2json((struct job *)entry);
  174. json_object_array_add(obj, json_job);
  175. return 0;
  176. }
  177. json_object *get_list_of_jobs_json(struct module *module)
  178. {
  179. json_object *obj = json_object_new_array();
  180. pthread_mutex_lock(&module->lock);
  181. dictionary_walkthrough_read(module->jobs, _get_list_of_jobs_json_cb, obj);
  182. pthread_mutex_unlock(&module->lock);
  183. return obj;
  184. }
  185. struct job *get_job_by_name(struct module *module, const char *job_name)
  186. {
  187. return dictionary_get(module->jobs, job_name);
  188. }
  189. void unlink_job(const char *plugin_name, const char *module_name, const char *job_name)
  190. {
  191. // as we are going to do unlink here we better make sure we have all to build proper path
  192. if (unlikely(job_name == NULL || module_name == NULL || plugin_name == NULL))
  193. return;
  194. BUFFER *buffer = buffer_create(DYN_CONF_PATH_MAX, NULL);
  195. buffer_sprintf(buffer, DYN_CONF_DIR "/%s/%s/%s" DYN_CONF_CFG_EXT, plugin_name, module_name, job_name);
  196. if (unlink(buffer_tostring(buffer)))
  197. netdata_log_error("Cannot remove file %s", buffer_tostring(buffer));
  198. buffer_free(buffer);
  199. }
  200. void delete_job(struct configurable_plugin *plugin, const char *module_name, const char *job_name)
  201. {
  202. struct module *module = get_module_by_name(plugin, module_name);
  203. if (module == NULL) {
  204. error_report("DYNCFG module \"%s\" not found", module_name);
  205. return;
  206. }
  207. struct job *job_item = get_job_by_name(module, job_name);
  208. if (job_item == NULL) {
  209. error_report("DYNCFG job \"%s\" not found", job_name);
  210. return;
  211. }
  212. dictionary_del(module->jobs, job_name);
  213. }
  214. void delete_job_pname(DICTIONARY *plugins_dict, const char *plugin_name, const char *module_name, const char *job_name)
  215. {
  216. const DICTIONARY_ITEM *plugin_item = dictionary_get_and_acquire_item(plugins_dict, plugin_name);
  217. if (plugin_item == NULL) {
  218. error_report("DYNCFG plugin \"%s\" not found", plugin_name);
  219. return;
  220. }
  221. struct configurable_plugin *plugin = dictionary_acquired_item_value(plugin_item);
  222. delete_job(plugin, module_name, job_name);
  223. dictionary_acquired_item_release(plugins_dict, plugin_item);
  224. }
  225. int remove_job(struct module *module, struct job *job)
  226. {
  227. enum set_config_result rc = module->delete_job_cb(module->job_config_cb_usr_ctx, module->plugin->name, module->name, job->name);
  228. if (rc != SET_CONFIG_ACCEPTED) {
  229. error_report("DYNCFG module \"%s\" rejected delete job for \"%s\"", module->name, job->name);
  230. return 0;
  231. }
  232. return 1;
  233. }
  234. struct module *get_module_by_name(struct configurable_plugin *plugin, const char *module_name)
  235. {
  236. return dictionary_get(plugin->modules, module_name);
  237. }
  238. inline struct configurable_plugin *get_plugin_by_name(DICTIONARY *plugins_dict, const char *name)
  239. {
  240. return dictionary_get(plugins_dict, name);
  241. }
  242. static int store_config(const char *module_name, const char *submodule_name, const char *cfg_idx, dyncfg_config_t cfg)
  243. {
  244. BUFFER *filename = buffer_create(DYN_CONF_PATH_MAX, NULL);
  245. buffer_sprintf(filename, DYN_CONF_DIR "/%s", module_name);
  246. if (mkdir(buffer_tostring(filename), 0755) == -1) {
  247. if (errno != EEXIST) {
  248. netdata_log_error("DYNCFG store_config: failed to create module directory %s", buffer_tostring(filename));
  249. buffer_free(filename);
  250. return 1;
  251. }
  252. }
  253. if (submodule_name != NULL) {
  254. buffer_sprintf(filename, "/%s", submodule_name);
  255. if (mkdir(buffer_tostring(filename), 0755) == -1) {
  256. if (errno != EEXIST) {
  257. netdata_log_error("DYNCFG store_config: failed to create submodule directory %s", buffer_tostring(filename));
  258. buffer_free(filename);
  259. return 1;
  260. }
  261. }
  262. }
  263. if (cfg_idx != NULL)
  264. buffer_sprintf(filename, "/%s", cfg_idx);
  265. buffer_strcat(filename, DYN_CONF_CFG_EXT);
  266. error_report("DYNCFG store_config: %s", buffer_tostring(filename));
  267. //write to file
  268. FILE *f = fopen(buffer_tostring(filename), "w");
  269. if (f == NULL) {
  270. error_report("DYNCFG store_config: failed to open %s for writing", buffer_tostring(filename));
  271. buffer_free(filename);
  272. return 1;
  273. }
  274. fwrite(cfg.data, cfg.data_size, 1, f);
  275. fclose(f);
  276. buffer_free(filename);
  277. return 0;
  278. }
  279. #ifdef NETDATA_DEV_MODE
  280. #define netdata_dev_fatal(...) fatal(__VA_ARGS__)
  281. #else
  282. #define netdata_dev_fatal(...) error_report(__VA_ARGS__)
  283. #endif
  284. void dyn_conf_store_config(const char *function, const char *payload, struct configurable_plugin *plugin) {
  285. dyncfg_config_t config = {
  286. .data = (char*)payload,
  287. .data_size = strlen(payload)
  288. };
  289. char *fnc = strdupz(function);
  290. // split fnc to words
  291. char *words[DYNCFG_MAX_WORDS];
  292. size_t words_c = quoted_strings_splitter(fnc, words, DYNCFG_MAX_WORDS, isspace_map_pluginsd);
  293. const char *fnc_name = get_word(words, words_c, 0);
  294. if (fnc_name == NULL) {
  295. error_report("Function name expected \"%s\"", function);
  296. goto CLEANUP;
  297. }
  298. if (strncmp(fnc_name, FUNCTION_NAME_SET_PLUGIN_CONFIG, strlen(FUNCTION_NAME_SET_PLUGIN_CONFIG)) == 0) {
  299. store_config(plugin->name, NULL, NULL, config);
  300. goto CLEANUP;
  301. }
  302. if (words_c < 2) {
  303. error_report("Module name expected \"%s\"", function);
  304. goto CLEANUP;
  305. }
  306. const char *module_name = get_word(words, words_c, 1);
  307. if (strncmp(fnc_name, FUNCTION_NAME_SET_MODULE_CONFIG, strlen(FUNCTION_NAME_SET_MODULE_CONFIG)) == 0) {
  308. store_config(plugin->name, module_name, NULL, config);
  309. goto CLEANUP;
  310. }
  311. if (words_c < 3) {
  312. error_report("Job name expected \"%s\"", function);
  313. goto CLEANUP;
  314. }
  315. const char *job_name = get_word(words, words_c, 2);
  316. if (strncmp(fnc_name, FUNCTION_NAME_SET_JOB_CONFIG, strlen(FUNCTION_NAME_SET_JOB_CONFIG)) == 0) {
  317. store_config(plugin->name, module_name, job_name, config);
  318. goto CLEANUP;
  319. }
  320. netdata_dev_fatal("Unknown function \"%s\"", function);
  321. CLEANUP:
  322. freez(fnc);
  323. }
  324. dyncfg_config_t load_config(const char *plugin_name, const char *module_name, const char *job_id)
  325. {
  326. BUFFER *filename = buffer_create(DYN_CONF_PATH_MAX, NULL);
  327. buffer_sprintf(filename, DYN_CONF_DIR "/%s", plugin_name);
  328. if (module_name != NULL)
  329. buffer_sprintf(filename, "/%s", module_name);
  330. if (job_id != NULL)
  331. buffer_sprintf(filename, "/%s", job_id);
  332. buffer_strcat(filename, DYN_CONF_CFG_EXT);
  333. dyncfg_config_t config;
  334. long bytes;
  335. config.data = read_by_filename(buffer_tostring(filename), &bytes);
  336. if (config.data == NULL)
  337. error_report("DYNCFG load_config: failed to load config from %s", buffer_tostring(filename));
  338. config.data_size = bytes;
  339. buffer_free(filename);
  340. return config;
  341. }
  342. char *set_plugin_config(struct configurable_plugin *plugin, dyncfg_config_t cfg)
  343. {
  344. enum set_config_result rc = plugin->set_config_cb(plugin->cb_usr_ctx, plugin->name, &cfg);
  345. if (rc != SET_CONFIG_ACCEPTED) {
  346. error_report("DYNCFG plugin \"%s\" rejected config", plugin->name);
  347. return "plugin rejected config";
  348. }
  349. return NULL;
  350. }
  351. static char *set_module_config(struct module *mod, dyncfg_config_t cfg)
  352. {
  353. struct configurable_plugin *plugin = mod->plugin;
  354. enum set_config_result rc = mod->set_config_cb(mod->config_cb_usr_ctx, plugin->name, mod->name, &cfg);
  355. if (rc != SET_CONFIG_ACCEPTED) {
  356. error_report("DYNCFG module \"%s\" rejected config", plugin->name);
  357. return "module rejected config";
  358. }
  359. return NULL;
  360. }
  361. struct job *job_new(const char *job_id)
  362. {
  363. struct job *job = callocz(1, sizeof(struct job));
  364. job->state = JOB_STATUS_UNKNOWN;
  365. job->last_state_update = now_realtime_usec();
  366. job->name = strdupz(job_id);
  367. netdata_mutex_init(&job->lock);
  368. return job;
  369. }
  370. static inline void job_del(struct job *job)
  371. {
  372. netdata_mutex_destroy(&job->lock);
  373. freez(job->reason);
  374. freez((void*)job->name);
  375. freez(job);
  376. }
  377. void job_del_cb(const DICTIONARY_ITEM *item, void *value, void *data)
  378. {
  379. UNUSED(item);
  380. UNUSED(data);
  381. job_del((struct job *)value);
  382. }
  383. void module_del_cb(const DICTIONARY_ITEM *item, void *value, void *data)
  384. {
  385. UNUSED(item);
  386. UNUSED(data);
  387. struct module *mod = (struct module *)value;
  388. dictionary_destroy(mod->jobs);
  389. freez(mod->name);
  390. freez(mod);
  391. }
  392. const DICTIONARY_ITEM *register_plugin(DICTIONARY *plugins_dict, struct configurable_plugin *plugin, bool localhost)
  393. {
  394. if (get_plugin_by_name(plugins_dict, plugin->name) != NULL) {
  395. error_report("DYNCFG plugin \"%s\" already registered", plugin->name);
  396. return NULL;
  397. }
  398. if (plugin->set_config_cb == NULL) {
  399. error_report("DYNCFG plugin \"%s\" has no set_config_cb", plugin->name);
  400. return NULL;
  401. }
  402. pthread_mutex_init(&plugin->lock, NULL);
  403. plugin->modules = dictionary_create(DICT_OPTION_VALUE_LINK_DONT_CLONE);
  404. dictionary_register_delete_callback(plugin->modules, module_del_cb, NULL);
  405. if (localhost)
  406. deferred_config_push_back(plugins_dict, plugin->name, NULL, NULL);
  407. dictionary_set(plugins_dict, plugin->name, plugin, sizeof(plugin));
  408. // the plugin keeps the pointer to the dictionary item, so we need to acquire it
  409. return dictionary_get_and_acquire_item(plugins_dict, plugin->name);
  410. }
  411. void unregister_plugin(DICTIONARY *plugins_dict, const DICTIONARY_ITEM *plugin)
  412. {
  413. struct configurable_plugin *plug = dictionary_acquired_item_value(plugin);
  414. dictionary_acquired_item_release(plugins_dict, plugin);
  415. dictionary_del(plugins_dict, plug->name);
  416. }
  417. int register_module(DICTIONARY *plugins_dict, struct configurable_plugin *plugin, struct module *module, bool localhost)
  418. {
  419. if (get_module_by_name(plugin, module->name) != NULL) {
  420. error_report("DYNCFG module \"%s\" already registered", module->name);
  421. return 1;
  422. }
  423. pthread_mutex_init(&module->lock, NULL);
  424. if (localhost)
  425. deferred_config_push_back(plugins_dict, plugin->name, module->name, NULL);
  426. module->plugin = plugin;
  427. if (module->type == MOD_TYPE_ARRAY) {
  428. module->jobs = dictionary_create(DICT_OPTION_VALUE_LINK_DONT_CLONE);
  429. dictionary_register_delete_callback(module->jobs, job_del_cb, NULL);
  430. if (localhost) {
  431. // load all jobs from disk
  432. BUFFER *path = buffer_create(DYN_CONF_PATH_MAX, NULL);
  433. buffer_sprintf(path, "%s/%s/%s", DYN_CONF_DIR, plugin->name, module->name);
  434. DIR *dir = opendir(buffer_tostring(path));
  435. if (dir != NULL) {
  436. struct dirent *ent;
  437. while ((ent = readdir(dir)) != NULL) {
  438. if (ent->d_name[0] == '.')
  439. continue;
  440. if (ent->d_type != DT_REG)
  441. continue;
  442. size_t len = strnlen(ent->d_name, NAME_MAX);
  443. if (len <= strlen(DYN_CONF_CFG_EXT))
  444. continue;
  445. if (strcmp(ent->d_name + len - strlen(DYN_CONF_CFG_EXT), DYN_CONF_CFG_EXT) != 0)
  446. continue;
  447. ent->d_name[len - strlen(DYN_CONF_CFG_EXT)] = '\0';
  448. struct job *job = job_new(ent->d_name);
  449. job->module = module;
  450. job->flags = JOB_FLG_PS_LOADED;
  451. job->type = JOB_TYPE_USER;
  452. dictionary_set(module->jobs, job->name, job, sizeof(job));
  453. deferred_config_push_back(plugins_dict, plugin->name, module->name, ent->d_name);
  454. }
  455. closedir(dir);
  456. }
  457. buffer_free(path);
  458. }
  459. }
  460. dictionary_set(plugin->modules, module->name, module, sizeof(module));
  461. return 0;
  462. }
  463. int register_job(DICTIONARY *plugins_dict, const char *plugin_name, const char *module_name, const char *job_name, enum job_type job_type, dyncfg_job_flg_t flags, int ignore_existing)
  464. {
  465. int rc = 1;
  466. const DICTIONARY_ITEM *plugin_item = dictionary_get_and_acquire_item(plugins_dict, plugin_name);
  467. if (plugin_item == NULL) {
  468. error_report("plugin \"%s\" not registered", plugin_name);
  469. return rc;
  470. }
  471. struct configurable_plugin *plugin = dictionary_acquired_item_value(plugin_item);
  472. struct module *mod = get_module_by_name(plugin, module_name);
  473. if (mod == NULL) {
  474. error_report("module \"%s\" not registered", module_name);
  475. goto ERR_EXIT;
  476. }
  477. if (mod->type != MOD_TYPE_ARRAY) {
  478. error_report("module \"%s\" is not an array", module_name);
  479. goto ERR_EXIT;
  480. }
  481. if (get_job_by_name(mod, job_name) != NULL) {
  482. if (!ignore_existing)
  483. error_report("job \"%s\" already registered", job_name);
  484. goto ERR_EXIT;
  485. }
  486. struct job *job = job_new(job_name);
  487. job->module = mod;
  488. job->flags = flags;
  489. job->type = job_type;
  490. dictionary_set(mod->jobs, job->name, job, sizeof(job));
  491. rc = 0;
  492. ERR_EXIT:
  493. dictionary_acquired_item_release(plugins_dict, plugin_item);
  494. return rc;
  495. }
  496. void freez_dyncfg(void *ptr) {
  497. freez(ptr);
  498. }
  499. #ifdef NETDATA_TEST_DYNCFG
  500. static void handle_dyncfg_root(DICTIONARY *plugins_dict, struct uni_http_response *resp, int method)
  501. {
  502. if (method != HTTP_METHOD_GET) {
  503. resp->content = "method not allowed";
  504. resp->content_length = strlen(resp->content);
  505. resp->status = HTTP_RESP_METHOD_NOT_ALLOWED;
  506. return;
  507. }
  508. json_object *obj = get_list_of_plugins_json(plugins_dict);
  509. json_object *wrapper = json_object_new_object();
  510. json_object_object_add(wrapper, "configurable_plugins", obj);
  511. resp->content = strdupz(json_object_to_json_string_ext(wrapper, JSON_C_TO_STRING_PRETTY));
  512. json_object_put(wrapper);
  513. resp->status = HTTP_RESP_OK;
  514. resp->content_type = CT_APPLICATION_JSON;
  515. resp->content_free = freez_dyncfg;
  516. resp->content_length = strlen(resp->content);
  517. }
  518. static void handle_plugin_root(struct uni_http_response *resp, int method, struct configurable_plugin *plugin, void *post_payload, size_t post_payload_size)
  519. {
  520. switch(method) {
  521. case HTTP_METHOD_GET:
  522. {
  523. dyncfg_config_t cfg = plugin->get_config_cb(plugin->cb_usr_ctx, plugin->name);
  524. resp->content = mallocz(cfg.data_size);
  525. memcpy(resp->content, cfg.data, cfg.data_size);
  526. resp->status = HTTP_RESP_OK;
  527. resp->content_free = freez_dyncfg;
  528. resp->content_length = cfg.data_size;
  529. return;
  530. }
  531. case HTTP_METHOD_PUT:
  532. {
  533. char *response;
  534. if (post_payload == NULL) {
  535. resp->content = "no payload";
  536. resp->content_length = strlen(resp->content);
  537. resp->status = HTTP_RESP_BAD_REQUEST;
  538. return;
  539. }
  540. dyncfg_config_t cont = {
  541. .data = post_payload,
  542. .data_size = post_payload_size
  543. };
  544. response = set_plugin_config(plugin, cont);
  545. if (response == NULL) {
  546. resp->status = HTTP_RESP_OK;
  547. resp->content = "OK";
  548. resp->content_length = strlen(resp->content);
  549. } else {
  550. resp->status = HTTP_RESP_BAD_REQUEST;
  551. resp->content = response;
  552. resp->content_length = strlen(resp->content);
  553. }
  554. return;
  555. }
  556. default:
  557. resp->content = "method not allowed";
  558. resp->content_length = strlen(resp->content);
  559. resp->status = HTTP_RESP_METHOD_NOT_ALLOWED;
  560. return;
  561. }
  562. }
  563. #endif
  564. void handle_module_root(struct uni_http_response *resp, int method, struct configurable_plugin *plugin, const char *module, void *post_payload, size_t post_payload_size)
  565. {
  566. if (strncmp(module, DYN_CONF_SCHEMA, sizeof(DYN_CONF_SCHEMA)) == 0) {
  567. dyncfg_config_t cfg = plugin->get_config_schema_cb(plugin->cb_usr_ctx, plugin->name);
  568. resp->content = mallocz(cfg.data_size);
  569. memcpy(resp->content, cfg.data, cfg.data_size);
  570. resp->status = HTTP_RESP_OK;
  571. resp->content_free = freez_dyncfg;
  572. resp->content_length = cfg.data_size;
  573. return;
  574. }
  575. if (strncmp(module, DYN_CONF_MODULE_LIST, sizeof(DYN_CONF_MODULE_LIST)) == 0) {
  576. if (method != HTTP_METHOD_GET) {
  577. resp->content = "method not allowed (only GET)";
  578. resp->content_length = strlen(resp->content);
  579. resp->status = HTTP_RESP_METHOD_NOT_ALLOWED;
  580. return;
  581. }
  582. json_object *obj = get_list_of_modules_json(plugin);
  583. json_object *wrapper = json_object_new_object();
  584. json_object_object_add(wrapper, "modules", obj);
  585. resp->content = strdupz(json_object_to_json_string_ext(wrapper, JSON_C_TO_STRING_PRETTY));
  586. json_object_put(wrapper);
  587. resp->status = HTTP_RESP_OK;
  588. resp->content_type = CT_APPLICATION_JSON;
  589. resp->content_free = freez_dyncfg;
  590. resp->content_length = strlen(resp->content);
  591. return;
  592. }
  593. struct module *mod = get_module_by_name(plugin, module);
  594. if (mod == NULL) {
  595. resp->content = "module not found";
  596. resp->content_length = strlen(resp->content);
  597. resp->status = HTTP_RESP_NOT_FOUND;
  598. return;
  599. }
  600. if (method == HTTP_METHOD_GET) {
  601. dyncfg_config_t cfg = mod->get_config_cb(mod->config_cb_usr_ctx, plugin->name, mod->name);
  602. resp->content = mallocz(cfg.data_size);
  603. memcpy(resp->content, cfg.data, cfg.data_size);
  604. resp->status = HTTP_RESP_OK;
  605. resp->content_free = freez_dyncfg;
  606. resp->content_length = cfg.data_size;
  607. return;
  608. } else if (method == HTTP_METHOD_PUT) {
  609. char *response;
  610. if (post_payload == NULL) {
  611. resp->content = "no payload";
  612. resp->content_length = strlen(resp->content);
  613. resp->status = HTTP_RESP_BAD_REQUEST;
  614. return;
  615. }
  616. dyncfg_config_t cont = {
  617. .data = post_payload,
  618. .data_size = post_payload_size
  619. };
  620. response = set_module_config(mod, cont);
  621. if (response == NULL) {
  622. resp->status = HTTP_RESP_OK;
  623. resp->content = "OK";
  624. resp->content_length = strlen(resp->content);
  625. } else {
  626. resp->status = HTTP_RESP_BAD_REQUEST;
  627. resp->content = response;
  628. resp->content_length = strlen(resp->content);
  629. }
  630. return;
  631. }
  632. resp->content = "method not allowed";
  633. resp->content_length = strlen(resp->content);
  634. resp->status = HTTP_RESP_METHOD_NOT_ALLOWED;
  635. }
  636. static inline void _handle_job_root(struct uni_http_response *resp, int method, struct module *mod, const char *job_id, void *post_payload, size_t post_payload_size, struct job *job)
  637. {
  638. if (method == HTTP_METHOD_POST) {
  639. if (job != NULL) {
  640. resp->content = "can't POST, job already exists (use PUT to update?)";
  641. resp->content_length = strlen(resp->content);
  642. resp->status = HTTP_RESP_BAD_REQUEST;
  643. return;
  644. }
  645. if (post_payload == NULL) {
  646. resp->content = "no payload";
  647. resp->content_length = strlen(resp->content);
  648. resp->status = HTTP_RESP_BAD_REQUEST;
  649. return;
  650. }
  651. dyncfg_config_t cont = {
  652. .data = post_payload,
  653. .data_size = post_payload_size
  654. };
  655. if (mod->set_job_config_cb(mod->job_config_cb_usr_ctx, mod->plugin->name, mod->name, job_id, &cont)) {
  656. resp->content = "failed to add job";
  657. resp->content_length = strlen(resp->content);
  658. resp->status = HTTP_RESP_INTERNAL_SERVER_ERROR;
  659. return;
  660. }
  661. resp->status = HTTP_RESP_OK;
  662. resp->content = "OK";
  663. resp->content_length = strlen(resp->content);
  664. return;
  665. }
  666. if (job == NULL) {
  667. resp->content = "job not found";
  668. resp->content_length = strlen(resp->content);
  669. resp->status = HTTP_RESP_NOT_FOUND;
  670. return;
  671. }
  672. switch (method) {
  673. case HTTP_METHOD_GET:
  674. {
  675. dyncfg_config_t cfg = mod->get_job_config_cb(mod->job_config_cb_usr_ctx, mod->plugin->name, mod->name, job->name);
  676. resp->content = mallocz(cfg.data_size);
  677. memcpy(resp->content, cfg.data, cfg.data_size);
  678. resp->status = HTTP_RESP_OK;
  679. resp->content_free = freez_dyncfg;
  680. resp->content_length = cfg.data_size;
  681. return;
  682. }
  683. case HTTP_METHOD_PUT:
  684. {
  685. if (post_payload == NULL) {
  686. resp->content = "missing payload";
  687. resp->content_length = strlen(resp->content);
  688. resp->status = HTTP_RESP_BAD_REQUEST;
  689. return;
  690. }
  691. dyncfg_config_t cont = {
  692. .data = post_payload,
  693. .data_size = post_payload_size
  694. };
  695. if (mod->set_job_config_cb(mod->job_config_cb_usr_ctx, mod->plugin->name, mod->name, job->name, &cont) != SET_CONFIG_ACCEPTED) {
  696. error_report("DYNCFG module \"%s\" rejected config for job \"%s\"", mod->name, job->name);
  697. resp->content = "failed to set job config";
  698. resp->content_length = strlen(resp->content);
  699. resp->status = HTTP_RESP_INTERNAL_SERVER_ERROR;
  700. return;
  701. }
  702. resp->status = HTTP_RESP_OK;
  703. resp->content = "OK";
  704. resp->content_length = strlen(resp->content);
  705. return;
  706. }
  707. case HTTP_METHOD_DELETE:
  708. {
  709. if (!remove_job(mod, job)) {
  710. resp->content = "failed to remove job";
  711. resp->content_length = strlen(resp->content);
  712. resp->status = HTTP_RESP_INTERNAL_SERVER_ERROR;
  713. return;
  714. }
  715. resp->status = HTTP_RESP_OK;
  716. resp->content = "OK";
  717. resp->content_length = strlen(resp->content);
  718. return;
  719. }
  720. default:
  721. resp->content = "method not allowed (only GET, PUT, DELETE)";
  722. resp->content_length = strlen(resp->content);
  723. resp->status = HTTP_RESP_METHOD_NOT_ALLOWED;
  724. return;
  725. }
  726. }
  727. void handle_job_root(struct uni_http_response *resp, int method, struct module *mod, const char *job_id, void *post_payload, size_t post_payload_size)
  728. {
  729. if (strncmp(job_id, DYN_CONF_SCHEMA, sizeof(DYN_CONF_SCHEMA)) == 0) {
  730. dyncfg_config_t cfg = mod->get_config_schema_cb(mod->config_cb_usr_ctx, mod->plugin->name, mod->name);
  731. resp->content = mallocz(cfg.data_size);
  732. memcpy(resp->content, cfg.data, cfg.data_size);
  733. resp->status = HTTP_RESP_OK;
  734. resp->content_free = freez_dyncfg;
  735. resp->content_length = cfg.data_size;
  736. return;
  737. }
  738. if (strncmp(job_id, DYN_CONF_JOB_SCHEMA, sizeof(DYN_CONF_JOB_SCHEMA)) == 0) {
  739. dyncfg_config_t cfg = mod->get_job_config_schema_cb(mod->job_config_cb_usr_ctx, mod->plugin->name, mod->name);
  740. resp->content = mallocz(cfg.data_size);
  741. memcpy(resp->content, cfg.data, cfg.data_size);
  742. resp->status = HTTP_RESP_OK;
  743. resp->content_free = freez_dyncfg;
  744. resp->content_length = cfg.data_size;
  745. return;
  746. }
  747. if (strncmp(job_id, DYN_CONF_JOB_LIST, sizeof(DYN_CONF_JOB_LIST)) == 0) {
  748. if (mod->type != MOD_TYPE_ARRAY) {
  749. resp->content = "module type is not job_array (can't get the list of jobs)";
  750. resp->content_length = strlen(resp->content);
  751. resp->status = HTTP_RESP_NOT_FOUND;
  752. return;
  753. }
  754. if (method != HTTP_METHOD_GET) {
  755. resp->content = "method not allowed (only GET)";
  756. resp->content_length = strlen(resp->content);
  757. resp->status = HTTP_RESP_METHOD_NOT_ALLOWED;
  758. return;
  759. }
  760. json_object *obj = get_list_of_jobs_json(mod);
  761. json_object *wrapper = json_object_new_object();
  762. json_object_object_add(wrapper, "jobs", obj);
  763. resp->content = strdupz(json_object_to_json_string_ext(wrapper, JSON_C_TO_STRING_PRETTY));
  764. json_object_put(wrapper);
  765. resp->status = HTTP_RESP_OK;
  766. resp->content_type = CT_APPLICATION_JSON;
  767. resp->content_free = freez_dyncfg;
  768. resp->content_length = strlen(resp->content);
  769. return;
  770. }
  771. const DICTIONARY_ITEM *job_item = dictionary_get_and_acquire_item(mod->jobs, job_id);
  772. struct job *job = dictionary_acquired_item_value(job_item);
  773. _handle_job_root(resp, method, mod, job_id, post_payload, post_payload_size, job);
  774. dictionary_acquired_item_release(mod->jobs, job_item);
  775. }
  776. struct uni_http_response dyn_conf_process_http_request(
  777. DICTIONARY *plugins_dict __maybe_unused,
  778. int method __maybe_unused,
  779. const char *plugin __maybe_unused,
  780. const char *module __maybe_unused,
  781. const char *job_id __maybe_unused,
  782. void *post_payload __maybe_unused,
  783. size_t post_payload_size __maybe_unused)
  784. {
  785. struct uni_http_response resp = {
  786. .status = HTTP_RESP_INTERNAL_SERVER_ERROR,
  787. .content_type = CT_TEXT_PLAIN,
  788. .content = HTTP_RESP_INTERNAL_SERVER_ERROR_STR,
  789. .content_free = NULL,
  790. .content_length = 0
  791. };
  792. #ifndef NETDATA_TEST_DYNCFG
  793. resp.content = "DYNCFG is disabled (as it is for now developer only feature). This will be enabled by default when ready for technical preview.";
  794. resp.content_length = strlen(resp.content);
  795. resp.status = HTTP_RESP_PRECOND_FAIL;
  796. return resp;
  797. #else
  798. if (plugin == NULL) {
  799. handle_dyncfg_root(plugins_dict, &resp, method);
  800. return resp;
  801. }
  802. const DICTIONARY_ITEM *plugin_item = dictionary_get_and_acquire_item(plugins_dict, plugin);
  803. if (plugin_item == NULL) {
  804. resp.content = "plugin not found";
  805. resp.content_length = strlen(resp.content);
  806. resp.status = HTTP_RESP_NOT_FOUND;
  807. return resp;
  808. }
  809. struct configurable_plugin *plug = dictionary_acquired_item_value(plugin_item);
  810. if (module == NULL) {
  811. handle_plugin_root(&resp, method, plug, post_payload, post_payload_size);
  812. goto EXIT_PLUGIN;
  813. }
  814. if (job_id == NULL) {
  815. handle_module_root(&resp, method, plug, module, post_payload, post_payload_size);
  816. goto EXIT_PLUGIN;
  817. }
  818. // for modules we do not do get_and_acquire as modules are never removed (only together with the plugin)
  819. struct module *mod = get_module_by_name(plug, module);
  820. if (mod == NULL) {
  821. resp.content = "module not found";
  822. resp.content_length = strlen(resp.content);
  823. resp.status = HTTP_RESP_NOT_FOUND;
  824. goto EXIT_PLUGIN;
  825. }
  826. if (mod->type != MOD_TYPE_ARRAY) {
  827. resp.content = "400 - this module is not array type";
  828. resp.content_length = strlen(resp.content);
  829. resp.status = HTTP_RESP_BAD_REQUEST;
  830. goto EXIT_PLUGIN;
  831. }
  832. handle_job_root(&resp, method, mod, job_id, post_payload, post_payload_size);
  833. EXIT_PLUGIN:
  834. dictionary_acquired_item_release(plugins_dict, plugin_item);
  835. return resp;
  836. #endif
  837. }
  838. void plugin_del_cb(const DICTIONARY_ITEM *item, void *value, void *data)
  839. {
  840. UNUSED(item);
  841. UNUSED(data);
  842. struct configurable_plugin *plugin = (struct configurable_plugin *)value;
  843. dictionary_destroy(plugin->modules);
  844. freez(plugin->name);
  845. freez(plugin);
  846. }
  847. // on failure - return NULL - all unlocked, nothing acquired
  848. // on success - return pointer to job item - keep job and plugin acquired and locked!!!
  849. // for caller convenience (to prevent another lock and races)
  850. // caller is responsible to unlock the job and release it when not needed anymore
  851. // this also avoids dependency creep
  852. const DICTIONARY_ITEM *report_job_status_acq_lock(DICTIONARY *plugins_dict, const DICTIONARY_ITEM **plugin_acq_item, DICTIONARY **job_dict, const char *plugin_name, const char *module_name, const char *job_name, enum job_status status, int status_code, char *reason)
  853. {
  854. *plugin_acq_item = dictionary_get_and_acquire_item(plugins_dict, plugin_name);
  855. if (*plugin_acq_item == NULL) {
  856. netdata_log_error("plugin %s not found", plugin_name);
  857. return NULL;
  858. }
  859. struct configurable_plugin *plug = dictionary_acquired_item_value(*plugin_acq_item);
  860. struct module *mod = get_module_by_name(plug, module_name);
  861. if (mod == NULL) {
  862. netdata_log_error("module %s not found", module_name);
  863. dictionary_acquired_item_release(plugins_dict, *plugin_acq_item);
  864. return NULL;
  865. }
  866. if (mod->type != MOD_TYPE_ARRAY) {
  867. netdata_log_error("module %s is not array", module_name);
  868. dictionary_acquired_item_release(plugins_dict, *plugin_acq_item);
  869. return NULL;
  870. }
  871. *job_dict = mod->jobs;
  872. const DICTIONARY_ITEM *job_item = dictionary_get_and_acquire_item(mod->jobs, job_name);
  873. if (job_item == NULL) {
  874. netdata_log_error("job %s not found", job_name);
  875. dictionary_acquired_item_release(plugins_dict, *plugin_acq_item);
  876. return NULL;
  877. }
  878. struct job *job = dictionary_acquired_item_value(job_item);
  879. pthread_mutex_lock(&job->lock);
  880. job->status = status;
  881. job->state = status_code;
  882. if (job->reason != NULL) {
  883. freez(job->reason);
  884. }
  885. job->reason = reason != NULL ? strdupz(reason) : NULL; // reason is optional
  886. job->last_state_update = now_realtime_usec();
  887. job->dirty = true;
  888. // no unlock and acquired_item_release on success on purpose
  889. return job_item;
  890. }
  891. int dyn_conf_init(void)
  892. {
  893. if (mkdir(DYN_CONF_DIR, 0755) == -1) {
  894. if (errno != EEXIST) {
  895. netdata_log_error("failed to create directory for dynamic configuration");
  896. return 1;
  897. }
  898. }
  899. return 0;
  900. }
  901. static void dyncfg_cleanup(void *ptr) {
  902. struct netdata_static_thread *static_thread = (struct netdata_static_thread *) ptr;
  903. static_thread->enabled = NETDATA_MAIN_THREAD_EXITING;
  904. netdata_log_info("cleaning up...");
  905. pthread_mutex_lock(&deferred_configs_lock);
  906. dyncfg_shutdown = true;
  907. while (deferred_configs != NULL) {
  908. struct deferred_cfg_send *dcs = deferred_configs;
  909. deferred_configs = dcs->next;
  910. deferred_config_free(dcs);
  911. }
  912. pthread_mutex_unlock(&deferred_configs_lock);
  913. static_thread->enabled = NETDATA_MAIN_THREAD_EXITED;
  914. }
  915. void *dyncfg_main(void *ptr)
  916. {
  917. netdata_thread_cleanup_push(dyncfg_cleanup, ptr);
  918. while (!netdata_exit) {
  919. struct deferred_cfg_send *dcs = deferred_config_pop(ptr);
  920. DICTIONARY *plugins_dict = dcs->plugins_dict;
  921. #ifdef NETDATA_INTERNAL_CHECKS
  922. if (plugins_dict == NULL) {
  923. fatal("DYNCFG, plugins_dict is NULL");
  924. deferred_config_free(dcs);
  925. continue;
  926. }
  927. #endif
  928. const DICTIONARY_ITEM *plugin_item = dictionary_get_and_acquire_item(plugins_dict, dcs->plugin_name);
  929. if (plugin_item == NULL) {
  930. error_report("DYNCFG, plugin %s not found", dcs->plugin_name);
  931. deferred_config_free(dcs);
  932. continue;
  933. }
  934. struct configurable_plugin *plugin = dictionary_acquired_item_value(plugin_item);
  935. if (dcs->module_name == NULL) {
  936. dyncfg_config_t cfg = load_config(dcs->plugin_name, NULL, NULL);
  937. if (cfg.data != NULL) {
  938. plugin->set_config_cb(plugin->cb_usr_ctx, plugin->name, &cfg);
  939. freez(cfg.data);
  940. }
  941. } else if (dcs->job_name == NULL) {
  942. dyncfg_config_t cfg = load_config(dcs->plugin_name, dcs->module_name, NULL);
  943. if (cfg.data != NULL) {
  944. struct module *mod = get_module_by_name(plugin, dcs->module_name);
  945. mod->set_config_cb(mod->config_cb_usr_ctx, plugin->name, mod->name, &cfg);
  946. freez(cfg.data);
  947. }
  948. } else {
  949. dyncfg_config_t cfg = load_config(dcs->plugin_name, dcs->module_name, dcs->job_name);
  950. if (cfg.data != NULL) {
  951. struct module *mod = get_module_by_name(plugin, dcs->module_name);
  952. mod->set_job_config_cb(mod->job_config_cb_usr_ctx, plugin->name, mod->name, dcs->job_name, &cfg);
  953. freez(cfg.data);
  954. }
  955. }
  956. deferred_config_free(dcs);
  957. dictionary_acquired_item_release(plugins_dict, plugin_item);
  958. }
  959. netdata_thread_cleanup_pop(1);
  960. return NULL;
  961. }
  962. bool is_dyncfg_function(const char *function_name, uint8_t type) {
  963. // TODO add hash to speed things up
  964. if (type & (DYNCFG_FUNCTION_TYPE_GET | DYNCFG_FUNCTION_TYPE_REGULAR)) {
  965. if (strncmp(function_name, FUNCTION_NAME_GET_PLUGIN_CONFIG, strlen(FUNCTION_NAME_GET_PLUGIN_CONFIG)) == 0)
  966. return true;
  967. if (strncmp(function_name, FUNCTION_NAME_GET_PLUGIN_CONFIG_SCHEMA, strlen(FUNCTION_NAME_GET_PLUGIN_CONFIG_SCHEMA)) == 0)
  968. return true;
  969. if (strncmp(function_name, FUNCTION_NAME_GET_MODULE_CONFIG, strlen(FUNCTION_NAME_GET_MODULE_CONFIG)) == 0)
  970. return true;
  971. if (strncmp(function_name, FUNCTION_NAME_GET_MODULE_CONFIG_SCHEMA, strlen(FUNCTION_NAME_GET_MODULE_CONFIG_SCHEMA)) == 0)
  972. return true;
  973. if (strncmp(function_name, FUNCTION_NAME_GET_JOB_CONFIG, strlen(FUNCTION_NAME_GET_JOB_CONFIG)) == 0)
  974. return true;
  975. if (strncmp(function_name, FUNCTION_NAME_GET_JOB_CONFIG_SCHEMA, strlen(FUNCTION_NAME_GET_JOB_CONFIG_SCHEMA)) == 0)
  976. return true;
  977. }
  978. if (type & (DYNCFG_FUNCTION_TYPE_SET | DYNCFG_FUNCTION_TYPE_PAYLOAD)) {
  979. if (strncmp(function_name, FUNCTION_NAME_SET_PLUGIN_CONFIG, strlen(FUNCTION_NAME_SET_PLUGIN_CONFIG)) == 0)
  980. return true;
  981. if (strncmp(function_name, FUNCTION_NAME_SET_MODULE_CONFIG, strlen(FUNCTION_NAME_SET_MODULE_CONFIG)) == 0)
  982. return true;
  983. if (strncmp(function_name, FUNCTION_NAME_SET_JOB_CONFIG, strlen(FUNCTION_NAME_SET_JOB_CONFIG)) == 0)
  984. return true;
  985. }
  986. if (type & (DYNCFG_FUNCTION_TYPE_DELETE | DYNCFG_FUNCTION_TYPE_REGULAR)) {
  987. if (strncmp(function_name, FUNCTION_NAME_DELETE_JOB, strlen(FUNCTION_NAME_DELETE_JOB)) == 0)
  988. return true;
  989. }
  990. return false;
  991. }