health parsing almost complete
Costa Tsaousis committed
Aug 13, 2016 at 21:54 UTC
a82c482bfb71ce7ae6c684612004195614e8159f
5 files changed
+939
-584
src/health.c
+359
-30
@@ -1,5 +1,7 @@
1
#include "common.h"
2
3
+static const char *health_default_exec = PLUGINS_DIR "/alarm.sh";
4
+
5
// ----------------------------------------------------------------------------
6
// RRDVAR management
7
@@ -578,38 +580,21 @@ void rrdsetcalc_unlink(RRDCALC *rc) {
580
rrdcalc_unlinked_optimize_rrdhost(host, rc);
581
}
582
581
-RRDCALC *rrdcalc_create(RRDHOST *host, const char *name, const char *chart, const char *dimensions, int group_method, uint32_t after, uint32_t before, int update_every, uint32_t options) {
582
- uint32_t hash = simple_hash(name);
583
-
583
+static inline int rrdcalc_exists(RRDHOST *host, const char *name, uint32_t hash) {
584
RRDCALC *rc;
585
586
// make sure it does not already exist
587
for(rc = host->calculations; rc ; rc = rc->next) {
588
if (rc->hash == hash && !strcmp(name, rc->name)) {
589
- error("Attempted to create RRDCAL '%s' in host '%s', but it already exists. Ignoring it.", name, host->hostname);
590
- return NULL;
589
+ error("Attempted to create RRDCAL '%s' in host '%s', but it already exists.", name, host->hostname);
590
+ return 1;
591
}
592
}
593
594
- rc = callocz(1, sizeof(RRDCALC));
595
-
596
- rc->name = strdupz(name);
597
- rc->hash = simple_hash(rc->name);
598
-
599
- rc->chart = strdupz(chart);
600
- rc->hash_chart = simple_hash(rc->chart);
601
-
602
- if(dimensions) {
603
- rc->dimensions = strdupz(dimensions);
604
- rc->hash_chart = simple_hash(rc->chart);
605
- }
606
-
607
- rc->group = group_method;
608
- rc->after = after;
609
- rc->before = before;
610
- rc->update_every = update_every;
611
- rc->options = options;
594
+ return 0;
595
+}
596
597
+void rrdcalc_create_part2(RRDHOST *host, RRDCALC *rc) {
598
// link it to the host
599
rc->next = host->calculations;
600
host->calculations = rc;
@@ -622,8 +607,32 @@ RRDCALC *rrdcalc_create(RRDHOST *host, const char *name, const char *chart, cons
607
break;
608
}
609
}
610
+}
611
+
612
+RRDCALC *rrdcalc_create(RRDHOST *host, const char *name, const char *chart, const char *dimensions, int group_method, uint32_t after, uint32_t before, int update_every, uint32_t options) {
613
+ uint32_t hash = simple_hash(name);
614
626
- return NULL;
615
+ if(rrdcalc_exists(host, name, hash))
616
+ return NULL;
617
+
618
+ RRDCALC *rc = callocz(1, sizeof(RRDCALC));
619
+
620
+ rc->name = strdupz(name);
621
+ rc->hash = simple_hash(rc->name);
622
+
623
+ rc->chart = strdupz(chart);
624
+ rc->hash_chart = simple_hash(rc->chart);
625
+
626
+ if(dimensions) rc->dimensions = strdupz(dimensions);
627
+
628
+ rc->group = group_method;
629
+ rc->after = after;
630
+ rc->before = before;
631
+ rc->update_every = update_every;
632
+ rc->options = options;
633
+
634
+ rrdcalc_create_part2(host, rc);
635
+ return rc;
636
}
637
638
void rrdcalc_free(RRDHOST *host, RRDCALC *rc) {
@@ -650,9 +659,13 @@ void rrdcalc_free(RRDHOST *host, RRDCALC *rc) {
659
else
660
error("Cannot unlink RRDCALC '%s' from RRDHOST '%s': RRDHOST does not have any calculations", rc->name, host->hostname);
661
662
+ if(rc->warning) expression_free(rc->warning);
663
+ if(rc->critical) expression_free(rc->critical);
664
+
665
freez(rc->name);
666
freez(rc->chart);
667
freez(rc->dimensions);
668
+ freez(rc->exec);
669
670
freez(rc);
671
}
@@ -662,18 +675,226 @@ void rrdcalc_free(RRDHOST *host, RRDCALC *rc) {
675
676
#define HEALTH_CONF_MAX_LINE 4096
677
678
+#define HEALTH_ALARM_KEY "alarm"
679
+#define HEALTH_TEMPLATE_KEY "template"
680
+#define HEALTH_ON_KEY "on"
681
+#define HEALTH_CALC_KEY "calc"
682
+#define HEALTH_GREEN_KEY "green"
683
+#define HEALTH_RED_KEY "red"
684
+#define HEALTH_WARN_KEY "warn"
685
+#define HEALTH_CRIT_KEY "crit"
686
+#define HEALTH_EXEC_KEY "exec"
687
+
688
+static inline int rrdcalc_add(RRDHOST *host, RRDCALC *rc) {
689
+ info("Health configuration examining alarm '%s': chart '%s', exec '%s', green %Lf, red %Lf, calculation: group %d, after %d, before %d, options %u, update every %d, dimensions '%s', warning '%s', critical '%s'",
690
+ rc->name,
691
+ (rc->chart)?rc->chart:"NONE",
692
+ (rc->exec)?rc->exec:"DEFAULT",
693
+ rc->green,
694
+ rc->red,
695
+ rc->group,
696
+ rc->after,
697
+ rc->before,
698
+ rc->options,
699
+ rc->update_every,
700
+ (rc->dimensions)?rc->dimensions:"NONE",
701
+ (rc->warning)?rc->warning->parsed_as:"NONE",
702
+ (rc->critical)?rc->critical->parsed_as:"NONE");
703
+
704
+ if(!rc->chart) {
705
+ error("Health configuration for alarm '%s' does not have a chart", rc->name);
706
+ return 0;
707
+ }
708
+
709
+ if(!rc->after) {
710
+ error("Health configuration for alarm '%s' does not have a calculation", rc->name);
711
+ return 0;
712
+ }
713
+
714
+ if(!rc->group) {
715
+ error("Health configuration for alarm '%s' does not specify a chart name", rc->name);
716
+ return 0;
717
+ }
718
+
719
+ rrdcalc_create_part2(host, rc);
720
+ return 1;
721
+}
722
+
723
+static inline int rrdcalctemplate_add(RRDHOST *host, RRDCALCTEMPLATE *rt) {
724
+}
725
+
726
+static inline void rrdcalctemplate_free(RRDHOST *host, RRDCALCTEMPLATE *rt) {
727
+
728
+}
729
+
730
+static inline int health_parse_time(char *string, int *result) {
731
+ // make sure it is a number
732
+ if(!*string || !(isdigit(*string) || *string == '+' || *string == '-')) {
733
+ *result = 0;
734
+ return 0;
735
+ }
736
+
737
+ char *e = NULL;
738
+ calculated_number n = strtold(string, &e);
739
+ if(e && *e) {
740
+ switch (*e) {
741
+ case 'Y':
742
+ *result = (int) (n * 86400 * 365);
743
+ break;
744
+ case 'M':
745
+ *result = (int) (n * 86400 * 30);
746
+ break;
747
+ case 'w':
748
+ *result = (int) (n * 86400 * 7);
749
+ break;
750
+ case 'd':
751
+ *result = (int) (n * 86400);
752
+ break;
753
+ case 'h':
754
+ *result = (int) (n * 3600);
755
+ break;
756
+ case 'm':
757
+ *result = (int) (n * 60);
758
+ break;
759
+
760
+ default:
761
+ case 's':
762
+ *result = (int) (n);
763
+ break;
764
+ }
765
+ }
766
+ else
767
+ *result = (int)(n);
768
+
769
+ return 1;
770
+}
771
+
772
+static inline int health_parse_chart_calc(size_t line,
773
+ const char *path,
774
+ const char *file,
775
+ char *string,
776
+ int *group_method,
777
+ int *after,
778
+ int *before,
779
+ int *every,
780
+ uint32_t *options,
781
+ char **dimensions
782
+) {
783
+ if(*dimensions) freez(*dimensions);
784
+ *dimensions = NULL;
785
+ *after = -1;
786
+ *before = 0;
787
+ *every = 1;
788
+ *options = 0;
789
+
790
+ char *s = string, *key;
791
+
792
+ // first is the group method
793
+ key = s;
794
+ while(*s && !isspace(*s)) s++;
795
+ while(*s && isspace(*s)) *s++ = '\0';
796
+ if(!*s) {
797
+ error("Health configuration invalid chart calculation at line %zu of file '%s/%s': expected group method followed by the 'after' time, but got '%s'",
798
+ line, path, file, key);
799
+ return 0;
800
+ }
801
+
802
+ if((*group_method = web_client_api_request_v1_data_group(key, -1)) == -1) {
803
+ error("Health configuration at line %zu of file '%s/%s': invalid group method '%s'",
804
+ line, path, file, key);
805
+ return 0;
806
+ }
807
+
808
+ // then is the 'after' time
809
+ key = s;
810
+ while(*s && !isspace(*s)) s++;
811
+ while(*s && isspace(*s)) *s++ = '\0';
812
+
813
+ if(!health_parse_time(key, after)) {
814
+ error("Health configuration at line %zu of file '%s/%s': invalid duration '%s' after group method",
815
+ line, path, file, key);
816
+ return 0;
817
+ }
818
+
819
+ // now we may have optional parameters
820
+ while(*s) {
821
+ key = s;
822
+ while(*s && !isspace(*s)) s++;
823
+ while(*s && isspace(*s)) *s++ = '\0';
824
+ if(!*key) break;
825
+
826
+ if(!strcasecmp(key, "at")) {
827
+ char *value = s;
828
+ while(*s && !isspace(*s)) s++;
829
+ while(*s && isspace(*s)) *s++ = '\0';
830
+
831
+ if (!health_parse_time(value, before)) {
832
+ error("Health configuration at line %zu of file '%s/%s': invalid duration '%s' for '%s' keyword",
833
+ line, path, file, value, key);
834
+ }
835
+ }
836
+ else if(!strcasecmp(key, "every")) {
837
+ char *value = s;
838
+ while(*s && !isspace(*s)) s++;
839
+ while(*s && isspace(*s)) *s++ = '\0';
840
+
841
+ if (!health_parse_time(value, every)) {
842
+ error("Health configuration at line %zu of file '%s/%s': invalid duration '%s' for '%s' keyword",
843
+ line, path, file, value, key);
844
+ }
845
+ }
846
+ else if(!strcasecmp(key, "absolute") || !strcasecmp(key, "abs") || !strcasecmp(key, "absolute_sum")) {
847
+ *options |= RRDR_OPTION_ABSOLUTE;
848
+ }
849
+ else if(!strcasecmp(key, "min2max")) {
850
+ *options |= RRDR_OPTION_MIN2MAX;
851
+ }
852
+ else if(!strcasecmp(key, "null2zero")) {
853
+ *options |= RRDR_OPTION_NULL2ZERO;
854
+ }
855
+ else if(!strcasecmp(key, "percentage")) {
856
+ *options |= RRDR_OPTION_PERCENTAGE;
857
+ }
858
+ else if(!strcasecmp(key, "unaligned")) {
859
+ *options |= RRDR_OPTION_NOT_ALIGNED;
860
+ }
861
+ else if(!strcasecmp(key, "of")) {
862
+ *dimensions = strdupz(s);
863
+ break;
864
+ }
865
+ }
866
+
867
+ return 1;
868
+}
869
+
870
int health_readfile(const char *path, const char *filename) {
871
+ static uint32_t hash_alarm = 0, hash_template = 0, hash_on = 0, hash_calc = 0, hash_green = 0, hash_red = 0, hash_warn = 0, hash_crit = 0, hash_exec = 0;
872
char buffer[HEALTH_CONF_MAX_LINE + 1];
873
668
- info("Reading file '%s/%s'", path, filename);
874
+ if(unlikely(!hash_alarm)) {
875
+ hash_alarm = simple_uhash(HEALTH_ALARM_KEY);
876
+ hash_template = simple_uhash(HEALTH_TEMPLATE_KEY);
877
+ hash_on = simple_uhash(HEALTH_ON_KEY);
878
+ hash_calc = simple_uhash(HEALTH_CALC_KEY);
879
+ hash_green = simple_uhash(HEALTH_GREEN_KEY);
880
+ hash_red = simple_uhash(HEALTH_RED_KEY);
881
+ hash_warn = simple_uhash(HEALTH_WARN_KEY);
882
+ hash_crit = simple_uhash(HEALTH_CRIT_KEY);
883
+ hash_exec = simple_uhash(HEALTH_EXEC_KEY);
884
+ }
885
+
886
+ // info("Reading file '%s/%s'", path, filename);
887
888
snprintfz(buffer, HEALTH_CONF_MAX_LINE, "%s/%s", path, filename);
889
FILE *fp = fopen(buffer, "r");
890
if(!fp) {
673
- error("Cannot read file '%s' for reading health configuration.", buffer);
891
+ error("Health configuration cannot read file '%s'.", buffer);
892
return 0;
893
}
894
895
+ RRDCALC *rc = NULL;
896
+ RRDCALCTEMPLATE *rt = NULL;
897
+
898
size_t line = 0, append = 0;
899
char *s;
900
while((s = fgets(&buffer[append], (int)(HEALTH_CONF_MAX_LINE - append), fp)) || append) {
@@ -697,7 +918,7 @@ int health_readfile(const char *path, const char *filename) {
918
char *key = s;
919
while(*s && *s != ':') s++;
920
if(!*s) {
700
- error("Invalid line %zu of file '%s/%s'. It does not contain a ':'. Ignoring it.", line, path, filename);
921
+ error("Health configuration has invalid line %zu of file '%s/%s'. It does not contain a ':'. Ignoring it.", line, path, filename);
922
continue;
923
}
924
*s = '\0';
@@ -708,18 +929,123 @@ int health_readfile(const char *path, const char *filename) {
929
value = trim(value);
930
931
if(!key) {
711
- error("Invalid line %zu of file '%s/%s'. Keyword is empty. Ignoring it.", line, path, filename);
932
+ error("Health configuration has invalid line %zu of file '%s/%s'. Keyword is empty. Ignoring it.", line, path, filename);
933
continue;
934
}
935
936
if(!value) {
716
- error("Invalid line %zu of file '%s/%s'. value is empty. Ignoring it.", line, path, filename);
937
+ error("Health configuration has invalid line %zu of file '%s/%s'. value is empty. Ignoring it.", line, path, filename);
938
continue;
939
}
940
941
// info("Health file '%s/%s', key '%s', value '%s'", path, filename, key, value);
942
+ uint32_t hash = simple_uhash(key);
943
+
944
+ if(hash == hash_alarm && !strcasecmp(key, HEALTH_ALARM_KEY)) {
945
+ if(rc && !rrdcalc_add(&localhost, rc))
946
+ rrdcalc_free(&localhost, rc);
947
+
948
+ if(rt && !rrdcalctemplate_add(&localhost, rt)) {
949
+ rrdcalctemplate_free(&localhost, rt);
950
+ rt = NULL;
951
+ }
952
+
953
+ rc = callocz(1, sizeof(RRDCALC));
954
+ rc->name = strdupz(value);
955
+ rc->hash = simple_hash(rc->name);
956
+ }
957
+ else if(hash == hash_template && !strcasecmp(key, HEALTH_TEMPLATE_KEY)) {
958
+ if(rc && !rrdcalc_add(&localhost, rc)) {
959
+ rrdcalc_free(&localhost, rc);
960
+ rc = NULL;
961
+ }
962
+
963
+ if(rt && !rrdcalctemplate_add(&localhost, rt))
964
+ rrdcalctemplate_free(&localhost, rt);
965
+
966
+ rt = callocz(1, sizeof(RRDCALCTEMPLATE));
967
+ rt->name = strdupz(value);
968
+ rt->hash_name = simple_hash(rt->name);
969
+ }
970
+ else if(rc) {
971
+ if(hash == hash_on && !strcasecmp(key, HEALTH_ON_KEY)) {
972
+ if(rc->chart) {
973
+ if(strcmp(rc->chart, value))
974
+ info("Health configuration at line %zu of file '%s/%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
975
+ line, path, filename, rc->name, key, rc->chart, value, value);
976
+
977
+ freez(rc->chart);
978
+ }
979
+ rc->chart = strdupz(value);
980
+ rc->hash_chart = simple_hash(rc->chart);
981
+ }
982
+ else if(hash == hash_calc && !strcasecmp(key, HEALTH_CALC_KEY)) {
983
+ health_parse_chart_calc(line, path, filename, value, &rc->group, &rc->after, &rc->before, &rc->update_every, &rc->options, &rc->dimensions);
984
+ }
985
+ else if(hash == hash_green && !strcasecmp(key, HEALTH_GREEN_KEY)) {
986
+ char *e;
987
+ rc->green = strtold(value, &e);
988
+ if(e && *e) {
989
+ info("Health configuration at line %zu of file '%s/%s' for alarm '%s' at key '%s' leaves this string unmatched: '%s'.",
990
+ line, path, filename, rc->name, key, e);
991
+ }
992
+ }
993
+ else if(hash == hash_red && !strcasecmp(key, HEALTH_RED_KEY)) {
994
+ char *e;
995
+ rc->red = strtold(value, &e);
996
+ if(e && *e) {
997
+ info("Health configuration at line %zu of file '%s/%s' for alarm '%s' at key '%s' leaves this string unmatched: '%s'.",
998
+ line, path, filename, rc->name, key, e);
999
+ }
1000
+ }
1001
+ else if(hash == hash_warn && !strcasecmp(key, HEALTH_WARN_KEY)) {
1002
+ const char *failed_at = NULL;
1003
+ int error = 0;
1004
+ rc->warning = expression_parse(value, &failed_at, &error);
1005
+ if(!rc->warning) {
1006
+ error("Health configuration at line %zu of file '%s/%s' for alarm '%s' at key '%s' has unparse-able expression '%s': %s at '%s'",
1007
+ line, path, filename, rc->name, key, value, expression_strerror(error), failed_at);
1008
+ }
1009
+ }
1010
+ else if(hash == hash_crit && !strcasecmp(key, HEALTH_CRIT_KEY)) {
1011
+ const char *failed_at = NULL;
1012
+ int error = 0;
1013
+ rc->critical = expression_parse(value, &failed_at, &error);
1014
+ if(!rc->critical) {
1015
+ error("Health configuration at line %zu of file '%s/%s' for alarm '%s' at key '%s' has unparse-able expression '%s': %s at '%s'",
1016
+ line, path, filename, rc->name, key, value, expression_strerror(error), failed_at);
1017
+ }
1018
+ }
1019
+ else if(hash == hash_exec && !strcasecmp(key, HEALTH_EXEC_KEY)) {
1020
+ if(rc->exec) {
1021
+ if(strcmp(rc->exec, value))
1022
+ info("Health configuration at line %zu of file '%s/%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
1023
+ line, path, filename, rc->name, key, rc->exec, value, value);
1024
+
1025
+ freez(rc->exec);
1026
+ }
1027
+ rc->exec = strdupz(value);
1028
+ }
1029
+ else {
1030
+ error("Health configuration at line %zu of file '%s/%s' for alarm '%s' has unknown key '%s'.",
1031
+ line, path, filename, rc->name, key);
1032
+ }
1033
+ }
1034
+ else if(rt) {
1035
+
1036
+ }
1037
+ else {
1038
+ error("Health configuration at line %zu of file '%s/%s' has unknown key '%s'. Expected either '" HEALTH_ALARM_KEY "' or '" HEALTH_TEMPLATE_KEY "'.",
1039
+ line, path, filename, key);
1040
+ }
1041
}
1042
1043
+ if(rc && !rrdcalc_add(&localhost, rc))
1044
+ rrdcalc_free(&localhost, rc);
1045
+
1046
+ if(rt && !rrdcalctemplate_add(&localhost, rt))
1047
+ rrdcalctemplate_free(&localhost, rt);
1048
+
1049
fclose(fp);
1050
return 1;
1051
}
@@ -731,7 +1057,7 @@ void health_readdir(const char *path) {
1057
1058
DIR *dir = opendir(path);
1059
if (!dir) {
734
- error("Cannot open directory '%s' for reading health configuration files.", path);
1060
+ error("Health configuration cannot open directory '%s'.", path);
1061
return;
1062
}
1063
@@ -770,6 +1096,9 @@ void health_init(void) {
1096
char buffer[FILENAME_MAX + 1];
1097
snprintfz(buffer, FILENAME_MAX, "%s/health.d", config_get("global", "config directory", CONFIG_DIR));
1098
path = config_get("health", "configuration files in directory", buffer);
1099
+
1100
+ snprintfz(buffer, FILENAME_MAX, "%s/alarm.sh", config_get("global", "plugins directory", PLUGINS_DIR));
1101
+ health_default_exec = config_get("health", "script to execute on alarm", buffer);
1102
}
1103
1104
health_readdir(path);
src/health.h
+12
-1
@@ -145,6 +145,8 @@ typedef struct rrdcalc {
145
char *name;
146
uint32_t hash;
147
148
+ char *exec;
149
+
150
char *chart; // the chart name
151
uint32_t hash_chart;
152
@@ -159,10 +161,14 @@ typedef struct rrdcalc {
161
time_t last_updated;
162
time_t next_update;
163
162
- EVAL_EXPRESSION *expression;
164
+ EVAL_EXPRESSION *warning;
165
+ EVAL_EXPRESSION *critical;
166
167
calculated_number value;
168
169
+ calculated_number green;
170
+ calculated_number red;
171
+
172
RRDVAR *local;
173
RRDVAR *context;
174
RRDVAR *host;
@@ -182,6 +188,8 @@ typedef struct rrdcalctemplate {
188
char *name;
189
uint32_t hash_name;
190
191
+ char *exec;
192
+
193
char *context;
194
uint32_t hash_context;
195
@@ -193,6 +201,9 @@ typedef struct rrdcalctemplate {
201
uint32_t options; // calculation options
202
int update_every; // update frequency for the calculation
203
204
+ calculated_number green;
205
+ calculated_number red;
206
+
207
struct rrdcalctemplate *next;
208
} RRDCALCTEMPLATE;
209
src/main.c
+563
-549
@@ -3,527 +3,538 @@
3
extern void *cgroups_main(void *ptr);
4
5
void netdata_cleanup_and_exit(int ret) {
6
- netdata_exit = 1;
6
+ netdata_exit = 1;
7
8
- error_log_limit_unlimited();
8
+ error_log_limit_unlimited();
9
10
- info("Called: netdata_cleanup_and_exit()");
10
+ info("Called: netdata_cleanup_and_exit()");
11
#ifdef NETDATA_INTERNAL_CHECKS
12
rrdset_free_all();
13
#else
14
- rrdset_save_all();
14
+ rrdset_save_all();
15
#endif
16
- // kill_childs();
16
+ // kill_childs();
17
18
- if(pidfile[0]) {
19
- if(unlink(pidfile) != 0)
20
- error("Cannot unlink pidfile '%s'.", pidfile);
21
- }
18
+ if(pidfile[0]) {
19
+ if(unlink(pidfile) != 0)
20
+ error("Cannot unlink pidfile '%s'.", pidfile);
21
+ }
22
23
- info("NetData exiting. Bye bye...");
24
- exit(ret);
23
+ info("NetData exiting. Bye bye...");
24
+ exit(ret);
25
}
26
27
struct netdata_static_thread {
28
- char *name;
28
+ char *name;
29
30
- char *config_section;
31
- char *config_name;
30
+ char *config_section;
31
+ char *config_name;
32
33
- int enabled;
33
+ int enabled;
34
35
- pthread_t *thread;
35
+ pthread_t *thread;
36
37
- void (*init_routine) (void);
38
- void *(*start_routine) (void *);
37
+ void (*init_routine) (void);
38
+ void *(*start_routine) (void *);
39
} static_threads[] = {
40
#ifdef INTERNAL_PLUGIN_NFACCT
41
// nfacct requires root access
42
- // so, we build it as an external plugin with setuid to root
43
- {"nfacct", "plugins", "nfacct", 1, NULL, NULL, nfacct_main},
42
+ // so, we build it as an external plugin with setuid to root
43
+ {"nfacct", "plugins", "nfacct", 1, NULL, NULL, nfacct_main},
44
#endif
45
46
- {"tc", "plugins", "tc", 1, NULL, NULL, tc_main},
47
- {"idlejitter", "plugins", "idlejitter", 1, NULL, NULL, cpuidlejitter_main},
48
- {"proc", "plugins", "proc", 1, NULL, NULL, proc_main},
49
- {"cgroups", "plugins", "cgroups", 1, NULL, NULL, cgroups_main},
50
- {"plugins.d", NULL, NULL, 1, NULL, NULL, pluginsd_main},
51
- {"check", "plugins", "checks", 0, NULL, NULL, checks_main},
52
- {"web", NULL, NULL, 1, NULL, NULL, socket_listen_main_multi_threaded},
53
- {"web-single-threaded", NULL, NULL, 0, NULL, NULL, socket_listen_main_single_threaded},
54
- {NULL, NULL, NULL, 0, NULL, NULL, NULL}
46
+ {"tc", "plugins", "tc", 1, NULL, NULL, tc_main},
47
+ {"idlejitter", "plugins", "idlejitter", 1, NULL, NULL, cpuidlejitter_main},
48
+ {"proc", "plugins", "proc", 1, NULL, NULL, proc_main},
49
+ {"cgroups", "plugins", "cgroups", 1, NULL, NULL, cgroups_main},
50
+ {"plugins.d", NULL, NULL, 1, NULL, NULL, pluginsd_main},
51
+ {"check", "plugins", "checks", 0, NULL, NULL, checks_main},
52
+ {"web", NULL, NULL, 1, NULL, NULL, socket_listen_main_multi_threaded},
53
+ {"web-single-threaded", NULL, NULL, 0, NULL, NULL, socket_listen_main_single_threaded},
54
+ {NULL, NULL, NULL, 0, NULL, NULL, NULL}
55
};
56
57
void web_server_threading_selection(void) {
58
- int threaded = config_get_boolean("global", "multi threaded web server", 1);
58
+ int threaded = config_get_boolean("global", "multi threaded web server", 1);
59
60
- int i;
61
- for(i = 0; static_threads[i].name ; i++) {
62
- if(static_threads[i].start_routine == socket_listen_main_multi_threaded)
63
- static_threads[i].enabled = threaded?1:0;
60
+ int i;
61
+ for(i = 0; static_threads[i].name ; i++) {
62
+ if(static_threads[i].start_routine == socket_listen_main_multi_threaded)
63
+ static_threads[i].enabled = threaded?1:0;
64
65
- if(static_threads[i].start_routine == socket_listen_main_single_threaded)
66
- static_threads[i].enabled = threaded?0:1;
67
- }
65
+ if(static_threads[i].start_routine == socket_listen_main_single_threaded)
66
+ static_threads[i].enabled = threaded?0:1;
67
+ }
68
69
- web_client_timeout = (int) config_get_number("global", "disconnect idle web clients after seconds", DEFAULT_DISCONNECT_IDLE_WEB_CLIENTS_AFTER_SECONDS);
69
+ web_client_timeout = (int) config_get_number("global", "disconnect idle web clients after seconds", DEFAULT_DISCONNECT_IDLE_WEB_CLIENTS_AFTER_SECONDS);
70
71
- web_donotrack_comply = config_get_boolean("global", "respect web browser do not track policy", web_donotrack_comply);
71
+ web_donotrack_comply = config_get_boolean("global", "respect web browser do not track policy", web_donotrack_comply);
72
73
#ifdef NETDATA_WITH_ZLIB
74
- web_enable_gzip = config_get_boolean("global", "enable web responses gzip compression", web_enable_gzip);
75
-
76
- char *s = config_get("global", "web compression strategy", "default");
77
- if(!strcmp(s, "default"))
78
- web_gzip_strategy = Z_DEFAULT_STRATEGY;
79
- else if(!strcmp(s, "filtered"))
80
- web_gzip_strategy = Z_FILTERED;
81
- else if(!strcmp(s, "huffman only"))
82
- web_gzip_strategy = Z_HUFFMAN_ONLY;
83
- else if(!strcmp(s, "rle"))
84
- web_gzip_strategy = Z_RLE;
85
- else if(!strcmp(s, "fixed"))
86
- web_gzip_strategy = Z_FIXED;
87
- else {
88
- error("Invalid compression strategy '%s'. Valid strategies are 'default', 'filtered', 'huffman only', 'rle' and 'fixed'. Proceeding with 'default'.", s);
89
- web_gzip_strategy = Z_DEFAULT_STRATEGY;
90
- }
91
-
92
- web_gzip_level = (int)config_get_number("global", "web compression level", 3);
93
- if(web_gzip_level < 1) {
94
- error("Invalid compression level %d. Valid levels are 1 (fastest) to 9 (best ratio). Proceeding with level 1 (fastest compression).", web_gzip_level);
95
- web_gzip_level = 1;
96
- }
97
- else if(web_gzip_level > 9) {
98
- error("Invalid compression level %d. Valid levels are 1 (fastest) to 9 (best ratio). Proceeding with level 9 (best compression).", web_gzip_level);
99
- web_gzip_level = 9;
100
- }
74
+ web_enable_gzip = config_get_boolean("global", "enable web responses gzip compression", web_enable_gzip);
75
+
76
+ char *s = config_get("global", "web compression strategy", "default");
77
+ if(!strcmp(s, "default"))
78
+ web_gzip_strategy = Z_DEFAULT_STRATEGY;
79
+ else if(!strcmp(s, "filtered"))
80
+ web_gzip_strategy = Z_FILTERED;
81
+ else if(!strcmp(s, "huffman only"))
82
+ web_gzip_strategy = Z_HUFFMAN_ONLY;
83
+ else if(!strcmp(s, "rle"))
84
+ web_gzip_strategy = Z_RLE;
85
+ else if(!strcmp(s, "fixed"))
86
+ web_gzip_strategy = Z_FIXED;
87
+ else {
88
+ error("Invalid compression strategy '%s'. Valid strategies are 'default', 'filtered', 'huffman only', 'rle' and 'fixed'. Proceeding with 'default'.", s);
89
+ web_gzip_strategy = Z_DEFAULT_STRATEGY;
90
+ }
91
+
92
+ web_gzip_level = (int)config_get_number("global", "web compression level", 3);
93
+ if(web_gzip_level < 1) {
94
+ error("Invalid compression level %d. Valid levels are 1 (fastest) to 9 (best ratio). Proceeding with level 1 (fastest compression).", web_gzip_level);
95
+ web_gzip_level = 1;
96
+ }
97
+ else if(web_gzip_level > 9) {
98
+ error("Invalid compression level %d. Valid levels are 1 (fastest) to 9 (best ratio). Proceeding with level 9 (best compression).", web_gzip_level);
99
+ web_gzip_level = 9;
100
+ }
101
#endif /* NETDATA_WITH_ZLIB */
102
}
103
104
105
int killpid(pid_t pid, int sig)
106
{
107
- int ret = -1;
108
- debug(D_EXIT, "Request to kill pid %d", pid);
109
-
110
- errno = 0;
111
- if(kill(pid, 0) == -1) {
112
- switch(errno) {
113
- case ESRCH:
114
- error("Request to kill pid %d, but it is not running.", pid);
115
- break;
116
-
117
- case EPERM:
118
- error("Request to kill pid %d, but I do not have enough permissions.", pid);
119
- break;
120
-
121
- default:
122
- error("Request to kill pid %d, but I received an error.", pid);
123
- break;
124
- }
125
- }
126
- else {
127
- errno = 0;
128
- ret = kill(pid, sig);
129
- if(ret == -1) {
130
- switch(errno) {
131
- case ESRCH:
132
- error("Cannot kill pid %d, but it is not running.", pid);
133
- break;
134
-
135
- case EPERM:
136
- error("Cannot kill pid %d, but I do not have enough permissions.", pid);
137
- break;
138
-
139
- default:
140
- error("Cannot kill pid %d, but I received an error.", pid);
141
- break;
142
- }
143
- }
144
- }
145
-
146
- return ret;
107
+ int ret = -1;
108
+ debug(D_EXIT, "Request to kill pid %d", pid);
109
+
110
+ errno = 0;
111
+ if(kill(pid, 0) == -1) {
112
+ switch(errno) {
113
+ case ESRCH:
114
+ error("Request to kill pid %d, but it is not running.", pid);
115
+ break;
116
+
117
+ case EPERM:
118
+ error("Request to kill pid %d, but I do not have enough permissions.", pid);
119
+ break;
120
+
121
+ default:
122
+ error("Request to kill pid %d, but I received an error.", pid);
123
+ break;
124
+ }
125
+ }
126
+ else {
127
+ errno = 0;
128
+ ret = kill(pid, sig);
129
+ if(ret == -1) {
130
+ switch(errno) {
131
+ case ESRCH:
132
+ error("Cannot kill pid %d, but it is not running.", pid);
133
+ break;
134
+
135
+ case EPERM:
136
+ error("Cannot kill pid %d, but I do not have enough permissions.", pid);
137
+ break;
138
+
139
+ default:
140
+ error("Cannot kill pid %d, but I received an error.", pid);
141
+ break;
142
+ }
143
+ }
144
+ }
145
+
146
+ return ret;
147
}
148
149
void kill_childs()
150
{
151
- siginfo_t info;
152
-
153
- struct web_client *w;
154
- for(w = web_clients; w ; w = w->next) {
155
- debug(D_EXIT, "Stopping web client %s", w->client_ip);
156
- pthread_cancel(w->thread);
157
- pthread_join(w->thread, NULL);
158
- }
159
-
160
- int i;
161
- for (i = 0; static_threads[i].name != NULL ; i++) {
162
- if(static_threads[i].thread) {
163
- debug(D_EXIT, "Stopping %s thread", static_threads[i].name);
164
- pthread_cancel(*static_threads[i].thread);
165
- pthread_join(*static_threads[i].thread, NULL);
166
- static_threads[i].thread = NULL;
167
- }
168
- }
169
-
170
- if(tc_child_pid) {
171
- info("Killing tc-qos-helper procees");
172
- if(killpid(tc_child_pid, SIGTERM) != -1)
173
- waitid(P_PID, (id_t) tc_child_pid, &info, WEXITED);
174
- }
175
- tc_child_pid = 0;
176
-
177
- struct plugind *cd;
178
- for(cd = pluginsd_root ; cd ; cd = cd->next) {
179
- debug(D_EXIT, "Stopping %s plugin thread", cd->id);
180
- pthread_cancel(cd->thread);
181
- pthread_join(cd->thread, NULL);
182
-
183
- if(cd->pid && !cd->obsolete) {
184
- debug(D_EXIT, "killing %s plugin process", cd->id);
185
- if(killpid(cd->pid, SIGTERM) != -1)
186
- waitid(P_PID, (id_t) cd->pid, &info, WEXITED);
187
- }
188
- }
189
-
190
- // if, for any reason there is any child exited
191
- // catch it here
192
- waitid(P_PID, 0, &info, WEXITED|WNOHANG);
193
-
194
- debug(D_EXIT, "All threads/childs stopped.");
151
+ siginfo_t info;
152
+
153
+ struct web_client *w;
154
+ for(w = web_clients; w ; w = w->next) {
155
+ debug(D_EXIT, "Stopping web client %s", w->client_ip);
156
+ pthread_cancel(w->thread);
157
+ pthread_join(w->thread, NULL);
158
+ }
159
+
160
+ int i;
161
+ for (i = 0; static_threads[i].name != NULL ; i++) {
162
+ if(static_threads[i].thread) {
163
+ debug(D_EXIT, "Stopping %s thread", static_threads[i].name);
164
+ pthread_cancel(*static_threads[i].thread);
165
+ pthread_join(*static_threads[i].thread, NULL);
166
+ static_threads[i].thread = NULL;
167
+ }
168
+ }
169
+
170
+ if(tc_child_pid) {
171
+ info("Killing tc-qos-helper procees");
172
+ if(killpid(tc_child_pid, SIGTERM) != -1)
173
+ waitid(P_PID, (id_t) tc_child_pid, &info, WEXITED);
174
+ }
175
+ tc_child_pid = 0;
176
+
177
+ struct plugind *cd;
178
+ for(cd = pluginsd_root ; cd ; cd = cd->next) {
179
+ debug(D_EXIT, "Stopping %s plugin thread", cd->id);
180
+ pthread_cancel(cd->thread);
181
+ pthread_join(cd->thread, NULL);
182
+
183
+ if(cd->pid && !cd->obsolete) {
184
+ debug(D_EXIT, "killing %s plugin process", cd->id);
185
+ if(killpid(cd->pid, SIGTERM) != -1)
186
+ waitid(P_PID, (id_t) cd->pid, &info, WEXITED);
187
+ }
188
+ }
189
+
190
+ // if, for any reason there is any child exited
191
+ // catch it here
192
+ waitid(P_PID, 0, &info, WEXITED|WNOHANG);
193
+
194
+ debug(D_EXIT, "All threads/childs stopped.");
195
}
196
197
struct option_def options[] = {
198
- // opt description arg name default value
199
- {'c', "Load alternate configuration file", "config_file", CONFIG_DIR "/" CONFIG_FILENAME},
200
- {'D', "Disable fork into background", NULL, NULL},
201
- {'h', "Display help message", NULL, NULL},
202
- {'P', "File to save a pid while running", "FILE", NULL},
203
- {'i', "The IP address to listen to.", "address", "All addresses"},
204
- {'p', "Port to listen. Can be from 1 to 65535.", "port_number", "19999"},
205
- {'s', "Path to access host /proc and /sys when running in a container.", "PATH", NULL},
206
- {'t', "The frequency in seconds, for data collection. \
198
+ // opt description arg name default value
199
+ {'c', "Load alternate configuration file", "config_file", CONFIG_DIR "/" CONFIG_FILENAME},
200
+ {'D', "Disable fork into background", NULL, NULL},
201
+ {'h', "Display help message", NULL, NULL},
202
+ {'P', "File to save a pid while running", "FILE", NULL},
203
+ {'i', "The IP address to listen to.", "address", "All addresses"},
204
+ {'k', "Check daemon configuration.", NULL, NULL},
205
+ {'p', "Port to listen. Can be from 1 to 65535.", "port_number", "19999"},
206
+ {'s', "Path to access host /proc and /sys when running in a container.", "PATH", NULL},
207
+ {'t', "The frequency in seconds, for data collection. \
208
Same as 'update every' config file option.", "seconds", "1"},
208
- {'u', "System username to run as.", "username", "netdata"},
209
- {'v', "Version of the program", NULL, NULL},
210
- {'W', "vendor options.", "stacksize=<size>|unittest|debug_flag", NULL},
209
+ {'u', "System username to run as.", "username", "netdata"},
210
+ {'v', "Version of the program", NULL, NULL},
211
+ {'W', "vendor options.", "stacksize=<size>|unittest|debug_flag", NULL},
212
};
213
214
void help(int exitcode) {
214
- FILE *stream;
215
- if(exitcode == 0)
216
- stream = stdout;
217
- else
218
- stream = stderr;
219
-
220
- int num_opts = sizeof(options) / sizeof(struct option_def);
221
- int i;
222
- int max_len_arg = 0;
223
-
224
- // Compute maximum argument length
225
- for( i = 0; i < num_opts; i++ ) {
226
- if(options[i].arg_name) {
227
- int len_arg = strlen(options[i].arg_name);
228
- if(len_arg > max_len_arg) max_len_arg = len_arg;
229
- }
230
- }
231
-
232
- fprintf(stream, "SYNOPSIS: netdata [options]\n");
233
- fprintf(stream, "\n");
234
- fprintf(stream, "Options:\n");
235
-
236
- // Output options description.
237
- for( i = 0; i < num_opts; i++ ) {
238
- fprintf(stream, " -%c %-*s %s", options[i].val, max_len_arg, options[i].arg_name ? options[i].arg_name : "", options[i].description);
239
- if(options[i].default_value) {
240
- fprintf(stream, " Default: %s\n", options[i].default_value);
241
- } else {
242
- fprintf(stream, "\n");
243
- }
244
- }
245
-
246
- fflush(stream);
247
- exit(exitcode);
215
+ FILE *stream;
216
+ if(exitcode == 0)
217
+ stream = stdout;
218
+ else
219
+ stream = stderr;
220
+
221
+ int num_opts = sizeof(options) / sizeof(struct option_def);
222
+ int i;
223
+ int max_len_arg = 0;
224
+
225
+ // Compute maximum argument length
226
+ for( i = 0; i < num_opts; i++ ) {
227
+ if(options[i].arg_name) {
228
+ int len_arg = (int)strlen(options[i].arg_name);
229
+ if(len_arg > max_len_arg) max_len_arg = len_arg;
230
+ }
231
+ }
232
+
233
+ fprintf(stream, "SYNOPSIS: netdata [options]\n");
234
+ fprintf(stream, "\n");
235
+ fprintf(stream, "Options:\n");
236
+
237
+ // Output options description.
238
+ for( i = 0; i < num_opts; i++ ) {
239
+ fprintf(stream, " -%c %-*s %s", options[i].val, max_len_arg, options[i].arg_name ? options[i].arg_name : "", options[i].description);
240
+ if(options[i].default_value) {
241
+ fprintf(stream, " Default: %s\n", options[i].default_value);
242
+ } else {
243
+ fprintf(stream, "\n");
244
+ }
245
+ }
246
+
247
+ fflush(stream);
248
+ exit(exitcode);
249
}
250
251
// TODO: Remove this function with the nix major release.
252
void remove_option(int opt_index, int *argc, char **argv) {
252
- int i = opt_index;
253
- // remove the options.
254
- do {
255
- *argc = *argc - 1;
256
- for(i = opt_index; i < *argc; i++) {
257
- argv[i] = argv[i+1];
258
- }
259
- i = opt_index;
260
- } while(argv[i][0] != '-' && opt_index >= *argc);
253
+ int i = opt_index;
254
+ // remove the options.
255
+ do {
256
+ *argc = *argc - 1;
257
+ for(i = opt_index; i < *argc; i++) {
258
+ argv[i] = argv[i+1];
259
+ }
260
+ i = opt_index;
261
+ } while(argv[i][0] != '-' && opt_index >= *argc);
262
}
263
264
265
int main(int argc, char **argv)
266
{
266
- int i;
267
- int config_loaded = 0;
268
- int dont_fork = 0;
269
- size_t wanted_stacksize = 0, stacksize = 0;
270
- pthread_attr_t attr;
271
-
272
- // global initialization
273
- get_HZ();
274
-
275
- // set the name for logging
276
- program_name = "netdata";
277
-
278
- // parse command line.
279
-
280
- // parse depercated options
281
- // TODO: Remove this block with the next major release.
282
- {
283
- i = 1;
284
- while(i < argc) {
285
- if(strcmp(argv[i], "-pidfile") == 0 && (i+1) < argc) {
286
- strncpyz(pidfile, argv[i+1], FILENAME_MAX);
287
- fprintf(stderr, "%s: deprecated option -- %s -- please use -P instead.\n", argv[0], argv[i]);
288
- remove_option(i, &argc, argv);
289
- }
290
- else if(strcmp(argv[i], "-nodaemon") == 0 || strcmp(argv[i], "-nd") == 0) {
291
- dont_fork = 1;
292
- fprintf(stderr, "%s: deprecated option -- %s -- please use -D instead.\n ", argv[0], argv[i]);
293
- remove_option(i, &argc, argv);
294
- }
295
- else if(strcmp(argv[i], "-ch") == 0 && (i+1) < argc) {
296
- config_set("global", "host access prefix", argv[i+1]);
297
- fprintf(stderr, "%s: deprecated option -- %s -- please use -s instead.\n", argv[0], argv[i]);
298
- remove_option(i, &argc, argv);
299
- }
300
- else if(strcmp(argv[i], "-l") == 0 && (i+1) < argc) {
301
- config_set("global", "history", argv[i+1]);
302
- fprintf(stderr, "%s: deprecated option -- %s -- This option will be removed with V2.*.\n", argv[0], argv[i]);
303
- remove_option(i, &argc, argv);
304
- }
305
- else i++;
306
- }
307
- }
308
-
309
- // parse options
310
- {
311
- int num_opts = sizeof(options) / sizeof(struct option_def);
312
- char optstring[(num_opts * 2) + 1];
313
-
314
- int string_i = 0;
315
- for( i = 0; i < num_opts; i++ ) {
316
- optstring[string_i] = options[i].val;
317
- string_i++;
318
- if(options[i].arg_name) {
319
- optstring[string_i] = ':';
320
- string_i++;
321
- }
322
- }
323
-
324
- int opt;
325
- while( (opt = getopt(argc, argv, optstring)) != -1 ) {
326
- switch(opt) {
327
- case 'c':
328
- if(load_config(optarg, 1) != 1) {
329
- error("Cannot load configuration file %s.", optarg);
330
- exit(1);
331
- }
332
- else {
333
- debug(D_OPTIONS, "Configuration loaded from %s.", optarg);
334
- config_loaded = 1;
335
- }
336
- break;
337
- case 'D':
338
- dont_fork = 1;
339
- break;
340
- case 'h':
341
- help(0);
342
- break;
343
- case 'i':
344
- config_set("global", "bind to", optarg);
345
- break;
346
- case 'P':
347
- strncpy(pidfile, optarg, FILENAME_MAX);
348
- pidfile[FILENAME_MAX] = '\0';
349
- break;
350
- case 'p':
351
- config_set("global", "default port", optarg);
352
- break;
353
- case 's':
354
- config_set("global", "host access prefix", optarg);
355
- break;
356
- case 't':
357
- config_set("global", "update every", optarg);
358
- break;
359
- case 'u':
360
- config_set("global", "run as user", optarg);
361
- break;
362
- case 'v':
363
- // TODO: Outsource version to makefile which can compute version from git.
364
- printf("netdata 1.2.1_master\n");
365
- return 0;
366
- case 'W':
367
- {
368
- char* stacksize_string = "stacksize=";
369
- char* debug_flags_string = "debug_flags=";
370
- if(strcmp(optarg, "unittest") == 0) {
371
- rrd_update_every = 1;
372
- if(run_all_mockup_tests()) exit(1);
373
- if(unit_test_storage()) exit(1);
374
- fprintf(stderr, "\n\nALL TESTS PASSED\n\n");
375
- exit(0);
376
- } else if(strncmp(optarg, stacksize_string, strlen(stacksize_string)) == 0) {
377
- optarg += strlen(stacksize_string);
378
- config_set("global", "pthread stack size", optarg);
379
- } else if(strncmp(optarg, debug_flags_string, strlen(debug_flags_string)) == 0) {
380
- optarg += strlen(debug_flags_string);
381
- config_set("global", "debug flags", optarg);
382
- debug_flags = strtoull(optarg, NULL, 0);
383
- }
384
- }
385
- break;
386
- default: /* ? */
387
- help(1);
388
- break;
389
- }
390
- }
391
- }
392
-
393
- if(!config_loaded) load_config(NULL, 0);
394
-
395
- // prepare configuration environment variables for the plugins
396
- setenv("NETDATA_CONFIG_DIR" , config_get("global", "config directory" , CONFIG_DIR) , 1);
397
- setenv("NETDATA_PLUGINS_DIR", config_get("global", "plugins directory" , PLUGINS_DIR), 1);
398
- setenv("NETDATA_WEB_DIR" , config_get("global", "web files directory", WEB_DIR) , 1);
399
- setenv("NETDATA_CACHE_DIR" , config_get("global", "cache directory" , CACHE_DIR) , 1);
400
- setenv("NETDATA_LIB_DIR" , config_get("global", "lib directory" , VARLIB_DIR) , 1);
401
- setenv("NETDATA_LOG_DIR" , config_get("global", "log directory" , LOG_DIR) , 1);
402
- setenv("NETDATA_HOST_PREFIX", config_get("global", "host access prefix" , "") , 1);
403
- setenv("HOME" , config_get("global", "home directory" , CACHE_DIR) , 1);
404
-
405
- // disable buffering for python plugins
406
- setenv("PYTHONUNBUFFERED", "1", 1);
407
-
408
- // avoid flood calls to stat(/etc/localtime)
409
- // http://stackoverflow.com/questions/4554271/how-to-avoid-excessive-stat-etc-localtime-calls-in-strftime-on-linux
410
- setenv("TZ", ":/etc/localtime", 0);
411
-
412
- {
413
- char path[1024 + 1], *p = getenv("PATH");
414
- if(!p) p = "/bin:/usr/bin";
415
- snprintfz(path, 1024, "%s:%s", p, "/sbin:/usr/sbin:/usr/local/bin:/usr/local/sbin");
416
- setenv("PATH", config_get("plugins", "PATH environment variable", path), 1);
417
- }
418
-
419
- // cd to /tmp to avoid any plugins writing files at random places
420
- if(chdir("/tmp")) error("netdata: ERROR: Cannot cd to /tmp");
421
-
422
- char *user = NULL;
423
- {
424
- char *flags = config_get("global", "debug flags", "0x00000000");
425
- setenv("NETDATA_DEBUG_FLAGS", flags, 1);
426
-
427
- debug_flags = strtoull(flags, NULL, 0);
428
- debug(D_OPTIONS, "Debug flags set to '0x%8llx'.", debug_flags);
429
-
430
- if(debug_flags != 0) {
431
- struct rlimit rl = { RLIM_INFINITY, RLIM_INFINITY };
432
- if(setrlimit(RLIMIT_CORE, &rl) != 0)
433
- info("Cannot request unlimited core dumps for debugging... Proceeding anyway...");
434
- prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
435
- }
436
-
437
- // --------------------------------------------------------------------
267
+ int i, check_config = 0;
268
+ int config_loaded = 0;
269
+ int dont_fork = 0;
270
+ size_t wanted_stacksize = 0, stacksize = 0;
271
+ pthread_attr_t attr;
272
+
273
+ // global initialization
274
+ get_HZ();
275
+
276
+ // set the name for logging
277
+ program_name = "netdata";
278
+
279
+ // parse command line.
280
+
281
+ // parse depercated options
282
+ // TODO: Remove this block with the next major release.
283
+ {
284
+ i = 1;
285
+ while(i < argc) {
286
+ if(strcmp(argv[i], "-pidfile") == 0 && (i+1) < argc) {
287
+ strncpyz(pidfile, argv[i+1], FILENAME_MAX);
288
+ fprintf(stderr, "%s: deprecated option -- %s -- please use -P instead.\n", argv[0], argv[i]);
289
+ remove_option(i, &argc, argv);
290
+ }
291
+ else if(strcmp(argv[i], "-nodaemon") == 0 || strcmp(argv[i], "-nd") == 0) {
292
+ dont_fork = 1;
293
+ fprintf(stderr, "%s: deprecated option -- %s -- please use -D instead.\n ", argv[0], argv[i]);
294
+ remove_option(i, &argc, argv);
295
+ }
296
+ else if(strcmp(argv[i], "-ch") == 0 && (i+1) < argc) {
297
+ config_set("global", "host access prefix", argv[i+1]);
298
+ fprintf(stderr, "%s: deprecated option -- %s -- please use -s instead.\n", argv[0], argv[i]);
299
+ remove_option(i, &argc, argv);
300
+ }
301
+ else if(strcmp(argv[i], "-l") == 0 && (i+1) < argc) {
302
+ config_set("global", "history", argv[i+1]);
303
+ fprintf(stderr, "%s: deprecated option -- %s -- This option will be removed with V2.*.\n", argv[0], argv[i]);
304
+ remove_option(i, &argc, argv);
305
+ }
306
+ else i++;
307
+ }
308
+ }
309
+
310
+ // parse options
311
+ {
312
+ int num_opts = sizeof(options) / sizeof(struct option_def);
313
+ char optstring[(num_opts * 2) + 1];
314
+
315
+ int string_i = 0;
316
+ for( i = 0; i < num_opts; i++ ) {
317
+ optstring[string_i] = options[i].val;
318
+ string_i++;
319
+ if(options[i].arg_name) {
320
+ optstring[string_i] = ':';
321
+ string_i++;
322
+ }
323
+ }
324
+
325
+ int opt;
326
+ while( (opt = getopt(argc, argv, optstring)) != -1 ) {
327
+ switch(opt) {
328
+ case 'c':
329
+ if(load_config(optarg, 1) != 1) {
330
+ error("Cannot load configuration file %s.", optarg);
331
+ exit(1);
332
+ }
333
+ else {
334
+ debug(D_OPTIONS, "Configuration loaded from %s.", optarg);
335
+ config_loaded = 1;
336
+ }
337
+ break;
338
+ case 'D':
339
+ dont_fork = 1;
340
+ break;
341
+ case 'h':
342
+ help(0);
343
+ break;
344
+ case 'i':
345
+ config_set("global", "bind to", optarg);
346
+ break;
347
+ case 'k':
348
+ dont_fork = 1;
349
+ check_config = 1;
350
+ break;
351
+ case 'P':
352
+ strncpy(pidfile, optarg, FILENAME_MAX);
353
+ pidfile[FILENAME_MAX] = '\0';
354
+ break;
355
+ case 'p':
356
+ config_set("global", "default port", optarg);
357
+ break;
358
+ case 's':
359
+ config_set("global", "host access prefix", optarg);
360
+ break;
361
+ case 't':
362
+ config_set("global", "update every", optarg);
363
+ break;
364
+ case 'u':
365
+ config_set("global", "run as user", optarg);
366
+ break;
367
+ case 'v':
368
+ // TODO: Outsource version to makefile which can compute version from git.
369
+ printf("netdata 1.2.1_master\n");
370
+ return 0;
371
+ case 'W':
372
+ {
373
+ char* stacksize_string = "stacksize=";
374
+ char* debug_flags_string = "debug_flags=";
375
+ if(strcmp(optarg, "unittest") == 0) {
376
+ rrd_update_every = 1;
377
+ if(run_all_mockup_tests()) exit(1);
378
+ if(unit_test_storage()) exit(1);
379
+ fprintf(stderr, "\n\nALL TESTS PASSED\n\n");
380
+ exit(0);
381
+ } else if(strncmp(optarg, stacksize_string, strlen(stacksize_string)) == 0) {
382
+ optarg += strlen(stacksize_string);
383
+ config_set("global", "pthread stack size", optarg);
384
+ } else if(strncmp(optarg, debug_flags_string, strlen(debug_flags_string)) == 0) {
385
+ optarg += strlen(debug_flags_string);
386
+ config_set("global", "debug flags", optarg);
387
+ debug_flags = strtoull(optarg, NULL, 0);
388
+ }
389
+ }
390
+ break;
391
+ default: /* ? */
392
+ help(1);
393
+ break;
394
+ }
395
+ }
396
+ }
397
+
398
+ if(!config_loaded) load_config(NULL, 0);
399
+
400
+ // prepare configuration environment variables for the plugins
401
+ setenv("NETDATA_CONFIG_DIR" , config_get("global", "config directory" , CONFIG_DIR) , 1);
402
+ setenv("NETDATA_PLUGINS_DIR", config_get("global", "plugins directory" , PLUGINS_DIR), 1);
403
+ setenv("NETDATA_WEB_DIR" , config_get("global", "web files directory", WEB_DIR) , 1);
404
+ setenv("NETDATA_CACHE_DIR" , config_get("global", "cache directory" , CACHE_DIR) , 1);
405
+ setenv("NETDATA_LIB_DIR" , config_get("global", "lib directory" , VARLIB_DIR) , 1);
406
+ setenv("NETDATA_LOG_DIR" , config_get("global", "log directory" , LOG_DIR) , 1);
407
+ setenv("NETDATA_HOST_PREFIX", config_get("global", "host access prefix" , "") , 1);
408
+ setenv("HOME" , config_get("global", "home directory" , CACHE_DIR) , 1);
409
+
410
+ // disable buffering for python plugins
411
+ setenv("PYTHONUNBUFFERED", "1", 1);
412
+
413
+ // avoid flood calls to stat(/etc/localtime)
414
+ // http://stackoverflow.com/questions/4554271/how-to-avoid-excessive-stat-etc-localtime-calls-in-strftime-on-linux
415
+ setenv("TZ", ":/etc/localtime", 0);
416
+
417
+ {
418
+ char path[1024 + 1], *p = getenv("PATH");
419
+ if(!p) p = "/bin:/usr/bin";
420
+ snprintfz(path, 1024, "%s:%s", p, "/sbin:/usr/sbin:/usr/local/bin:/usr/local/sbin");
421
+ setenv("PATH", config_get("plugins", "PATH environment variable", path), 1);
422
+ }
423
+
424
+ // cd to /tmp to avoid any plugins writing files at random places
425
+ if(chdir("/tmp")) error("netdata: ERROR: Cannot cd to /tmp");
426
+
427
+ char *user = NULL;
428
+ {
429
+ char *flags = config_get("global", "debug flags", "0x00000000");
430
+ setenv("NETDATA_DEBUG_FLAGS", flags, 1);
431
+
432
+ debug_flags = strtoull(flags, NULL, 0);
433
+ debug(D_OPTIONS, "Debug flags set to '0x%8llx'.", debug_flags);
434
+
435
+ if(debug_flags != 0) {
436
+ struct rlimit rl = { RLIM_INFINITY, RLIM_INFINITY };
437
+ if(setrlimit(RLIMIT_CORE, &rl) != 0)
438
+ info("Cannot request unlimited core dumps for debugging... Proceeding anyway...");
439
+ prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
440
+ }
441
+
442
+ // --------------------------------------------------------------------
443
444
#ifdef MADV_MERGEABLE
440
- enable_ksm = config_get_boolean("global", "memory deduplication (ksm)", enable_ksm);
445
+ enable_ksm = config_get_boolean("global", "memory deduplication (ksm)", enable_ksm);
446
#else
447
#warning "Kernel memory deduplication (KSM) is not available"
448
#endif
449
445
- // --------------------------------------------------------------------
446
-
447
- global_host_prefix = config_get("global", "host access prefix", "");
448
- setenv("NETDATA_HOST_PREFIX", global_host_prefix, 1);
449
-
450
- // --------------------------------------------------------------------
451
-
452
- stdout_filename = config_get("global", "debug log", LOG_DIR "/debug.log");
453
- stderr_filename = config_get("global", "error log", LOG_DIR "/error.log");
454
- stdaccess_filename = config_get("global", "access log", LOG_DIR "/access.log");
455
-
456
- error_log_throttle_period = config_get_number("global", "errors flood protection period", error_log_throttle_period);
457
- setenv("NETDATA_ERRORS_THROTTLE_PERIOD", config_get("global", "errors flood protection period" , ""), 1);
458
-
459
- error_log_errors_per_period = (unsigned long)config_get_number("global", "errors to trigger flood protection", error_log_errors_per_period);
460
- setenv("NETDATA_ERRORS_PER_PERIOD" , config_get("global", "errors to trigger flood protection", ""), 1);
461
-
462
- // --------------------------------------------------------------------
463
-
464
- rrd_memory_mode = rrd_memory_mode_id(config_get("global", "memory mode", rrd_memory_mode_name(rrd_memory_mode)));
465
-
466
- // --------------------------------------------------------------------
467
-
468
- {
469
- char hostnamebuf[HOSTNAME_MAX + 1];
470
- if(gethostname(hostnamebuf, HOSTNAME_MAX) == -1)
471
- error("WARNING: Cannot get machine hostname.");
472
- hostname = config_get("global", "hostname", hostnamebuf);
473
- debug(D_OPTIONS, "hostname set to '%s'", hostname);
474
- }
475
-
476
- // --------------------------------------------------------------------
477
-
478
- rrd_default_history_entries = (int) config_get_number("global", "history", RRD_DEFAULT_HISTORY_ENTRIES);
479
- if(rrd_default_history_entries < 5 || rrd_default_history_entries > RRD_HISTORY_ENTRIES_MAX) {
480
- info("Invalid save lines %d given. Defaulting to %d.", rrd_default_history_entries, RRD_DEFAULT_HISTORY_ENTRIES);
481
- rrd_default_history_entries = RRD_DEFAULT_HISTORY_ENTRIES;
482
- }
483
- else {
484
- debug(D_OPTIONS, "save lines set to %d.", rrd_default_history_entries);
485
- }
486
-
487
- // --------------------------------------------------------------------
488
-
489
- rrd_update_every = (int) config_get_number("global", "update every", UPDATE_EVERY);
490
- if(rrd_update_every < 1 || rrd_update_every > 600) {
491
- info("Invalid update timer %d given. Defaulting to %d.", rrd_update_every, UPDATE_EVERY_MAX);
492
- rrd_update_every = UPDATE_EVERY;
493
- }
494
- else debug(D_OPTIONS, "update timer set to %d.", rrd_update_every);
495
-
496
- // let the plugins know the min update_every
497
- {
498
- char buf[16];
499
- snprintfz(buf, 15, "%d", rrd_update_every);
500
- setenv("NETDATA_UPDATE_EVERY", buf, 1);
501
- }
502
-
503
- // --------------------------------------------------------------------
504
-
505
- // block signals while initializing threads.
506
- // this causes the threads to block signals.
507
- sigset_t sigset;
508
- sigfillset(&sigset);
509
-
510
- if(pthread_sigmask(SIG_BLOCK, &sigset, NULL) == -1) {
511
- error("Could not block signals for threads");
512
- }
513
-
514
- // Catch signals which we want to use
515
- struct sigaction sa;
516
- sigemptyset(&sa.sa_mask);
517
- sigaddset(&sa.sa_mask, SIGINT);
518
- sigaddset(&sa.sa_mask, SIGTERM);
519
- sa.sa_handler = sig_handler_exit;
520
- sa.sa_flags = 0;
521
- if(sigaction(SIGINT, &sa, NULL) == -1) {
522
- error("Failed to change signal handler for SIGINT");
523
- }
524
- if(sigaction(SIGTERM, &sa, NULL) == -1) {
525
- error("Failed to change signal handler for SIGTERM");
526
- }
450
+ // --------------------------------------------------------------------
451
+
452
+ global_host_prefix = config_get("global", "host access prefix", "");
453
+ setenv("NETDATA_HOST_PREFIX", global_host_prefix, 1);
454
+
455
+ // --------------------------------------------------------------------
456
+
457
+ stdout_filename = config_get("global", "debug log", LOG_DIR "/debug.log");
458
+ stderr_filename = config_get("global", "error log", LOG_DIR "/error.log");
459
+ stdaccess_filename = config_get("global", "access log", LOG_DIR "/access.log");
460
+
461
+ error_log_throttle_period = config_get_number("global", "errors flood protection period", error_log_throttle_period);
462
+ setenv("NETDATA_ERRORS_THROTTLE_PERIOD", config_get("global", "errors flood protection period" , ""), 1);
463
+
464
+ error_log_errors_per_period = (unsigned long)config_get_number("global", "errors to trigger flood protection", error_log_errors_per_period);
465
+ setenv("NETDATA_ERRORS_PER_PERIOD" , config_get("global", "errors to trigger flood protection", ""), 1);
466
+
467
+ if(check_config) {
468
+ stdout_filename = stderr_filename = stdaccess_filename = "system";
469
+ error_log_throttle_period = 0;
470
+ error_log_errors_per_period = 0;
471
+ }
472
+
473
+ // --------------------------------------------------------------------
474
+
475
+ rrd_memory_mode = rrd_memory_mode_id(config_get("global", "memory mode", rrd_memory_mode_name(rrd_memory_mode)));
476
+
477
+ // --------------------------------------------------------------------
478
+
479
+ {
480
+ char hostnamebuf[HOSTNAME_MAX + 1];
481
+ if(gethostname(hostnamebuf, HOSTNAME_MAX) == -1)
482
+ error("WARNING: Cannot get machine hostname.");
483
+ hostname = config_get("global", "hostname", hostnamebuf);
484
+ debug(D_OPTIONS, "hostname set to '%s'", hostname);
485
+ }
486
+
487
+ // --------------------------------------------------------------------
488
+
489
+ rrd_default_history_entries = (int) config_get_number("global", "history", RRD_DEFAULT_HISTORY_ENTRIES);
490
+ if(rrd_default_history_entries < 5 || rrd_default_history_entries > RRD_HISTORY_ENTRIES_MAX) {
491
+ info("Invalid save lines %d given. Defaulting to %d.", rrd_default_history_entries, RRD_DEFAULT_HISTORY_ENTRIES);
492
+ rrd_default_history_entries = RRD_DEFAULT_HISTORY_ENTRIES;
493
+ }
494
+ else {
495
+ debug(D_OPTIONS, "save lines set to %d.", rrd_default_history_entries);
496
+ }
497
+
498
+ // --------------------------------------------------------------------
499
+
500
+ rrd_update_every = (int) config_get_number("global", "update every", UPDATE_EVERY);
501
+ if(rrd_update_every < 1 || rrd_update_every > 600) {
502
+ info("Invalid update timer %d given. Defaulting to %d.", rrd_update_every, UPDATE_EVERY_MAX);
503
+ rrd_update_every = UPDATE_EVERY;
504
+ }
505
+ else debug(D_OPTIONS, "update timer set to %d.", rrd_update_every);
506
+
507
+ // let the plugins know the min update_every
508
+ {
509
+ char buf[16];
510
+ snprintfz(buf, 15, "%d", rrd_update_every);
511
+ setenv("NETDATA_UPDATE_EVERY", buf, 1);
512
+ }
513
+
514
+ // --------------------------------------------------------------------
515
+
516
+ // block signals while initializing threads.
517
+ // this causes the threads to block signals.
518
+ sigset_t sigset;
519
+ sigfillset(&sigset);
520
+
521
+ if(pthread_sigmask(SIG_BLOCK, &sigset, NULL) == -1) {
522
+ error("Could not block signals for threads");
523
+ }
524
+
525
+ // Catch signals which we want to use
526
+ struct sigaction sa;
527
+ sigemptyset(&sa.sa_mask);
528
+ sigaddset(&sa.sa_mask, SIGINT);
529
+ sigaddset(&sa.sa_mask, SIGTERM);
530
+ sa.sa_handler = sig_handler_exit;
531
+ sa.sa_flags = 0;
532
+ if(sigaction(SIGINT, &sa, NULL) == -1) {
533
+ error("Failed to change signal handler for SIGINT");
534
+ }
535
+ if(sigaction(SIGTERM, &sa, NULL) == -1) {
536
+ error("Failed to change signal handler for SIGTERM");
537
+ }
538
539
sigemptyset(&sa.sa_mask);
540
sigaddset(&sa.sa_mask, SIGHUP);
@@ -533,72 +544,72 @@ int main(int argc, char **argv)
544
error("Failed to change signal handler for SIGHUP");
545
}
546
536
- // save database on SIGUSR1
547
+ // save database on SIGUSR1
548
sigemptyset(&sa.sa_mask);
549
sigaddset(&sa.sa_mask, SIGUSR1);
539
- sa.sa_handler = sig_handler_save;
540
- if(sigaction(SIGUSR1, &sa, NULL) == -1) {
541
- error("Failed to change signal handler for SIGUSR1");
542
- }
543
-
544
- // Ignore SIGPIPE completely.
545
- // INFO: If we add signals here we have to unblock them
546
- // at popen.c when running a external plugin.
550
+ sa.sa_handler = sig_handler_save;
551
+ if(sigaction(SIGUSR1, &sa, NULL) == -1) {
552
+ error("Failed to change signal handler for SIGUSR1");
553
+ }
554
+
555
+ // Ignore SIGPIPE completely.
556
+ // INFO: If we add signals here we have to unblock them
557
+ // at popen.c when running a external plugin.
558
sigemptyset(&sa.sa_mask);
559
sigaddset(&sa.sa_mask, SIGPIPE);
549
- sa.sa_handler = SIG_IGN;
550
- if(sigaction(SIGPIPE, &sa, NULL) == -1) {
551
- error("Failed to change signal handler for SIGPIPE");
552
- }
560
+ sa.sa_handler = SIG_IGN;
561
+ if(sigaction(SIGPIPE, &sa, NULL) == -1) {
562
+ error("Failed to change signal handler for SIGPIPE");
563
+ }
564
554
- // --------------------------------------------------------------------
565
+ // --------------------------------------------------------------------
566
556
- i = pthread_attr_init(&attr);
557
- if(i != 0)
558
- fatal("pthread_attr_init() failed with code %d.", i);
567
+ i = pthread_attr_init(&attr);
568
+ if(i != 0)
569
+ fatal("pthread_attr_init() failed with code %d.", i);
570
560
- i = pthread_attr_getstacksize(&attr, &stacksize);
561
- if(i != 0)
562
- fatal("pthread_attr_getstacksize() failed with code %d.", i);
563
- else
564
- debug(D_OPTIONS, "initial pthread stack size is %zu bytes", stacksize);
571
+ i = pthread_attr_getstacksize(&attr, &stacksize);
572
+ if(i != 0)
573
+ fatal("pthread_attr_getstacksize() failed with code %d.", i);
574
+ else
575
+ debug(D_OPTIONS, "initial pthread stack size is %zu bytes", stacksize);
576
566
- wanted_stacksize = (size_t)config_get_number("global", "pthread stack size", (long)stacksize);
577
+ wanted_stacksize = (size_t)config_get_number("global", "pthread stack size", (long)stacksize);
578
568
- // --------------------------------------------------------------------
579
+ // --------------------------------------------------------------------
580
570
- for (i = 0; static_threads[i].name != NULL ; i++) {
571
- struct netdata_static_thread *st = &static_threads[i];
581
+ for (i = 0; static_threads[i].name != NULL ; i++) {
582
+ struct netdata_static_thread *st = &static_threads[i];
583
573
- if(st->config_name) st->enabled = config_get_boolean(st->config_section, st->config_name, st->enabled);
574
- if(st->enabled && st->init_routine) st->init_routine();
575
- }
584
+ if(st->config_name) st->enabled = config_get_boolean(st->config_section, st->config_name, st->enabled);
585
+ if(st->enabled && st->init_routine) st->init_routine();
586
+ }
587
577
- // --------------------------------------------------------------------
588
+ // --------------------------------------------------------------------
589
579
- // get the user we should run
580
- // IMPORTANT: this is required before web_files_uid()
581
- user = config_get("global", "run as user" , (getuid() == 0)?NETDATA_USER:"");
590
+ // get the user we should run
591
+ // IMPORTANT: this is required before web_files_uid()
592
+ user = config_get("global", "run as user" , (getuid() == 0)?NETDATA_USER:"");
593
583
- // IMPORTANT: these have to run once, while single threaded
584
- web_files_uid(); // IMPORTANT: web_files_uid() before web_files_gid()
585
- web_files_gid();
594
+ // IMPORTANT: these have to run once, while single threaded
595
+ web_files_uid(); // IMPORTANT: web_files_uid() before web_files_gid()
596
+ web_files_gid();
597
587
- // --------------------------------------------------------------------
598
+ // --------------------------------------------------------------------
599
589
- create_listen_sockets();
590
- }
600
+ create_listen_sockets();
601
+ }
602
603
// initialize the log files
604
open_all_log_files();
605
606
#ifdef NETDATA_INTERNAL_CHECKS
596
- if(debug_flags != 0) {
597
- struct rlimit rl = { RLIM_INFINITY, RLIM_INFINITY };
598
- if(setrlimit(RLIMIT_CORE, &rl) != 0)
599
- info("Cannot request unlimited core dumps for debugging... Proceeding anyway...");
600
- prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
601
- }
607
+ if(debug_flags != 0) {
608
+ struct rlimit rl = { RLIM_INFINITY, RLIM_INFINITY };
609
+ if(setrlimit(RLIMIT_CORE, &rl) != 0)
610
+ info("Cannot request unlimited core dumps for debugging... Proceeding anyway...");
611
+ prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
612
+ }
613
#endif /* NETDATA_INTERNAL_CHECKS */
614
615
// fork, switch user, create pid file, set process priority
@@ -608,65 +619,68 @@ int main(int argc, char **argv)
619
info("NetData started on pid %d", getpid());
620
621
611
- // ------------------------------------------------------------------------
612
- // get default pthread stack size
622
+ // ------------------------------------------------------------------------
623
+ // get default pthread stack size
624
614
- if(stacksize < wanted_stacksize) {
615
- i = pthread_attr_setstacksize(&attr, wanted_stacksize);
616
- if(i != 0)
617
- fatal("pthread_attr_setstacksize() to %zu bytes, failed with code %d.", wanted_stacksize, i);
618
- else
619
- info("Successfully set pthread stacksize to %zu bytes", wanted_stacksize);
620
- }
625
+ if(stacksize < wanted_stacksize) {
626
+ i = pthread_attr_setstacksize(&attr, wanted_stacksize);
627
+ if(i != 0)
628
+ fatal("pthread_attr_setstacksize() to %zu bytes, failed with code %d.", wanted_stacksize, i);
629
+ else
630
+ info("Successfully set pthread stacksize to %zu bytes", wanted_stacksize);
631
+ }
632
622
- // ------------------------------------------------------------------------
623
- // initialize the registry
633
+ // ------------------------------------------------------------------------
634
+ // initialize the registry
635
625
- registry_init();
636
+ registry_init();
637
638
// ------------------------------------------------------------------------
639
// initialize health monitoring
640
641
health_init();
642
632
- // ------------------------------------------------------------------------
633
- // spawn the threads
643
+ if(check_config)
644
+ exit(1);
645
635
- web_server_threading_selection();
636
-
637
- for (i = 0; static_threads[i].name != NULL ; i++) {
638
- struct netdata_static_thread *st = &static_threads[i];
646
+ // ------------------------------------------------------------------------
647
+ // spawn the threads
648
640
- if(st->enabled) {
641
- st->thread = mallocz(sizeof(pthread_t));
649
+ web_server_threading_selection();
650
643
- info("Starting thread %s.", st->name);
651
+ for (i = 0; static_threads[i].name != NULL ; i++) {
652
+ struct netdata_static_thread *st = &static_threads[i];
653
645
- if(pthread_create(st->thread, &attr, st->start_routine, NULL))
646
- error("failed to create new thread for %s.", st->name);
654
+ if(st->enabled) {
655
+ st->thread = mallocz(sizeof(pthread_t));
656
648
- else if(pthread_detach(*st->thread))
649
- error("Cannot request detach of newly created %s thread.", st->name);
650
- }
651
- else info("Not starting thread %s.", st->name);
652
- }
657
+ info("Starting thread %s.", st->name);
658
654
- // ------------------------------------------------------------------------
655
- // block signals while initializing threads.
656
- sigset_t sigset;
657
- sigfillset(&sigset);
659
+ if(pthread_create(st->thread, &attr, st->start_routine, NULL))
660
+ error("failed to create new thread for %s.", st->name);
661
659
- if(pthread_sigmask(SIG_UNBLOCK, &sigset, NULL) == -1) {
660
- error("Could not unblock signals for threads");
661
- }
662
+ else if(pthread_detach(*st->thread))
663
+ error("Cannot request detach of newly created %s thread.", st->name);
664
+ }
665
+ else info("Not starting thread %s.", st->name);
666
+ }
667
663
- // Handle flags set in the signal handler.
664
- while(1) {
665
- pause();
666
- if(netdata_exit) {
667
- info("Exit main loop of netdata.");
668
- netdata_cleanup_and_exit(0);
669
- exit(0);
670
- }
671
- }
668
+ // ------------------------------------------------------------------------
669
+ // block signals while initializing threads.
670
+ sigset_t sigset;
671
+ sigfillset(&sigset);
672
+
673
+ if(pthread_sigmask(SIG_UNBLOCK, &sigset, NULL) == -1) {
674
+ error("Could not unblock signals for threads");
675
+ }
676
+
677
+ // Handle flags set in the signal handler.
678
+ while(1) {
679
+ pause();
680
+ if(netdata_exit) {
681
+ info("Exit main loop of netdata.");
682
+ netdata_cleanup_and_exit(0);
683
+ exit(0);
684
+ }
685
+ }
686
}
src/web_client.c
+4
-4
@@ -551,7 +551,7 @@ uint32_t web_client_api_request_v1_data_google_format(char *name)
551
return DATASOURCE_JSON;
552
}
553
554
-int web_client_api_request_v1_data_group(char *name)
554
+int web_client_api_request_v1_data_group(char *name, int def)
555
{
556
if(!strcmp(name, "max"))
557
return GROUP_MAX;
@@ -565,7 +565,7 @@ int web_client_api_request_v1_data_group(char *name)
565
else if(!strcmp(name, "incremental-sum"))
566
return GROUP_INCREMENTAL_SUM;
567
568
- return GROUP_AVERAGE;
568
+ return def;
569
}
570
571
int web_client_api_request_v1_charts(struct web_client *w, char *url)
@@ -671,7 +671,7 @@ int web_client_api_v1_badge(struct web_client *w, char *url) {
671
else if(!strcmp(name, "before")) before_str = value;
672
else if(!strcmp(name, "points")) points_str = value;
673
else if(!strcmp(name, "group")) {
674
- group = web_client_api_request_v1_data_group(value);
674
+ group = web_client_api_request_v1_data_group(value, GROUP_AVERAGE);
675
}
676
else if(!strcmp(name, "options")) {
677
options |= web_client_api_request_v1_data_options(value);
@@ -830,7 +830,7 @@ int web_client_api_request_v1_data(struct web_client *w, char *url)
830
else if(!strcmp(name, "before")) before_str = value;
831
else if(!strcmp(name, "points")) points_str = value;
832
else if(!strcmp(name, "group")) {
833
- group = web_client_api_request_v1_data_group(value);
833
+ group = web_client_api_request_v1_data_group(value, GROUP_AVERAGE);
834
}
835
else if(!strcmp(name, "format")) {
836
format = web_client_api_request_v1_data_format(value);
src/web_client.h
+1
@@ -103,4 +103,5 @@ extern void web_client_reset(struct web_client *w);
103
104
extern void *web_client_main(void *ptr);
105
106
+extern int web_client_api_request_v1_data_group(char *name, int def);
107
#endif