@cryptotaxi247 / netdata-1 / commits / 561f74cbd

support multiple plugin directories; fixes #2009

Costa Tsaousis (ktsaou) committed Oct 19, 2017 at 22:50 UTC 561f74cbd9410151d3b34dcd27c137177471750d
4 files changed +137 -85
src/common.h
+1
@@ -243,6 +243,7 @@ typedef enum rrdcalc_status {
243 extern char *netdata_configured_hostname;
244 extern char *netdata_configured_config_dir;
245 extern char *netdata_configured_log_dir;
246 +extern char *netdata_configured_plugins_dir_base;
247 extern char *netdata_configured_plugins_dir;
248 extern char *netdata_configured_web_dir;
249 extern char *netdata_configured_cache_dir;
src/main.c
+11 -21
@@ -213,23 +213,8 @@ void kill_childs()
213 tc_child_pid = 0;
214 }
215
216 - struct plugind *cd;
217 - for(cd = pluginsd_root ; cd ; cd = cd->next) {
218 - if(cd->enabled && !cd->obsolete) {
219 - info("Stopping %s plugin thread", cd->id);
220 - pthread_cancel(cd->thread);
221 -
222 - if(cd->pid) {
223 - info("killing %s plugin child process pid %d", cd->id, cd->pid);
224 - if(killpid(cd->pid, SIGTERM) != -1)
225 - waitid(P_PID, (id_t) cd->pid, &info, WEXITED);
226 -
227 - cd->pid = 0;
228 - }
229 -
230 - cd->obsolete = 1;
231 - }
232 - }
216 + // stop all running plugins
217 + pluginsd_stop_all_external_plugins();
218
219 // if, for any reason there is any child exited
220 // catch it here
@@ -472,17 +457,23 @@ static void get_netdata_configured_variables() {
457 }
458
459 // ------------------------------------------------------------------------
475 - // let the plugins know the min update_every
476 -
460 // get system paths
461 +
462 netdata_configured_config_dir = config_get(CONFIG_SECTION_GLOBAL, "config directory", CONFIG_DIR);
463 netdata_configured_log_dir = config_get(CONFIG_SECTION_GLOBAL, "log directory", LOG_DIR);
480 - netdata_configured_plugins_dir = config_get(CONFIG_SECTION_GLOBAL, "plugins directory", PLUGINS_DIR);
464 netdata_configured_web_dir = config_get(CONFIG_SECTION_GLOBAL, "web files directory", WEB_DIR);
465 netdata_configured_cache_dir = config_get(CONFIG_SECTION_GLOBAL, "cache directory", CACHE_DIR);
466 netdata_configured_varlib_dir = config_get(CONFIG_SECTION_GLOBAL, "lib directory", VARLIB_DIR);
467 netdata_configured_home_dir = config_get(CONFIG_SECTION_GLOBAL, "home directory", CACHE_DIR);
468
469 + {
470 + char plugins_dirs[(FILENAME_MAX * 2) + 1];
471 + snprintfz(plugins_dirs, FILENAME_MAX * 2, "\"%s\" \"%s/custom-plugins.d\"", PLUGINS_DIR, CONFIG_DIR);
472 + netdata_configured_plugins_dir_base = strdupz(config_get(CONFIG_SECTION_GLOBAL, "plugins directory", plugins_dirs));
473 + quoted_strings_splitter(netdata_configured_plugins_dir_base, plugin_directories, PLUGINSD_MAX_DIRECTORIES, config_isspace);
474 + netdata_configured_plugins_dir = plugin_directories[0];
475 + }
476 +
477 // ------------------------------------------------------------------------
478 // get default memory mode for the database
479
@@ -708,7 +699,6 @@ int main(int argc, char **argv) {
699 break;
700 case 'h':
701 return help(0);
711 - break;
702 case 'i':
703 config_set(CONFIG_SECTION_WEB, "bind to", optarg);
704 break;
src/plugins_d.c
+117 -64
@@ -1,5 +1,8 @@
1 #include "common.h"
2
3 +char *plugin_directories[PLUGINSD_MAX_DIRECTORIES] = { NULL };
4 +char *netdata_configured_plugins_dir_base;
5 +
6 struct plugind *pluginsd_root = NULL;
7
8 static inline int pluginsd_space(char c) {
@@ -16,13 +19,27 @@ static inline int pluginsd_space(char c) {
19 }
20 }
21
22 +inline int config_isspace(char c) {
23 + switch(c) {
24 + case ' ':
25 + case '\t':
26 + case '\r':
27 + case '\n':
28 + case ',':
29 + return 1;
30 +
31 + default:
32 + return 0;
33 + }
34 +}
35 +
36 // split a text into words, respecting quotes
20 -inline int pluginsd_split_words(char *str, char **words, int max_words) {
37 +inline int quoted_strings_splitter(char *str, char **words, int max_words, int (*custom_isspace)(char)) {
38 char *s = str, quote = 0;
39 int i = 0, j;
40
41 // skip all white space
25 - while(unlikely(pluginsd_space(*s))) s++;
42 + while(unlikely(custom_isspace(*s))) s++;
43
44 // check for quote
45 if(unlikely(*s == '\'' || *s == '"')) {
@@ -49,13 +66,13 @@ inline int pluginsd_split_words(char *str, char **words, int max_words) {
66 }
67
68 // if it is a space
52 - else if(unlikely(quote == 0 && pluginsd_space(*s))) {
69 + else if(unlikely(quote == 0 && custom_isspace(*s))) {
70
71 // terminate the word
72 *s++ = '\0';
73
74 // skip all white space
58 - while(likely(pluginsd_space(*s))) s++;
75 + while(likely(custom_isspace(*s))) s++;
76
77 // check for quote
78 if(unlikely(*s == '\'' || *s == '"')) {
@@ -82,6 +99,10 @@ inline int pluginsd_split_words(char *str, char **words, int max_words) {
99 return i;
100 }
101
102 +inline int pluginsd_split_words(char *str, char **words, int max_words) {
103 + return quoted_strings_splitter(str, words, max_words, pluginsd_space);
104 +}
105 +
106 inline size_t pluginsd_process(RRDHOST *host, struct plugind *cd, FILE *fp, int trust_durations) {
107 int enabled = cd->enabled;
108
@@ -547,99 +568,131 @@ void *pluginsd_main(void *ptr) {
568
569 int automatic_run = config_get_boolean(CONFIG_SECTION_PLUGINS, "enable running new plugins", 1);
570 int scan_frequency = (int) config_get_number(CONFIG_SECTION_PLUGINS, "check for new plugins every", 60);
550 - DIR *dir = NULL;
551 - struct dirent *file = NULL;
552 - struct plugind *cd;
553 -
554 - // enable the apps plugin by default
555 - // config_get_boolean(CONFIG_SECTION_PLUGINS, "apps", 1);
556 -
571 if(scan_frequency < 1) scan_frequency = 1;
572
573 + // store the errno for each plugins directory
574 + // so that we don't log broken directories on each loop
575 + int directory_errors[PLUGINSD_MAX_DIRECTORIES] = { 0 };
576 +
577 for(;;) {
578 if(unlikely(netdata_exit)) break;
579
562 - dir = opendir(netdata_configured_plugins_dir);
563 - if(unlikely(!dir)) {
564 - error("Cannot open directory '%s'.", netdata_configured_plugins_dir);
565 - goto cleanup;
566 - }
580 + int idx;
581 + const char *directory_name;
582
568 - while(likely((file = readdir(dir)))) {
583 + for( idx = 0; idx < PLUGINSD_MAX_DIRECTORIES && (directory_name = plugin_directories[idx]) ; idx++ ) {
584 if(unlikely(netdata_exit)) break;
585
571 - debug(D_PLUGINSD, "PLUGINSD: Examining file '%s'", file->d_name);
572 -
573 - if(unlikely(strcmp(file->d_name, ".") == 0 || strcmp(file->d_name, "..") == 0)) continue;
574 -
575 - int len = (int) strlen(file->d_name);
576 - if(unlikely(len <= (int)PLUGINSD_FILE_SUFFIX_LEN)) continue;
577 - if(unlikely(strcmp(PLUGINSD_FILE_SUFFIX, &file->d_name[len - (int)PLUGINSD_FILE_SUFFIX_LEN]) != 0)) {
578 - debug(D_PLUGINSD, "PLUGINSD: File '%s' does not end in '%s'.", file->d_name, PLUGINSD_FILE_SUFFIX);
586 + errno = 0;
587 + DIR *dir = opendir(directory_name);
588 + if(unlikely(!dir)) {
589 + if(directory_errors[idx] != errno) {
590 + directory_errors[idx] = errno;
591 + error("PLUGINSD: Cannot open plugins directory '%s'.", directory_name);
592 + }
593 continue;
594 }
595
582 - char pluginname[CONFIG_MAX_NAME + 1];
583 - snprintfz(pluginname, CONFIG_MAX_NAME, "%.*s", (int)(len - PLUGINSD_FILE_SUFFIX_LEN), file->d_name);
584 - int enabled = config_get_boolean(CONFIG_SECTION_PLUGINS, pluginname, automatic_run);
596 + struct dirent *file = NULL;
597 + while(likely((file = readdir(dir)))) {
598 + if(unlikely(netdata_exit)) break;
599
586 - if(unlikely(!enabled)) {
587 - debug(D_PLUGINSD, "PLUGINSD: plugin '%s' is not enabled", file->d_name);
588 - continue;
589 - }
600 + debug(D_PLUGINSD, "PLUGINSD: Examining file '%s'", file->d_name);
601
591 - // check if it runs already
592 - for(cd = pluginsd_root ; cd ; cd = cd->next)
593 - if(unlikely(strcmp(cd->filename, file->d_name) == 0)) break;
602 + if(unlikely(strcmp(file->d_name, ".") == 0 || strcmp(file->d_name, "..") == 0)) continue;
603
595 - if(likely(cd && !cd->obsolete)) {
596 - debug(D_PLUGINSD, "PLUGINSD: plugin '%s' is already running", cd->filename);
597 - continue;
598 - }
604 + int len = (int) strlen(file->d_name);
605 + if(unlikely(len <= (int)PLUGINSD_FILE_SUFFIX_LEN)) continue;
606 + if(unlikely(strcmp(PLUGINSD_FILE_SUFFIX, &file->d_name[len - (int)PLUGINSD_FILE_SUFFIX_LEN]) != 0)) {
607 + debug(D_PLUGINSD, "PLUGINSD: File '%s' does not end in '%s'.", file->d_name, PLUGINSD_FILE_SUFFIX);
608 + continue;
609 + }
610 +
611 + char pluginname[CONFIG_MAX_NAME + 1];
612 + snprintfz(pluginname, CONFIG_MAX_NAME, "%.*s", (int)(len - PLUGINSD_FILE_SUFFIX_LEN), file->d_name);
613 + int enabled = config_get_boolean(CONFIG_SECTION_PLUGINS, pluginname, automatic_run);
614 +
615 + if(unlikely(!enabled)) {
616 + debug(D_PLUGINSD, "PLUGINSD: plugin '%s' is not enabled", file->d_name);
617 + continue;
618 + }
619
600 - // it is not running
601 - // allocate a new one, or use the obsolete one
602 - if(unlikely(!cd)) {
603 - cd = callocz(sizeof(struct plugind), 1);
620 + // check if it runs already
621 + struct plugind *cd;
622 + for(cd = pluginsd_root ; cd ; cd = cd->next)
623 + if(unlikely(strcmp(cd->filename, file->d_name) == 0)) break;
624
605 - snprintfz(cd->id, CONFIG_MAX_NAME, "plugin:%s", pluginname);
625 + if(likely(cd && !cd->obsolete)) {
626 + debug(D_PLUGINSD, "PLUGINSD: plugin '%s' is already running", cd->filename);
627 + continue;
628 + }
629 +
630 + // it is not running
631 + // allocate a new one, or use the obsolete one
632 + if(unlikely(!cd)) {
633 + cd = callocz(sizeof(struct plugind), 1);
634 +
635 + snprintfz(cd->id, CONFIG_MAX_NAME, "plugin:%s", pluginname);
636
607 - strncpyz(cd->filename, file->d_name, FILENAME_MAX);
608 - snprintfz(cd->fullfilename, FILENAME_MAX, "%s/%s", netdata_configured_plugins_dir, cd->filename);
637 + strncpyz(cd->filename, file->d_name, FILENAME_MAX);
638 + snprintfz(cd->fullfilename, FILENAME_MAX, "%s/%s", directory_name, cd->filename);
639
610 - cd->enabled = enabled;
611 - cd->update_every = (int) config_get_number(cd->id, "update every", localhost->rrd_update_every);
612 - cd->started_t = now_realtime_sec();
640 + cd->enabled = enabled;
641 + cd->update_every = (int) config_get_number(cd->id, "update every", localhost->rrd_update_every);
642 + cd->started_t = now_realtime_sec();
643
614 - char *def = "";
615 - snprintfz(cd->cmd, PLUGINSD_CMD_MAX, "exec %s %d %s", cd->fullfilename, cd->update_every, config_get(cd->id, "command options", def));
644 + char *def = "";
645 + snprintfz(cd->cmd, PLUGINSD_CMD_MAX, "exec %s %d %s", cd->fullfilename, cd->update_every, config_get(cd->id, "command options", def));
646
617 - // link it
618 - if(likely(pluginsd_root)) cd->next = pluginsd_root;
619 - pluginsd_root = cd;
647 + // link it
648 + if(likely(pluginsd_root)) cd->next = pluginsd_root;
649 + pluginsd_root = cd;
650
621 - // it is not currently running
622 - cd->obsolete = 1;
651 + // it is not currently running
652 + cd->obsolete = 1;
653
624 - if(cd->enabled) {
625 - // spawn a new thread for it
626 - if(unlikely(pthread_create(&cd->thread, NULL, pluginsd_worker_thread, cd) != 0))
627 - error("PLUGINSD: failed to create new thread for plugin '%s'.", cd->filename);
654 + if(cd->enabled) {
655 + // spawn a new thread for it
656 + if(unlikely(pthread_create(&cd->thread, NULL, pluginsd_worker_thread, cd) != 0))
657 + error("PLUGINSD: failed to create new thread for plugin '%s'.", cd->filename);
658
629 - else if(unlikely(pthread_detach(cd->thread) != 0))
630 - error("PLUGINSD: Cannot request detach of newly created thread for plugin '%s'.", cd->filename);
659 + else if(unlikely(pthread_detach(cd->thread) != 0))
660 + error("PLUGINSD: Cannot request detach of newly created thread for plugin '%s'.", cd->filename);
661 + }
662 }
663 }
664 +
665 + closedir(dir);
666 }
667
635 - closedir(dir);
668 sleep((unsigned int) scan_frequency);
669 }
670
639 -cleanup:
671 info("PLUGINS.D thread exiting");
672
673 static_thread->enabled = 0;
674 pthread_exit(NULL);
675 return NULL;
676 }
677 +
678 +
679 +void pluginsd_stop_all_external_plugins() {
680 + siginfo_t info;
681 + struct plugind *cd;
682 + for(cd = pluginsd_root ; cd ; cd = cd->next) {
683 + if(cd->enabled && !cd->obsolete) {
684 + info("Stopping %s plugin thread", cd->id);
685 + pthread_cancel(cd->thread);
686 +
687 + if(cd->pid) {
688 + info("killing %s plugin child process pid %d", cd->id, cd->pid);
689 + if(killpid(cd->pid, SIGTERM) != -1)
690 + waitid(P_PID, (id_t) cd->pid, &info, WEXITED);
691 +
692 + cd->pid = 0;
693 + }
694 +
695 + cd->obsolete = 1;
696 + }
697 + }
698 +}
src/plugins_d.h
+8
@@ -16,6 +16,9 @@
16 #define PLUGINSD_LINE_MAX 1024
17 #define PLUGINSD_MAX_WORDS 20
18
19 +#define PLUGINSD_MAX_DIRECTORIES 20
20 +extern char *plugin_directories[PLUGINSD_MAX_DIRECTORIES];
21 +
22 struct plugind {
23 char id[CONFIG_MAX_NAME+1]; // config node id
24
@@ -44,7 +47,12 @@ struct plugind {
47 extern struct plugind *pluginsd_root;
48
49 extern void *pluginsd_main(void *ptr);
50 +extern void pluginsd_stop_all_external_plugins(void);
51 +
52 extern size_t pluginsd_process(RRDHOST *host, struct plugind *cd, FILE *fp, int trust_durations);
53 extern int pluginsd_split_words(char *str, char **words, int max_words);
54
55 +extern int quoted_strings_splitter(char *str, char **words, int max_words, int (*custom_isspace)(char));
56 +extern int config_isspace(char c);
57 +
58 #endif /* NETDATA_PLUGINS_D_H */