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 */