@cryptotaxi247 / netdata-1 / commits / a82c482bf

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