summaryrefslogtreecommitdiffstats
path: root/daemon
diff options
context:
space:
mode:
Diffstat (limited to 'daemon')
-rw-r--r--daemon/analytics.c885
-rw-r--r--daemon/analytics.h90
-rwxr-xr-xdaemon/anonymous-statistics.sh.in72
-rw-r--r--daemon/buildinfo.c47
-rw-r--r--daemon/common.h3
-rw-r--r--daemon/config/README.md2
-rw-r--r--daemon/global_statistics.c155
-rw-r--r--daemon/global_statistics.h11
-rw-r--r--daemon/main.c235
-rw-r--r--daemon/signals.c2
-rw-r--r--daemon/unit_test.c2
11 files changed, 1228 insertions, 276 deletions
diff --git a/daemon/analytics.c b/daemon/analytics.c
new file mode 100644
index 00000000..08923a3c
--- /dev/null
+++ b/daemon/analytics.c
@@ -0,0 +1,885 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "common.h"
+
+struct analytics_data analytics_data;
+extern void analytics_exporting_connectors (BUFFER *b);
+extern void analytics_build_info (BUFFER *b);
+extern int aclk_connected;
+
+struct collector {
+ char *plugin;
+ char *module;
+};
+
+struct array_printer {
+ int c;
+ BUFFER *both;
+};
+
+/*
+ * Debug logging
+ */
+void analytics_log_data(void)
+{
+ debug(D_ANALYTICS, "NETDATA_CONFIG_STREAM_ENABLED : [%s]", analytics_data.netdata_config_stream_enabled);
+ debug(D_ANALYTICS, "NETDATA_CONFIG_MEMORY_MODE : [%s]", analytics_data.netdata_config_memory_mode);
+ debug(D_ANALYTICS, "NETDATA_CONFIG_EXPORTING_ENABLED : [%s]", analytics_data.netdata_config_exporting_enabled);
+ debug(D_ANALYTICS, "NETDATA_EXPORTING_CONNECTORS : [%s]", analytics_data.netdata_exporting_connectors);
+ debug(D_ANALYTICS, "NETDATA_ALLMETRICS_PROMETHEUS_USED : [%s]", analytics_data.netdata_allmetrics_prometheus_used);
+ debug(D_ANALYTICS, "NETDATA_ALLMETRICS_SHELL_USED : [%s]", analytics_data.netdata_allmetrics_shell_used);
+ debug(D_ANALYTICS, "NETDATA_ALLMETRICS_JSON_USED : [%s]", analytics_data.netdata_allmetrics_json_used);
+ debug(D_ANALYTICS, "NETDATA_DASHBOARD_USED : [%s]", analytics_data.netdata_dashboard_used);
+ debug(D_ANALYTICS, "NETDATA_COLLECTORS : [%s]", analytics_data.netdata_collectors);
+ debug(D_ANALYTICS, "NETDATA_COLLECTORS_COUNT : [%s]", analytics_data.netdata_collectors_count);
+ debug(D_ANALYTICS, "NETDATA_BUILDINFO : [%s]", analytics_data.netdata_buildinfo);
+ debug(D_ANALYTICS, "NETDATA_CONFIG_PAGE_CACHE_SIZE : [%s]", analytics_data.netdata_config_page_cache_size);
+ debug(D_ANALYTICS, "NETDATA_CONFIG_MULTIDB_DISK_QUOTA : [%s]", analytics_data.netdata_config_multidb_disk_quota);
+ debug(D_ANALYTICS, "NETDATA_CONFIG_HTTPS_ENABLED : [%s]", analytics_data.netdata_config_https_enabled);
+ debug(D_ANALYTICS, "NETDATA_CONFIG_WEB_ENABLED : [%s]", analytics_data.netdata_config_web_enabled);
+ debug(D_ANALYTICS, "NETDATA_CONFIG_RELEASE_CHANNEL : [%s]", analytics_data.netdata_config_release_channel);
+ debug(D_ANALYTICS, "NETDATA_MIRRORED_HOST_COUNT : [%s]", analytics_data.netdata_mirrored_host_count);
+ debug(D_ANALYTICS, "NETDATA_MIRRORED_HOSTS_REACHABLE : [%s]", analytics_data.netdata_mirrored_hosts_reachable);
+ debug(D_ANALYTICS, "NETDATA_MIRRORED_HOSTS_UNREACHABLE : [%s]", analytics_data.netdata_mirrored_hosts_unreachable);
+ debug(D_ANALYTICS, "NETDATA_NOTIFICATION_METHODS : [%s]", analytics_data.netdata_notification_methods);
+ debug(D_ANALYTICS, "NETDATA_ALARMS_NORMAL : [%s]", analytics_data.netdata_alarms_normal);
+ debug(D_ANALYTICS, "NETDATA_ALARMS_WARNING : [%s]", analytics_data.netdata_alarms_warning);
+ debug(D_ANALYTICS, "NETDATA_ALARMS_CRITICAL : [%s]", analytics_data.netdata_alarms_critical);
+ debug(D_ANALYTICS, "NETDATA_CHARTS_COUNT : [%s]", analytics_data.netdata_charts_count);
+ debug(D_ANALYTICS, "NETDATA_METRICS_COUNT : [%s]", analytics_data.netdata_metrics_count);
+ debug(D_ANALYTICS, "NETDATA_CONFIG_IS_PARENT : [%s]", analytics_data.netdata_config_is_parent);
+ debug(D_ANALYTICS, "NETDATA_CONFIG_HOSTS_AVAILABLE : [%s]", analytics_data.netdata_config_hosts_available);
+ debug(D_ANALYTICS, "NETDATA_HOST_CLOUD_AVAILABLE : [%s]", analytics_data.netdata_host_cloud_available);
+ debug(D_ANALYTICS, "NETDATA_HOST_ACLK_AVAILABLE : [%s]", analytics_data.netdata_host_aclk_available);
+ debug(D_ANALYTICS, "NETDATA_HOST_ACLK_IMPLEMENTATION : [%s]", analytics_data.netdata_host_aclk_implementation);
+ debug(D_ANALYTICS, "NETDATA_HOST_AGENT_CLAIMED : [%s]", analytics_data.netdata_host_agent_claimed);
+ debug(D_ANALYTICS, "NETDATA_HOST_CLOUD_ENABLED : [%s]", analytics_data.netdata_host_cloud_enabled);
+}
+
+/*
+ * Free data
+ */
+void analytics_free_data(void)
+{
+ freez(analytics_data.netdata_config_stream_enabled);
+ freez(analytics_data.netdata_config_memory_mode);
+ freez(analytics_data.netdata_config_exporting_enabled);
+ freez(analytics_data.netdata_exporting_connectors);
+ freez(analytics_data.netdata_allmetrics_prometheus_used);
+ freez(analytics_data.netdata_allmetrics_shell_used);
+ freez(analytics_data.netdata_allmetrics_json_used);
+ freez(analytics_data.netdata_dashboard_used);
+ freez(analytics_data.netdata_collectors);
+ freez(analytics_data.netdata_collectors_count);
+ freez(analytics_data.netdata_buildinfo);
+ freez(analytics_data.netdata_config_page_cache_size);
+ freez(analytics_data.netdata_config_multidb_disk_quota);
+ freez(analytics_data.netdata_config_https_enabled);
+ freez(analytics_data.netdata_config_web_enabled);
+ freez(analytics_data.netdata_config_release_channel);
+ freez(analytics_data.netdata_mirrored_host_count);
+ freez(analytics_data.netdata_mirrored_hosts_reachable);
+ freez(analytics_data.netdata_mirrored_hosts_unreachable);
+ freez(analytics_data.netdata_notification_methods);
+ freez(analytics_data.netdata_alarms_normal);
+ freez(analytics_data.netdata_alarms_warning);
+ freez(analytics_data.netdata_alarms_critical);
+ freez(analytics_data.netdata_charts_count);
+ freez(analytics_data.netdata_metrics_count);
+ freez(analytics_data.netdata_config_is_parent);
+ freez(analytics_data.netdata_config_hosts_available);
+ freez(analytics_data.netdata_host_cloud_available);
+ freez(analytics_data.netdata_host_aclk_available);
+ freez(analytics_data.netdata_host_aclk_implementation);
+ freez(analytics_data.netdata_host_agent_claimed);
+ freez(analytics_data.netdata_host_cloud_enabled);
+}
+
+/*
+ * Set a numeric/boolean data with a value
+ */
+void analytics_set_data(char **name, char *value)
+{
+ if (*name) {
+ analytics_data.data_length -= strlen(*name);
+ freez(*name);
+ }
+ *name = strdupz(value);
+ analytics_data.data_length += strlen(*name);
+}
+
+/*
+ * Set a string data with a value
+ */
+void analytics_set_data_str(char **name, char *value)
+{
+ size_t value_string_len;
+ if (*name) {
+ analytics_data.data_length -= strlen(*name);
+ freez(*name);
+ }
+ value_string_len = strlen(value) + 4;
+ *name = mallocz(sizeof(char) * value_string_len);
+ snprintfz(*name, value_string_len - 1, "\"%s\"", value);
+ analytics_data.data_length += strlen(*name);
+}
+
+/*
+ * Get data, used by web api v1
+ */
+void analytics_get_data(char *name, BUFFER *wb)
+{
+ buffer_strcat(wb, name);
+}
+
+/*
+ * Log hits on the allmetrics page, with prometheus parameter
+ */
+void analytics_log_prometheus(void)
+{
+ if (likely(analytics_data.prometheus_hits < ANALYTICS_MAX_PROMETHEUS_HITS)) {
+ analytics_data.prometheus_hits++;
+ char b[7];
+ snprintfz(b, 6, "%d", analytics_data.prometheus_hits);
+ analytics_set_data(&analytics_data.netdata_allmetrics_prometheus_used, b);
+ }
+}
+
+/*
+ * Log hits on the allmetrics page, with shell parameter (or default)
+ */
+void analytics_log_shell(void)
+{
+ if (likely(analytics_data.shell_hits < ANALYTICS_MAX_SHELL_HITS)) {
+ analytics_data.shell_hits++;
+ char b[7];
+ snprintfz(b, 6, "%d", analytics_data.shell_hits);
+ analytics_set_data(&analytics_data.netdata_allmetrics_shell_used, b);
+ }
+}
+
+/*
+ * Log hits on the allmetrics page, with json parameter
+ */
+void analytics_log_json(void)
+{
+ if (likely(analytics_data.json_hits < ANALYTICS_MAX_JSON_HITS)) {
+ analytics_data.json_hits++;
+ char b[7];
+ snprintfz(b, 6, "%d", analytics_data.json_hits);
+ analytics_set_data(&analytics_data.netdata_allmetrics_json_used, b);
+ }
+}
+
+/*
+ * Log hits on the dashboard, (when calling HELLO).
+ */
+void analytics_log_dashboard(void)
+{
+ if (likely(analytics_data.dashboard_hits < ANALYTICS_MAX_DASHBOARD_HITS)) {
+ analytics_data.dashboard_hits++;
+ char b[7];
+ snprintfz(b, 6, "%d", analytics_data.dashboard_hits);
+ analytics_set_data(&analytics_data.netdata_dashboard_used, b);
+ }
+}
+
+void analytics_mirrored_hosts(void)
+{
+ RRDHOST *host;
+ int count = 0;
+ int reachable = 0;
+ int unreachable = 0;
+ char b[11];
+
+ rrd_rdlock();
+ rrdhost_foreach_read(host)
+ {
+ if (rrdhost_flag_check(host, RRDHOST_FLAG_ARCHIVED))
+ continue;
+
+ netdata_mutex_lock(&host->receiver_lock);
+ ((host->receiver || host == localhost) ? reachable++ : unreachable++);
+ netdata_mutex_unlock(&host->receiver_lock);
+
+ count++;
+ }
+ rrd_unlock();
+
+ snprintfz(b, 10, "%d", count);
+ analytics_set_data(&analytics_data.netdata_mirrored_host_count, b);
+ snprintfz(b, 10, "%d", reachable);
+ analytics_set_data(&analytics_data.netdata_mirrored_hosts_reachable, b);
+ snprintfz(b, 10, "%d", unreachable);
+ analytics_set_data(&analytics_data.netdata_mirrored_hosts_unreachable, b);
+}
+
+void analytics_exporters(void)
+{
+ //when no exporters are available, an empty string will be sent
+ //decide if something else is more suitable (but propably not null)
+ BUFFER *bi = buffer_create(1000);
+ analytics_exporting_connectors(bi);
+ analytics_set_data_str(&analytics_data.netdata_exporting_connectors, (char *)buffer_tostring(bi));
+ buffer_free(bi);
+}
+
+int collector_counter_callb(void *entry, void *data)
+{
+ struct array_printer *ap = (struct array_printer *)data;
+ struct collector *col = (struct collector *)entry;
+
+ BUFFER *bt = ap->both;
+
+ if (likely(ap->c)) {
+ buffer_strcat(bt, ",");
+ }
+
+ buffer_strcat(bt, "{");
+ buffer_strcat(bt, " \"plugin\": \"");
+ buffer_strcat(bt, col->plugin);
+ buffer_strcat(bt, "\", \"module\":\"");
+ buffer_strcat(bt, col->module);
+ buffer_strcat(bt, "\" }");
+
+ (ap->c)++;
+
+ return 0;
+}
+
+/*
+ * Create a JSON array of available collectors, same as in api/v1/info
+ */
+void analytics_collectors(void)
+{
+ RRDSET *st;
+ DICTIONARY *dict = dictionary_create(DICTIONARY_FLAG_SINGLE_THREADED);
+ char name[500];
+ BUFFER *bt = buffer_create(1000);
+
+ rrdset_foreach_read(st, localhost)
+ {
+ if (rrdset_is_available_for_viewers(st)) {
+ struct collector col = { .plugin = st->plugin_name ? st->plugin_name : "",
+ .module = st->module_name ? st->module_name : "" };
+ snprintfz(name, 499, "%s:%s", col.plugin, col.module);
+ dictionary_set(dict, name, &col, sizeof(struct collector));
+ }
+ }
+
+ struct array_printer ap;
+ ap.c = 0;
+ ap.both = bt;
+
+ dictionary_get_all(dict, collector_counter_callb, &ap);
+ dictionary_destroy(dict);
+
+ analytics_set_data(&analytics_data.netdata_collectors, (char *)buffer_tostring(ap.both));
+
+ {
+ char b[7];
+ snprintfz(b, 6, "%d", ap.c);
+ analytics_set_data(&analytics_data.netdata_collectors_count, b);
+ }
+
+ buffer_free(bt);
+}
+
+/*
+ * Run alarm-notify.sh script using the dump_methods parameter
+ * SEND_CUSTOM is always available
+ */
+void analytics_alarms_notifications(void)
+{
+ char *script;
+ script = mallocz(
+ sizeof(char) * (strlen(netdata_configured_primary_plugins_dir) + strlen("alarm-notify.sh dump_methods") + 2));
+ sprintf(script, "%s/%s", netdata_configured_primary_plugins_dir, "alarm-notify.sh");
+ if (unlikely(access(script, R_OK) != 0)) {
+ info("Alarm notify script %s not found.", script);
+ freez(script);
+ return;
+ }
+
+ strcat(script, " dump_methods");
+
+ pid_t command_pid;
+
+ debug(D_ANALYTICS, "Executing %s", script);
+
+ BUFFER *b = buffer_create(1000);
+ int cnt = 0;
+ FILE *fp = mypopen(script, &command_pid);
+ if (fp) {
+ char line[200 + 1];
+
+ while (fgets(line, 200, fp) != NULL) {
+ char *end = line;
+ while (*end && *end != '\n')
+ end++;
+ *end = '\0';
+
+ if (likely(cnt))
+ buffer_strcat(b, "|");
+
+ buffer_strcat(b, line);
+
+ cnt++;
+ }
+ mypclose(fp, command_pid);
+ }
+ freez(script);
+
+ analytics_set_data_str(&analytics_data.netdata_notification_methods, (char *)buffer_tostring(b));
+
+ buffer_free(b);
+}
+
+void analytics_charts(void)
+{
+ RRDSET *st;
+ int c = 0;
+ rrdset_foreach_read(st, localhost)
+ {
+ if (rrdset_is_available_for_viewers(st)) {
+ c++;
+ }
+ }
+ {
+ char b[7];
+ snprintfz(b, 6, "%d", c);
+ analytics_set_data(&analytics_data.netdata_charts_count, b);
+ }
+}
+
+void analytics_metrics(void)
+{
+ RRDSET *st;
+ long int dimensions = 0;
+ RRDDIM *rd;
+ rrdset_foreach_read(st, localhost)
+ {
+ rrdset_rdlock(st);
+ rrddim_foreach_read(rd, st)
+ {
+ if (rrddim_flag_check(rd, RRDDIM_FLAG_HIDDEN) || rrddim_flag_check(rd, RRDDIM_FLAG_OBSOLETE))
+ continue;
+ dimensions++;
+ }
+ rrdset_unlock(st);
+ }
+ {
+ char b[7];
+ snprintfz(b, 6, "%ld", dimensions);
+ analytics_set_data(&analytics_data.netdata_metrics_count, b);
+ }
+}
+
+void analytics_alarms(void)
+{
+ int alarm_warn = 0, alarm_crit = 0, alarm_normal = 0;
+ char b[10];
+ RRDCALC *rc;
+ for (rc = localhost->alarms; rc; rc = rc->next) {
+ if (unlikely(!rc->rrdset || !rc->rrdset->last_collected_time.tv_sec))
+ continue;
+
+ switch (rc->status) {
+ case RRDCALC_STATUS_WARNING:
+ alarm_warn++;
+ break;
+ case RRDCALC_STATUS_CRITICAL:
+ alarm_crit++;
+ break;
+ default:
+ alarm_normal++;
+ }
+ }
+
+ snprintfz(b, 9, "%d", alarm_normal);
+ analytics_set_data(&analytics_data.netdata_alarms_normal, b);
+ snprintfz(b, 9, "%d", alarm_warn);
+ analytics_set_data(&analytics_data.netdata_alarms_warning, b);
+ snprintfz(b, 9, "%d", alarm_crit);
+ analytics_set_data(&analytics_data.netdata_alarms_critical, b);
+}
+
+/*
+ * Misc attributes to get (run from meta)
+ */
+void analytics_misc(void)
+{
+#ifdef ENABLE_ACLK
+ analytics_set_data(&analytics_data.netdata_host_cloud_available, "true");
+#ifdef ACLK_NG
+ analytics_set_data_str(&analytics_data.netdata_host_aclk_implementation, "Next Generation");
+#else
+ analytics_set_data_str(&analytics_data.netdata_host_aclk_implementation, "legacy");
+#endif
+#else
+ analytics_set_data(&analytics_data.netdata_host_cloud_available, "false");
+#endif
+
+#ifdef ENABLE_ACLK
+ if (aclk_connected)
+ analytics_set_data(&analytics_data.netdata_host_aclk_available, "true");
+ else
+#endif
+ analytics_set_data(&analytics_data.netdata_host_aclk_available, "false");
+}
+
+/*
+ * Get the meta data, called from the thread once after the original delay
+ * These are values that won't change between agent restarts, and therefore
+ * don't try to read them on each META event send
+ */
+void analytics_gather_immutable_meta_data(void)
+{
+ analytics_misc();
+ analytics_exporters();
+}
+
+/*
+ * Get the meta data, called from the thread on every heartbeat, and right before the EXIT event
+ * These are values that can change between agent restarts, and therefore
+ * try to read them on each META event send
+ */
+void analytics_gather_mutable_meta_data(void)
+{
+ rrdhost_rdlock(localhost);
+
+ analytics_collectors();
+ analytics_alarms();
+ analytics_charts();
+ analytics_metrics();
+
+ rrdhost_unlock(localhost);
+
+ analytics_mirrored_hosts();
+ analytics_alarms_notifications();
+
+ analytics_set_data(
+ &analytics_data.netdata_config_is_parent, (localhost->next || configured_as_parent()) ? "true" : "false");
+
+ char *claim_id = is_agent_claimed();
+ analytics_set_data(&analytics_data.netdata_host_agent_claimed, claim_id ? "true" : "false");
+ freez(claim_id);
+
+ {
+ char b[7];
+ snprintfz(b, 6, "%d", analytics_data.prometheus_hits);
+ analytics_set_data(&analytics_data.netdata_allmetrics_prometheus_used, b);
+
+ snprintfz(b, 6, "%d", analytics_data.shell_hits);
+ analytics_set_data(&analytics_data.netdata_allmetrics_shell_used, b);
+
+ snprintfz(b, 6, "%d", analytics_data.json_hits);
+ analytics_set_data(&analytics_data.netdata_allmetrics_json_used, b);
+
+ snprintfz(b, 6, "%d", analytics_data.dashboard_hits);
+ analytics_set_data(&analytics_data.netdata_dashboard_used, b);
+
+ snprintfz(b, 6, "%zu", rrd_hosts_available);
+ analytics_set_data(&analytics_data.netdata_config_hosts_available, b);
+ }
+}
+
+void analytics_main_cleanup(void *ptr)
+{
+ struct netdata_static_thread *static_thread = (struct netdata_static_thread *)ptr;
+ static_thread->enabled = NETDATA_MAIN_THREAD_EXITING;
+
+ debug(D_ANALYTICS, "Cleaning up...");
+
+ static_thread->enabled = NETDATA_MAIN_THREAD_EXITED;
+}
+
+/*
+ * The analytics thread. Sleep for ANALYTICS_INIT_SLEEP_SEC,
+ * gather the data, and then go to a loop where every ANALYTICS_HEARTBEAT
+ * it will send a new META event after gathering data that could be changed
+ * while the agent is running
+ */
+void *analytics_main(void *ptr)
+{
+ netdata_thread_cleanup_push(analytics_main_cleanup, ptr);
+ unsigned int sec = 0;
+ heartbeat_t hb;
+ heartbeat_init(&hb);
+ usec_t step_ut = USEC_PER_SEC;
+
+ debug(D_ANALYTICS, "Analytics thread starts");
+
+ //first delay after agent start
+ while (!netdata_exit && likely(sec <= ANALYTICS_INIT_SLEEP_SEC)) {
+ heartbeat_next(&hb, step_ut);
+ sec++;
+ }
+
+ if (unlikely(netdata_exit))
+ goto cleanup;
+
+ analytics_gather_immutable_meta_data();
+ analytics_gather_mutable_meta_data();
+ send_statistics("META", "-", "-");
+ analytics_log_data();
+
+ sec = 0;
+ while (1) {
+ heartbeat_next(&hb, step_ut * 2);
+ sec += 2;
+
+ if (unlikely(netdata_exit))
+ break;
+
+ if (likely(sec < ANALYTICS_HEARTBEAT))
+ continue;
+
+ analytics_gather_mutable_meta_data();
+ send_statistics("META", "-", "-");
+ analytics_log_data();
+ sec = 0;
+ }
+
+cleanup:
+ netdata_thread_cleanup_pop(1);
+ return NULL;
+}
+
+static const char *verify_required_directory(const char *dir)
+{
+ if (chdir(dir) == -1)
+ fatal("Cannot change directory to '%s'", dir);
+
+ DIR *d = opendir(dir);
+ if (!d)
+ fatal("Cannot examine the contents of directory '%s'", dir);
+ closedir(d);
+
+ return dir;
+}
+
+/*
+ * This is called after the rrdinit
+ * These values will be sent on the START event
+ */
+void set_late_global_environment()
+{
+ analytics_set_data(&analytics_data.netdata_config_stream_enabled, default_rrdpush_enabled ? "true" : "false");
+ analytics_set_data_str(&analytics_data.netdata_config_memory_mode, (char *)rrd_memory_mode_name(default_rrd_memory_mode));
+ analytics_set_data(&analytics_data.netdata_config_exporting_enabled, appconfig_get_boolean(&exporting_config, CONFIG_SECTION_EXPORTING, "enabled", CONFIG_BOOLEAN_NO) ? "true" : "false");
+
+#ifdef DISABLE_CLOUD
+ analytics_set_data(&analytics_data.netdata_host_cloud_enabled, "false");
+#else
+ analytics_set_data(
+ &analytics_data.netdata_host_cloud_enabled,
+ appconfig_get_boolean(&cloud_config, CONFIG_SECTION_GLOBAL, "enabled", CONFIG_BOOLEAN_YES) ? "true" : "false");
+#endif
+
+#ifdef ENABLE_DBENGINE
+ {
+ char b[16];
+ snprintfz(b, 15, "%d", default_rrdeng_page_cache_mb);
+ analytics_set_data(&analytics_data.netdata_config_page_cache_size, b);
+
+ snprintfz(b, 15, "%d", default_multidb_disk_quota_mb);
+ analytics_set_data(&analytics_data.netdata_config_multidb_disk_quota, b);
+ }
+#endif
+
+#ifdef ENABLE_HTTPS
+ analytics_set_data(&analytics_data.netdata_config_https_enabled, "true");
+#else
+ analytics_set_data(&analytics_data.netdata_config_https_enabled, "false");
+#endif
+
+ if (web_server_mode == WEB_SERVER_MODE_NONE)
+ analytics_set_data(&analytics_data.netdata_config_web_enabled, "false");
+ else
+ analytics_set_data(&analytics_data.netdata_config_web_enabled, "true");
+
+ analytics_set_data_str(&analytics_data.netdata_config_release_channel, (char *)get_release_channel());
+
+ {
+ BUFFER *bi = buffer_create(1000);
+ analytics_build_info(bi);
+ analytics_set_data_str(&analytics_data.netdata_buildinfo, (char *)buffer_tostring(bi));
+ buffer_free(bi);
+ }
+}
+
+static void get_system_timezone(void)
+{
+ // avoid flood calls to stat(/etc/localtime)
+ // http://stackoverflow.com/questions/4554271/how-to-avoid-excessive-stat-etc-localtime-calls-in-strftime-on-linux
+ const char *tz = getenv("TZ");
+ if (!tz || !*tz)
+ setenv("TZ", config_get(CONFIG_SECTION_GLOBAL, "TZ environment variable", ":/etc/localtime"), 0);
+
+ char buffer[FILENAME_MAX + 1] = "";
+ const char *timezone = NULL;
+ ssize_t ret;
+
+ // use the TZ variable
+ if (tz && *tz && *tz != ':') {
+ timezone = tz;
+ info("TIMEZONE: using TZ variable '%s'", timezone);
+ }
+
+ // use the contents of /etc/timezone
+ if (!timezone && !read_file("/etc/timezone", buffer, FILENAME_MAX)) {
+ timezone = buffer;
+ info("TIMEZONE: using the contents of /etc/timezone: '%s'", timezone);
+ }
+
+ // read the link /etc/localtime
+ if (!timezone) {
+ ret = readlink("/etc/localtime", buffer, FILENAME_MAX);
+
+ if (ret > 0) {
+ buffer[ret] = '\0';
+
+ char *cmp = "/usr/share/zoneinfo/";
+ size_t cmp_len = strlen(cmp);
+
+ char *s = strstr(buffer, cmp);
+ if (s && s[cmp_len]) {
+ timezone = &s[cmp_len];
+ info("TIMEZONE: using the link of /etc/localtime: '%s'", timezone);
+ }
+ } else
+ buffer[0] = '\0';
+ }
+
+ // find the timezone from strftime()
+ if (!timezone) {
+ time_t t;
+ struct tm *tmp, tmbuf;
+
+ t = now_realtime_sec();
+ tmp = localtime_r(&t, &tmbuf);
+
+ if (tmp != NULL) {
+ if (strftime(buffer, FILENAME_MAX, "%Z", tmp) == 0)
+ buffer[0] = '\0';
+ else {
+ buffer[FILENAME_MAX] = '\0';
+ timezone = buffer;
+ info("TIMEZONE: using strftime(): '%s'", timezone);
+ }
+ }
+ }
+
+ if (timezone && *timezone) {
+ // make sure it does not have illegal characters
+ // info("TIMEZONE: fixing '%s'", timezone);
+
+ size_t len = strlen(timezone);
+ char tmp[len + 1];
+ char *d = tmp;
+ *d = '\0';
+
+ while (*timezone) {
+ if (isalnum(*timezone) || *timezone == '_' || *timezone == '/')
+ *d++ = *timezone++;
+ else
+ timezone++;
+ }
+ *d = '\0';
+ strncpyz(buffer, tmp, len);
+ timezone = buffer;
+ info("TIMEZONE: fixed as '%s'", timezone);
+ }
+
+ if (!timezone || !*timezone)
+ timezone = "unknown";
+
+ netdata_configured_timezone = config_get(CONFIG_SECTION_GLOBAL, "timezone", timezone);
+}
+
+void set_global_environment()
+{
+ {
+ char b[16];
+ snprintfz(b, 15, "%d", default_rrd_update_every);
+ setenv("NETDATA_UPDATE_EVERY", b, 1);
+ }
+
+ setenv("NETDATA_VERSION", program_version, 1);
+ setenv("NETDATA_HOSTNAME", netdata_configured_hostname, 1);
+ setenv("NETDATA_CONFIG_DIR", verify_required_directory(netdata_configured_user_config_dir), 1);
+ setenv("NETDATA_USER_CONFIG_DIR", verify_required_directory(netdata_configured_user_config_dir), 1);
+ setenv("NETDATA_STOCK_CONFIG_DIR", verify_required_directory(netdata_configured_stock_config_dir), 1);
+ setenv("NETDATA_PLUGINS_DIR", verify_required_directory(netdata_configured_primary_plugins_dir), 1);
+ setenv("NETDATA_WEB_DIR", verify_required_directory(netdata_configured_web_dir), 1);
+ setenv("NETDATA_CACHE_DIR", verify_required_directory(netdata_configured_cache_dir), 1);
+ setenv("NETDATA_LIB_DIR", verify_required_directory(netdata_configured_varlib_dir), 1);
+ setenv("NETDATA_LOCK_DIR", netdata_configured_lock_dir, 1);
+ setenv("NETDATA_LOG_DIR", verify_required_directory(netdata_configured_log_dir), 1);
+ setenv("HOME", verify_required_directory(netdata_configured_home_dir), 1);
+ setenv("NETDATA_HOST_PREFIX", netdata_configured_host_prefix, 1);
+
+ analytics_data.data_length = 0;
+ analytics_set_data(&analytics_data.netdata_config_stream_enabled, "null");
+ analytics_set_data(&analytics_data.netdata_config_memory_mode, "null");
+ analytics_set_data(&analytics_data.netdata_config_exporting_enabled, "null");
+ analytics_set_data(&analytics_data.netdata_exporting_connectors, "null");
+ analytics_set_data(&analytics_data.netdata_allmetrics_prometheus_used, "null");
+ analytics_set_data(&analytics_data.netdata_allmetrics_shell_used, "null");
+ analytics_set_data(&analytics_data.netdata_allmetrics_json_used, "null");
+ analytics_set_data(&analytics_data.netdata_dashboard_used, "null");
+ analytics_set_data(&analytics_data.netdata_collectors, "null");
+ analytics_set_data(&analytics_data.netdata_collectors_count, "null");
+ analytics_set_data(&analytics_data.netdata_buildinfo, "null");
+ analytics_set_data(&analytics_data.netdata_config_page_cache_size, "null");
+ analytics_set_data(&analytics_data.netdata_config_multidb_disk_quota, "null");
+ analytics_set_data(&analytics_data.netdata_config_https_enabled, "null");
+ analytics_set_data(&analytics_data.netdata_config_web_enabled, "null");
+ analytics_set_data(&analytics_data.netdata_config_release_channel, "null");
+ analytics_set_data(&analytics_data.netdata_mirrored_host_count, "null");
+ analytics_set_data(&analytics_data.netdata_mirrored_hosts_reachable, "null");
+ analytics_set_data(&analytics_data.netdata_mirrored_hosts_unreachable, "null");
+ analytics_set_data(&analytics_data.netdata_notification_methods, "null");
+ analytics_set_data(&analytics_data.netdata_alarms_normal, "null");
+ analytics_set_data(&analytics_data.netdata_alarms_warning, "null");
+ analytics_set_data(&analytics_data.netdata_alarms_critical, "null");
+ analytics_set_data(&analytics_data.netdata_charts_count, "null");
+ analytics_set_data(&analytics_data.netdata_metrics_count, "null");
+ analytics_set_data(&analytics_data.netdata_config_is_parent, "null");
+ analytics_set_data(&analytics_data.netdata_config_hosts_available, "null");
+ analytics_set_data(&analytics_data.netdata_host_cloud_available, "null");
+ analytics_set_data(&analytics_data.netdata_host_aclk_implementation, "null");
+ analytics_set_data(&analytics_data.netdata_host_aclk_available, "null");
+ analytics_set_data(&analytics_data.netdata_host_agent_claimed, "null");
+ analytics_set_data(&analytics_data.netdata_host_cloud_enabled, "null");
+
+ analytics_data.prometheus_hits = 0;
+ analytics_data.shell_hits = 0;
+ analytics_data.json_hits = 0;
+ analytics_data.dashboard_hits = 0;
+
+ char *default_port = appconfig_get(&netdata_config, CONFIG_SECTION_WEB, "default port", NULL);
+ int clean = 0;
+ if (!default_port) {
+ default_port = strdupz("19999");
+ clean = 1;
+ }
+
+ setenv("NETDATA_LISTEN_PORT", default_port, 1);
+ if (clean)
+ freez(default_port);
+
+ get_system_timezone();
+
+ // set the path we need
+ char path[1024 + 1], *p = getenv("PATH");
+ if (!p)
+ p = "/bin:/usr/bin";
+ snprintfz(path, 1024, "%s:%s", p, "/sbin:/usr/sbin:/usr/local/bin:/usr/local/sbin");
+ setenv("PATH", config_get(CONFIG_SECTION_PLUGINS, "PATH environment variable", path), 1);
+
+ // python options
+ p = getenv("PYTHONPATH");
+ if (!p)
+ p = "";
+ setenv("PYTHONPATH", config_get(CONFIG_SECTION_PLUGINS, "PYTHONPATH environment variable", p), 1);
+
+ // disable buffering for python plugins
+ setenv("PYTHONUNBUFFERED", "1", 1);
+
+ // switch to standard locale for plugins
+ setenv("LC_ALL", "C", 1);
+}
+
+void send_statistics(const char *action, const char *action_result, const char *action_data)
+{
+ static char *as_script;
+
+ if (netdata_anonymous_statistics_enabled == -1) {
+ char *optout_file = mallocz(
+ sizeof(char) *
+ (strlen(netdata_configured_user_config_dir) + strlen(".opt-out-from-anonymous-statistics") + 2));
+ sprintf(optout_file, "%s/%s", netdata_configured_user_config_dir, ".opt-out-from-anonymous-statistics");
+ if (likely(access(optout_file, R_OK) != 0)) {
+ as_script = mallocz(
+ sizeof(char) *
+ (strlen(netdata_configured_primary_plugins_dir) + strlen("anonymous-statistics.sh") + 2));
+ sprintf(as_script, "%s/%s", netdata_configured_primary_plugins_dir, "anonymous-statistics.sh");
+ if (unlikely(access(as_script, R_OK) != 0)) {
+ netdata_anonymous_statistics_enabled = 0;
+ info("Anonymous statistics script %s not found.", as_script);
+ freez(as_script);
+ } else {
+ netdata_anonymous_statistics_enabled = 1;
+ }
+ } else {
+ netdata_anonymous_statistics_enabled = 0;
+ as_script = NULL;
+ }
+ freez(optout_file);
+ }
+ if (!netdata_anonymous_statistics_enabled)
+ return;
+ if (!action)
+ return;
+ if (!action_result)
+ action_result = "";
+ if (!action_data)
+ action_data = "";
+ char *command_to_run = mallocz(
+ sizeof(char) * (strlen(action) + strlen(action_result) + strlen(action_data) + strlen(as_script) +
+ analytics_data.data_length + (ANALYTICS_NO_OF_ITEMS * 3) + 15));
+ pid_t command_pid;
+
+ sprintf(
+ command_to_run,
+ "%s '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' ",
+ as_script,
+ action,
+ action_result,
+ action_data,
+ analytics_data.netdata_config_stream_enabled,
+ analytics_data.netdata_config_memory_mode,
+ analytics_data.netdata_config_exporting_enabled,
+ analytics_data.netdata_exporting_connectors,
+ analytics_data.netdata_allmetrics_prometheus_used,
+ analytics_data.netdata_allmetrics_shell_used,
+ analytics_data.netdata_allmetrics_json_used,
+ analytics_data.netdata_dashboard_used,
+ analytics_data.netdata_collectors,
+ analytics_data.netdata_collectors_count,
+ analytics_data.netdata_buildinfo,
+ analytics_data.netdata_config_page_cache_size,
+ analytics_data.netdata_config_multidb_disk_quota,
+ analytics_data.netdata_config_https_enabled,
+ analytics_data.netdata_config_web_enabled,
+ analytics_data.netdata_config_release_channel,
+ analytics_data.netdata_mirrored_host_count,
+ analytics_data.netdata_mirrored_hosts_reachable,
+ analytics_data.netdata_mirrored_hosts_unreachable,
+ analytics_data.netdata_notification_methods,
+ analytics_data.netdata_alarms_normal,
+ analytics_data.netdata_alarms_warning,
+ analytics_data.netdata_alarms_critical,
+ analytics_data.netdata_charts_count,
+ analytics_data.netdata_metrics_count,
+ analytics_data.netdata_config_is_parent,
+ analytics_data.netdata_config_hosts_available,
+ analytics_data.netdata_host_cloud_available,
+ analytics_data.netdata_host_aclk_available,
+ analytics_data.netdata_host_aclk_implementation,
+ analytics_data.netdata_host_agent_claimed,
+ analytics_data.netdata_host_cloud_enabled);
+
+ info("%s '%s' '%s' '%s'", as_script, action, action_result, action_data);
+
+ FILE *fp = mypopen(command_to_run, &command_pid);
+ if (fp) {
+ char buffer[100 + 1];
+ while (fgets(buffer, 100, fp) != NULL)
+ ;
+ mypclose(fp, command_pid);
+ }
+ freez(command_to_run);
+}
diff --git a/daemon/analytics.h b/daemon/analytics.h
new file mode 100644
index 00000000..e888297d
--- /dev/null
+++ b/daemon/analytics.h
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#ifndef NETDATA_ANALYTICS_H
+#define NETDATA_ANALYTICS_H 1
+
+#include "../daemon/common.h"
+
+/* Max number of seconds before the first META analytics is sent */
+#define ANALYTICS_INIT_SLEEP_SEC 120
+
+/* Send a META event every X seconds */
+#define ANALYTICS_HEARTBEAT 7200
+
+/* Maximum number of hits to log */
+#define ANALYTICS_MAX_PROMETHEUS_HITS 255
+#define ANALYTICS_MAX_SHELL_HITS 255
+#define ANALYTICS_MAX_JSON_HITS 255
+#define ANALYTICS_MAX_DASHBOARD_HITS 255
+
+#define NETDATA_PLUGIN_HOOK_ANALYTICS \
+ { \
+ .name = "ANALYTICS", \
+ .config_section = NULL, \
+ .config_name = NULL, \
+ .enabled = 0, \
+ .thread = NULL, \
+ .init_routine = NULL, \
+ .start_routine = analytics_main \
+ },
+
+/* Needed to calculate the space needed for parameters */
+#define ANALYTICS_NO_OF_ITEMS 32
+
+struct analytics_data {
+ char *netdata_config_stream_enabled;
+ char *netdata_config_memory_mode;
+ char *netdata_exporting_connectors;
+ char *netdata_config_exporting_enabled;
+ char *netdata_allmetrics_prometheus_used;
+ char *netdata_allmetrics_shell_used;
+ char *netdata_allmetrics_json_used;
+ char *netdata_dashboard_used;
+ char *netdata_collectors;
+ char *netdata_collectors_count;
+ char *netdata_buildinfo;
+ char *netdata_config_page_cache_size;
+ char *netdata_config_multidb_disk_quota;
+ char *netdata_config_https_enabled;
+ char *netdata_config_web_enabled;
+ char *netdata_config_release_channel;
+ char *netdata_mirrored_host_count;
+ char *netdata_mirrored_hosts_reachable;
+ char *netdata_mirrored_hosts_unreachable;
+ char *netdata_notification_methods;
+ char *netdata_alarms_normal;
+ char *netdata_alarms_warning;
+ char *netdata_alarms_critical;
+ char *netdata_charts_count;
+ char *netdata_metrics_count;
+ char *netdata_config_is_parent;
+ char *netdata_config_hosts_available;
+ char *netdata_host_cloud_available;
+ char *netdata_host_aclk_available;
+ char *netdata_host_aclk_implementation;
+ char *netdata_host_agent_claimed;
+ char *netdata_host_cloud_enabled;
+
+ size_t data_length;
+
+ uint8_t prometheus_hits;
+ uint8_t shell_hits;
+ uint8_t json_hits;
+ uint8_t dashboard_hits;
+};
+
+extern void *analytics_main(void *ptr);
+extern void analytics_get_data(char *name, BUFFER *wb);
+extern void set_late_global_environment(void);
+extern void analytics_free_data(void);
+extern void set_global_environment(void);
+extern void send_statistics(const char *action, const char *action_result, const char *action_data);
+extern void analytics_log_shell(void);
+extern void analytics_log_json(void);
+extern void analytics_log_prometheus(void);
+extern void analytics_log_dashboard(void);
+extern void analytics_gather_mutable_meta_data(void);
+
+extern struct analytics_data analytics_data;
+
+#endif //NETDATA_ANALYTICS_H
diff --git a/daemon/anonymous-statistics.sh.in b/daemon/anonymous-statistics.sh.in
index 47004f3d..bd22963d 100755
--- a/daemon/anonymous-statistics.sh.in
+++ b/daemon/anonymous-statistics.sh.in
@@ -26,6 +26,40 @@ fi
NETDATA_VERSION=$(echo "${NETDATA_VERSION}" | sed 's/-.*//g' | tr -d 'v')
# -------------------------------------------------------------------------------------------------
+# Get the extra variables
+
+NETDATA_CONFIG_STREAM_ENABLED="${4}"
+NETDATA_CONFIG_MEMORY_MODE="${5}"
+NETDATA_CONFIG_EXPORTING_ENABLED="${6}"
+NETDATA_EXPORTING_CONNECTORS="${7}"
+NETDATA_ALLMETRICS_PROMETHEUS_USED="${8}"
+NETDATA_ALLMETRICS_SHELL_USED="${9}"
+NETDATA_ALLMETRICS_JSON_USED="${10}"
+NETDATA_DASHBOARD_USED="${11}"
+NETDATA_COLLECTORS="${12}"
+NETDATA_COLLECTORS_COUNT="${13}"
+NETDATA_BUILDINFO="${14}"
+NETDATA_CONFIG_PAGE_CACHE_SIZE="${15}"
+NETDATA_CONFIG_MULTIDB_DISK_QUOTA="${16}"
+NETDATA_CONFIG_HTTPS_ENABLED="${17}"
+NETDATA_CONFIG_WEB_ENABLED="${18}"
+NETDATA_CONFIG_RELEASE_CHANNEL="${19}"
+NETDATA_MIRRORED_HOST_COUNT="${20}"
+NETDATA_MIRRORED_HOSTS_REACHABLE="${21}"
+NETDATA_MIRRORED_HOSTS_UNREACHABLE="${22}"
+NETDATA_NOTIFICATION_METHODS="${23}"
+NETDATA_ALARMS_NORMAL="${24}"
+NETDATA_ALARMS_WARNING="${25}"
+NETDATA_ALARMS_CRITICAL="${26}"
+NETDATA_CHARTS_COUNT="${27}"
+NETDATA_METRICS_COUNT="${28}"
+NETDATA_CONFIG_IS_PARENT="${29}"
+NETDATA_CONFIG_HOSTS_AVAILABLE="${30}"
+NETDATA_HOST_CLOUD_AVAILABLE="${31}"
+NETDATA_HOST_ACLK_AVAILABLE="${32}"
+NETDATA_HOST_ACLK_IMPLEMENTATION="${33}"
+NETDATA_HOST_AGENT_CLAIMED="${34}"
+NETDATA_HOST_CLOUD_ENABLED="${35}"
# define body of request to be sent
REQ_BODY="$(cat << EOF
@@ -44,6 +78,8 @@ REQ_BODY="$(cat << EOF
"action_data": "${ACTION_DATA}",
"netdata_machine_guid": "${NETDATA_REGISTRY_UNIQUE_ID}",
"netdata_version": "${NETDATA_VERSION}",
+ "netdata_buildinfo": ${NETDATA_BUILDINFO},
+ "netdata_release_channel": ${NETDATA_CONFIG_RELEASE_CHANNEL},
"host_os_name": "${NETDATA_HOST_OS_NAME}",
"host_os_id": "${NETDATA_HOST_OS_ID}",
"host_os_id_like": "${NETDATA_HOST_OS_ID_LIKE}",
@@ -72,7 +108,39 @@ REQ_BODY="$(cat << EOF
"system_disk_detection": "${NETDATA_SYSTEM_DISK_DETECTION}",
"system_ram_detection": "${NETDATA_SYSTEM_RAM_DETECTION}",
"system_total_disk_size": "${NETDATA_SYSTEM_TOTAL_DISK_SIZE}",
- "system_total_ram": "${NETDATA_SYSTEM_TOTAL_RAM}"
+ "system_total_ram": "${NETDATA_SYSTEM_TOTAL_RAM}",
+ "config_stream_enabled": ${NETDATA_CONFIG_STREAM_ENABLED},
+ "config_memory_mode": ${NETDATA_CONFIG_MEMORY_MODE},
+ "config_page_cache_size": ${NETDATA_CONFIG_PAGE_CACHE_SIZE},
+ "config_multidb_disk_quota": ${NETDATA_CONFIG_MULTIDB_DISK_QUOTA},
+ "config_https_enabled": ${NETDATA_CONFIG_HTTPS_ENABLED},
+ "config_web_enabled": ${NETDATA_CONFIG_WEB_ENABLED},
+ "config_exporting_enabled": ${NETDATA_CONFIG_EXPORTING_ENABLED},
+ "config_is_parent": ${NETDATA_CONFIG_IS_PARENT},
+ "config_hosts_available": ${NETDATA_CONFIG_HOSTS_AVAILABLE},
+ "alarms_normal": ${NETDATA_ALARMS_NORMAL},
+ "alarms_warning": ${NETDATA_ALARMS_WARNING},
+ "alarms_critical": ${NETDATA_ALARMS_CRITICAL},
+ "host_charts_count": ${NETDATA_CHARTS_COUNT},
+ "host_metrics_count": ${NETDATA_METRICS_COUNT},
+ "host_collectors":[
+ ${NETDATA_COLLECTORS}
+ ],
+ "host_collectors_count": ${NETDATA_COLLECTORS_COUNT},
+ "host_notification_methods": ${NETDATA_NOTIFICATION_METHODS},
+ "host_allmetrics_prometheus_used": ${NETDATA_ALLMETRICS_PROMETHEUS_USED},
+ "host_allmetrics_shell_used": ${NETDATA_ALLMETRICS_SHELL_USED},
+ "host_allmetrics_json_used": ${NETDATA_ALLMETRICS_JSON_USED},
+ "host_dashboard_used": ${NETDATA_DASHBOARD_USED},
+ "host_cloud_available": ${NETDATA_HOST_CLOUD_AVAILABLE},
+ "host_cloud_enabled": ${NETDATA_HOST_CLOUD_ENABLED},
+ "host_agent_claimed": ${NETDATA_HOST_AGENT_CLAIMED},
+ "host_aclk_available": ${NETDATA_HOST_ACLK_AVAILABLE},
+ "host_aclk_implementation": ${NETDATA_HOST_ACLK_IMPLEMENTATION},
+ "mirrored_host_count": ${NETDATA_MIRRORED_HOST_COUNT},
+ "mirrored_hosts_reachable": ${NETDATA_MIRRORED_HOSTS_REACHABLE},
+ "mirrored_hosts_unreachable": ${NETDATA_MIRRORED_HOSTS_UNREACHABLE},
+ "exporting_connectors": ${NETDATA_EXPORTING_CONNECTORS}
}
}
EOF
@@ -80,7 +148,7 @@ EOF
# send the anonymous statistics to the Netdata PostHog
if [ -n "$(command -v curl 2> /dev/null)" ]; then
- curl -X POST --header "Content-Type: application/json" -d "${REQ_BODY}" https://posthog.netdata.cloud/capture/ > /dev/null 2>&1
+ curl -X POST --max-time 2 --header "Content-Type: application/json" -d "${REQ_BODY}" https://posthog.netdata.cloud/capture/ > /dev/null 2>&1
else
wget -q -O - --no-check-certificate \
--method POST \
diff --git a/daemon/buildinfo.c b/daemon/buildinfo.c
index b1639054..ebeaa996 100644
--- a/daemon/buildinfo.c
+++ b/daemon/buildinfo.c
@@ -2,6 +2,7 @@
#include <stdio.h>
#include "./config.h"
+#include "common.h"
// Optional features
@@ -312,3 +313,49 @@ void print_build_info_json(void) {
printf(" }\n");
printf("}\n");
};
+
+//return a list of enabled features for use in analytics
+//find a way to have proper |
+void analytics_build_info(BUFFER *b) {
+ if(FEAT_DBENGINE) buffer_strcat (b, "dbengine");
+ if(FEAT_NATIVE_HTTPS) buffer_strcat (b, "|Native HTTPS");
+ if(FEAT_CLOUD) buffer_strcat (b, "|Netdata Cloud");
+ if(FEAT_TLS_HOST_VERIFY) buffer_strcat (b, "|TLS Host Verification");
+
+ if(FEAT_JEMALLOC) buffer_strcat (b, "|jemalloc");
+ if(FEAT_JSONC) buffer_strcat (b, "|JSON-C");
+ if(FEAT_LIBCAP) buffer_strcat (b, "|libcap");
+ if(FEAT_CRYPTO) buffer_strcat (b, "|libcrypto");
+ if(FEAT_LIBM) buffer_strcat (b, "|libm");
+
+#ifndef ACLK_NG
+#if defined(ENABLE_ACLK)
+ {
+ char buf[20];
+ snprintfz(buf, 19, "|LWS v%d.%d.%d", LWS_LIBRARY_VERSION_MAJOR, LWS_LIBRARY_VERSION_MINOR, LWS_LIBRARY_VERSION_PATCH);
+ if(FEAT_LWS) buffer_strcat(b, buf);
+ }
+#else
+ if(FEAT_LWS) buffer_strcat(b, "|LWS");
+#endif
+ if(FEAT_MOSQUITTO) buffer_strcat(b, "|mosquitto");
+#endif
+ if(FEAT_TCMALLOC) buffer_strcat(b, "|tcalloc");
+ if(FEAT_ZLIB) buffer_strcat(b, "|zlib");
+
+ if(FEAT_APPS_PLUGIN) buffer_strcat(b, "|apps");
+ if(FEAT_CGROUP_NET) buffer_strcat(b, "|cgroup Network Tracking");
+ if(FEAT_CUPS) buffer_strcat(b, "|CUPS");
+ if(FEAT_EBPF) buffer_strcat(b, "|EBPF");
+ if(FEAT_IPMI) buffer_strcat(b, "|IPMI");
+ if(FEAT_NFACCT) buffer_strcat(b, "|NFACCT");
+ if(FEAT_PERF) buffer_strcat(b, "|perf");
+ if(FEAT_SLABINFO) buffer_strcat(b, "|slabinfo");
+ if(FEAT_XEN) buffer_strcat(b, "|Xen");
+ if(FEAT_XEN_VBD_ERROR) buffer_strcat(b, "|Xen VBD Error Tracking");
+
+ if(FEAT_KINESIS) buffer_strcat(b, "|AWS Kinesis");
+ if(FEAT_PUBSUB) buffer_strcat(b, "|GCP PubSub");
+ if(FEAT_MONGO) buffer_strcat(b, "|MongoDB");
+ if(FEAT_REMOTE_WRITE) buffer_strcat(b, "|Prometheus Remote Write");
+}
diff --git a/daemon/common.h b/daemon/common.h
index 1a58ddda..4cb54010 100644
--- a/daemon/common.h
+++ b/daemon/common.h
@@ -77,11 +77,12 @@
// netdata agent spawn server
#include "spawn/spawn.h"
-// the netdata deamon
+// the netdata daemon
#include "daemon.h"
#include "main.h"
#include "signals.h"
#include "commands.h"
+#include "analytics.h"
// global netdata daemon variables
extern char *netdata_configured_hostname;
diff --git a/daemon/config/README.md b/daemon/config/README.md
index b1e790a2..cc755af7 100644
--- a/daemon/config/README.md
+++ b/daemon/config/README.md
@@ -50,7 +50,7 @@ Please note that your data history will be lost if you have modified `history` p
| setting|default|info|||
|:-----:|:-----:|:---|---|---|
| process scheduling policy|`keep`|See [Netdata process scheduling policy](/daemon/README.md#netdata-process-scheduling-policy)|||
-| OOM score|`1000`|See [OOM score](../#oom-score)|||
+| OOM score|`1000`|See [OOM score](/daemon/README.md#oom-score)|||
| glibc malloc arena max for plugins|`1`|See [Virtual memory](/daemon/README.md#virtual-memory).|||
| glibc malloc arena max for Netdata|`1`|See [Virtual memory](/daemon/README.md#virtual-memory).|||
| hostname|auto-detected|The hostname of the computer running Netdata.|||
diff --git a/daemon/global_statistics.c b/daemon/global_statistics.c
index 7e783551..edd26147 100644
--- a/daemon/global_statistics.c
+++ b/daemon/global_statistics.c
@@ -4,6 +4,7 @@
#define GLOBAL_STATS_RESET_WEB_USEC_MAX 0x01
+#define CONFIG_SECTION_GLOBAL_STATISTICS "global statistics"
static struct global_statistics {
volatile uint16_t connected_clients;
@@ -180,63 +181,17 @@ void global_statistics_charts(void) {
static collected_number compression_ratio = -1,
average_response_time = -1;
+ static time_t netdata_start_time = 0;
+ if (!netdata_start_time)
+ netdata_start_time = now_boottime_sec();
+ time_t netdata_uptime = now_boottime_sec() - netdata_start_time;
+
struct global_statistics gs;
- struct rusage me, thread;
+ struct rusage me;
global_statistics_copy(&gs, GLOBAL_STATS_RESET_WEB_USEC_MAX);
- getrusage(RUSAGE_THREAD, &thread);
getrusage(RUSAGE_SELF, &me);
- {
- static RRDSET *st_cpu_thread = NULL;
- static RRDDIM *rd_cpu_thread_user = NULL,
- *rd_cpu_thread_system = NULL;
-
-#ifdef __FreeBSD__
- if (unlikely(!st_cpu_thread)) {
- st_cpu_thread = rrdset_create_localhost(
- "netdata"
- , "plugin_freebsd_cpu"
- , NULL
- , "freebsd"
- , NULL
- , "NetData FreeBSD Plugin CPU usage"
- , "milliseconds/s"
- , "netdata"
- , "stats"
- , 132000
- , localhost->rrd_update_every
- , RRDSET_TYPE_STACKED
- );
-#else
- if (unlikely(!st_cpu_thread)) {
- st_cpu_thread = rrdset_create_localhost(
- "netdata"
- , "plugin_proc_cpu"
- , NULL
- , "proc"
- , NULL
- , "NetData Proc Plugin CPU usage"
- , "milliseconds/s"
- , "netdata"
- , "stats"
- , 132000
- , localhost->rrd_update_every
- , RRDSET_TYPE_STACKED
- );
-#endif
-
- rd_cpu_thread_user = rrddim_add(st_cpu_thread, "user", NULL, 1, 1000, RRD_ALGORITHM_INCREMENTAL);
- rd_cpu_thread_system = rrddim_add(st_cpu_thread, "system", NULL, 1, 1000, RRD_ALGORITHM_INCREMENTAL);
- }
- else
- rrdset_next(st_cpu_thread);
-
- rrddim_set_by_pointer(st_cpu_thread, rd_cpu_thread_user, thread.ru_utime.tv_sec * 1000000ULL + thread.ru_utime.tv_usec);
- rrddim_set_by_pointer(st_cpu_thread, rd_cpu_thread_system, thread.ru_stime.tv_sec * 1000000ULL + thread.ru_stime.tv_usec);
- rrdset_done(st_cpu_thread);
- }
-
// ----------------------------------------------------------------
{
@@ -251,7 +206,7 @@ void global_statistics_charts(void) {
, NULL
, "netdata"
, NULL
- , "NetData CPU usage"
+ , "Netdata CPU usage"
, "milliseconds/s"
, "netdata"
, "stats"
@@ -274,6 +229,35 @@ void global_statistics_charts(void) {
// ----------------------------------------------------------------
{
+ static RRDSET *st_uptime = NULL;
+ static RRDDIM *rd_uptime = NULL;
+
+ if (unlikely(!st_uptime)) {
+ st_uptime = rrdset_create_localhost(
+ "netdata",
+ "uptime",
+ NULL,
+ "netdata",
+ NULL,
+ "Netdata uptime",
+ "seconds",
+ "netdata",
+ "stats",
+ 130100,
+ localhost->rrd_update_every,
+ RRDSET_TYPE_LINE);
+
+ rd_uptime = rrddim_add(st_uptime, "uptime", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
+ } else
+ rrdset_next(st_uptime);
+
+ rrddim_set_by_pointer(st_uptime, rd_uptime, netdata_uptime);
+ rrdset_done(st_uptime);
+ }
+
+ // ----------------------------------------------------------------
+
+ {
static RRDSET *st_clients = NULL;
static RRDDIM *rd_clients = NULL;
@@ -284,7 +268,7 @@ void global_statistics_charts(void) {
, NULL
, "netdata"
, NULL
- , "NetData Web Clients"
+ , "Netdata Web Clients"
, "connected clients"
, "netdata"
, "stats"
@@ -315,7 +299,7 @@ void global_statistics_charts(void) {
, NULL
, "netdata"
, NULL
- , "NetData Web Requests"
+ , "Netdata Web Requests"
, "requests/s"
, "netdata"
, "stats"
@@ -347,7 +331,7 @@ void global_statistics_charts(void) {
, NULL
, "netdata"
, NULL
- , "NetData Network Traffic"
+ , "Netdata Network Traffic"
, "kilobits/s"
, "netdata"
, "stats"
@@ -381,7 +365,7 @@ void global_statistics_charts(void) {
, NULL
, "netdata"
, NULL
- , "NetData API Response Time"
+ , "Netdata API Response Time"
, "milliseconds/request"
, "netdata"
, "stats"
@@ -430,7 +414,7 @@ void global_statistics_charts(void) {
, NULL
, "netdata"
, NULL
- , "NetData API Responses Compression Savings Ratio"
+ , "Netdata API Responses Compression Savings Ratio"
, "percentage"
, "netdata"
, "stats"
@@ -477,7 +461,7 @@ void global_statistics_charts(void) {
, NULL
, "queries"
, NULL
- , "NetData API Queries"
+ , "Netdata API Queries"
, "queries/s"
, "netdata"
, "stats"
@@ -510,7 +494,7 @@ void global_statistics_charts(void) {
, NULL
, "queries"
, NULL
- , "NetData API Points"
+ , "Netdata API Points"
, "points/s"
, "netdata"
, "stats"
@@ -579,7 +563,7 @@ void global_statistics_charts(void) {
, NULL
, "dbengine"
, NULL
- , "NetData DB engine data extents' compression savings ratio"
+ , "Netdata DB engine data extents' compression savings ratio"
, "percentage"
, "netdata"
, "stats"
@@ -621,7 +605,7 @@ void global_statistics_charts(void) {
, NULL
, "dbengine"
, NULL
- , "NetData DB engine page cache hit ratio"
+ , "Netdata DB engine page cache hit ratio"
, "percentage"
, "netdata"
, "stats"
@@ -676,7 +660,7 @@ void global_statistics_charts(void) {
, NULL
, "dbengine"
, NULL
- , "NetData dbengine page cache statistics"
+ , "Netdata dbengine page cache statistics"
, "pages"
, "netdata"
, "stats"
@@ -721,7 +705,7 @@ void global_statistics_charts(void) {
, NULL
, "dbengine"
, NULL
- , "NetData dbengine long-term page statistics"
+ , "Netdata dbengine long-term page statistics"
, "pages"
, "netdata"
, "stats"
@@ -761,7 +745,7 @@ void global_statistics_charts(void) {
, NULL
, "dbengine"
, NULL
- , "NetData DB engine I/O throughput"
+ , "Netdata DB engine I/O throughput"
, "MiB/s"
, "netdata"
, "stats"
@@ -795,7 +779,7 @@ void global_statistics_charts(void) {
, NULL
, "dbengine"
, NULL
- , "NetData DB engine I/O operations"
+ , "Netdata DB engine I/O operations"
, "operations/s"
, "netdata"
, "stats"
@@ -830,7 +814,7 @@ void global_statistics_charts(void) {
, NULL
, "dbengine"
, NULL
- , "NetData DB engine errors"
+ , "Netdata DB engine errors"
, "errors/s"
, "netdata"
, "stats"
@@ -867,7 +851,7 @@ void global_statistics_charts(void) {
, NULL
, "dbengine"
, NULL
- , "NetData DB engine File Descriptors"
+ , "Netdata DB engine File Descriptors"
, "descriptors"
, "netdata"
, "stats"
@@ -906,7 +890,7 @@ void global_statistics_charts(void) {
, NULL
, "dbengine"
, NULL
- , "NetData DB engine RAM usage"
+ , "Netdata DB engine RAM usage"
, "MiB"
, "netdata"
, "stats"
@@ -948,3 +932,36 @@ void global_statistics_charts(void) {
#endif
}
+
+static void global_statistics_cleanup(void *ptr)
+{
+ struct netdata_static_thread *static_thread = (struct netdata_static_thread *)ptr;
+ static_thread->enabled = NETDATA_MAIN_THREAD_EXITING;
+
+ info("cleaning up...");
+
+ static_thread->enabled = NETDATA_MAIN_THREAD_EXITED;
+}
+
+void *global_statistics_main(void *ptr)
+{
+ netdata_thread_cleanup_push(global_statistics_cleanup, ptr);
+
+ int update_every =
+ (int)config_get_number("CONFIG_SECTION_GLOBAL_STATISTICS", "update every", localhost->rrd_update_every);
+ if (update_every < localhost->rrd_update_every)
+ update_every = localhost->rrd_update_every;
+
+ usec_t step = update_every * USEC_PER_SEC;
+ heartbeat_t hb;
+ heartbeat_init(&hb);
+ while (!netdata_exit) {
+ heartbeat_next(&hb, step);
+
+ global_statistics_charts();
+ registry_statistics();
+ }
+
+ netdata_thread_cleanup_pop(1);
+ return NULL;
+}
diff --git a/daemon/global_statistics.h b/daemon/global_statistics.h
index 9dd7db51..c200a693 100644
--- a/daemon/global_statistics.h
+++ b/daemon/global_statistics.h
@@ -8,6 +8,17 @@
// ----------------------------------------------------------------------------
// global statistics
+#define NETDATA_PLUGIN_HOOK_GLOBAL_STATISTICS \
+ {.name = "GLOBAL_STATS", \
+ .config_section = NULL, \
+ .config_name = NULL, \
+ .enabled = 1, \
+ .thread = NULL, \
+ .init_routine = NULL, \
+ .start_routine = global_statistics_main},
+
+extern void *global_statistics_main(void *ptr);
+
extern void rrdr_query_completed(uint64_t db_points_read, uint64_t result_points_generated);
extern void finished_web_request_statistics(uint64_t dt,
diff --git a/daemon/main.c b/daemon/main.c
index 93ec78b6..61041f54 100644
--- a/daemon/main.c
+++ b/daemon/main.c
@@ -28,10 +28,13 @@ void netdata_cleanup_and_exit(int ret) {
info("EXIT: netdata prepares to exit with code %d...", ret);
send_statistics("EXIT", ret?"ERROR":"OK","-");
+ analytics_free_data();
char agent_crash_file[FILENAME_MAX + 1];
+ char agent_incomplete_shutdown_file[FILENAME_MAX + 1];
snprintfz(agent_crash_file, FILENAME_MAX, "%s/.agent_crash", netdata_configured_varlib_dir);
- (void) unlink(agent_crash_file);
+ snprintfz(agent_incomplete_shutdown_file, FILENAME_MAX, "%s/.agent_incomplete_shutdown", netdata_configured_varlib_dir);
+ (void) rename(agent_crash_file, agent_incomplete_shutdown_file);
// cleanup/save the database and exit
info("EXIT: cleaning up the database...");
@@ -67,10 +70,12 @@ void netdata_cleanup_and_exit(int ret) {
security_clean_openssl();
#endif
info("EXIT: all done - netdata is now exiting - bye bye...");
+ (void) unlink(agent_incomplete_shutdown_file);
exit(ret);
}
struct netdata_static_thread static_threads[] = {
+ NETDATA_PLUGIN_HOOK_GLOBAL_STATISTICS
NETDATA_PLUGIN_HOOK_CHECKS
NETDATA_PLUGIN_HOOK_FREEBSD
@@ -79,6 +84,7 @@ struct netdata_static_thread static_threads[] = {
// linux internal plugins
NETDATA_PLUGIN_HOOK_LINUX_PROC
NETDATA_PLUGIN_HOOK_LINUX_DISKSPACE
+ NETDATA_PLUGIN_HOOK_LINUX_TIMEX
NETDATA_PLUGIN_HOOK_LINUX_CGROUPS
NETDATA_PLUGIN_HOOK_LINUX_TC
@@ -97,6 +103,7 @@ struct netdata_static_thread static_threads[] = {
NETDATA_PLUGIN_HOOK_PLUGINSD
NETDATA_PLUGIN_HOOK_HEALTH
+ NETDATA_PLUGIN_HOOK_ANALYTICS
{NULL, NULL, NULL, 0, NULL, NULL, NULL}
};
@@ -395,18 +402,6 @@ void remove_option(int opt_index, int *argc, char **argv) {
} while(argv[i][0] != '-' && opt_index >= *argc);
}
-static const char *verify_required_directory(const char *dir) {
- if(chdir(dir) == -1)
- fatal("Cannot cd to directory '%s'", dir);
-
- DIR *d = opendir(dir);
- if(!d)
- fatal("Cannot examine the contents of directory '%s'", dir);
- closedir(d);
-
- return dir;
-}
-
#ifdef ENABLE_HTTPS
static void security_init(){
char filename[FILENAME_MAX + 1];
@@ -610,147 +605,6 @@ static void get_netdata_configured_variables() {
}
-static void get_system_timezone(void) {
- // avoid flood calls to stat(/etc/localtime)
- // http://stackoverflow.com/questions/4554271/how-to-avoid-excessive-stat-etc-localtime-calls-in-strftime-on-linux
- const char *tz = getenv("TZ");
- if(!tz || !*tz)
- setenv("TZ", config_get(CONFIG_SECTION_GLOBAL, "TZ environment variable", ":/etc/localtime"), 0);
-
- char buffer[FILENAME_MAX + 1] = "";
- const char *timezone = NULL;
- ssize_t ret;
-
- // use the TZ variable
- if(tz && *tz && *tz != ':') {
- timezone = tz;
- // info("TIMEZONE: using TZ variable '%s'", timezone);
- }
-
- // use the contents of /etc/timezone
- if(!timezone && !read_file("/etc/timezone", buffer, FILENAME_MAX)) {
- timezone = buffer;
- // info("TIMEZONE: using the contents of /etc/timezone: '%s'", timezone);
- }
-
- // read the link /etc/localtime
- if(!timezone) {
- ret = readlink("/etc/localtime", buffer, FILENAME_MAX);
-
- if(ret > 0) {
- buffer[ret] = '\0';
-
- char *cmp = "/usr/share/zoneinfo/";
- size_t cmp_len = strlen(cmp);
-
- char *s = strstr(buffer, cmp);
- if (s && s[cmp_len]) {
- timezone = &s[cmp_len];
- // info("TIMEZONE: using the link of /etc/localtime: '%s'", timezone);
- }
- }
- else
- buffer[0] = '\0';
- }
-
- // find the timezone from strftime()
- if(!timezone) {
- time_t t;
- struct tm *tmp, tmbuf;
-
- t = now_realtime_sec();
- tmp = localtime_r(&t, &tmbuf);
-
- if (tmp != NULL) {
- if(strftime(buffer, FILENAME_MAX, "%Z", tmp) == 0)
- buffer[0] = '\0';
- else {
- buffer[FILENAME_MAX] = '\0';
- timezone = buffer;
- // info("TIMEZONE: using strftime(): '%s'", timezone);
- }
- }
- }
-
- if(timezone && *timezone) {
- // make sure it does not have illegal characters
- // info("TIMEZONE: fixing '%s'", timezone);
-
- size_t len = strlen(timezone);
- char tmp[len + 1];
- char *d = tmp;
- *d = '\0';
-
- while(*timezone) {
- if(isalnum(*timezone) || *timezone == '_' || *timezone == '/')
- *d++ = *timezone++;
- else
- timezone++;
- }
- *d = '\0';
- strncpyz(buffer, tmp, len);
- timezone = buffer;
- // info("TIMEZONE: fixed as '%s'", timezone);
- }
-
- if(!timezone || !*timezone)
- timezone = "unknown";
-
- netdata_configured_timezone = config_get(CONFIG_SECTION_GLOBAL, "timezone", timezone);
-}
-
-void set_global_environment() {
- {
- char b[16];
- snprintfz(b, 15, "%d", default_rrd_update_every);
- setenv("NETDATA_UPDATE_EVERY", b, 1);
- }
-
- setenv("NETDATA_VERSION" , program_version, 1);
- setenv("NETDATA_HOSTNAME" , netdata_configured_hostname, 1);
- setenv("NETDATA_CONFIG_DIR" , verify_required_directory(netdata_configured_user_config_dir), 1);
- setenv("NETDATA_USER_CONFIG_DIR" , verify_required_directory(netdata_configured_user_config_dir), 1);
- setenv("NETDATA_STOCK_CONFIG_DIR" , verify_required_directory(netdata_configured_stock_config_dir), 1);
- setenv("NETDATA_PLUGINS_DIR" , verify_required_directory(netdata_configured_primary_plugins_dir), 1);
- setenv("NETDATA_WEB_DIR" , verify_required_directory(netdata_configured_web_dir), 1);
- setenv("NETDATA_CACHE_DIR" , verify_required_directory(netdata_configured_cache_dir), 1);
- setenv("NETDATA_LIB_DIR" , verify_required_directory(netdata_configured_varlib_dir), 1);
- setenv("NETDATA_LOCK_DIR" , netdata_configured_lock_dir, 1);
- setenv("NETDATA_LOG_DIR" , verify_required_directory(netdata_configured_log_dir), 1);
- setenv("HOME" , verify_required_directory(netdata_configured_home_dir), 1);
- setenv("NETDATA_HOST_PREFIX" , netdata_configured_host_prefix, 1);
-
- char *default_port = appconfig_get(&netdata_config, CONFIG_SECTION_WEB, "default port", NULL);
- int clean = 0;
- if (!default_port) {
- default_port = strdupz("19999");
- clean = 1;
- }
-
- setenv("NETDATA_LISTEN_PORT" , default_port, 1);
- if(clean)
- freez(default_port);
-
- get_system_timezone();
-
- // set the path we need
- char path[1024 + 1], *p = getenv("PATH");
- if(!p) p = "/bin:/usr/bin";
- snprintfz(path, 1024, "%s:%s", p, "/sbin:/usr/sbin:/usr/local/bin:/usr/local/sbin");
- setenv("PATH", config_get(CONFIG_SECTION_PLUGINS, "PATH environment variable", path), 1);
-
- // python options
- p = getenv("PYTHONPATH");
- if(!p) p = "";
- setenv("PYTHONPATH", config_get(CONFIG_SECTION_PLUGINS, "PYTHONPATH environment variable", p), 1);
-
- // disable buffering for python plugins
- setenv("PYTHONUNBUFFERED", "1", 1);
-
- // switch to standard locale for plugins
- setenv("LC_ALL", "C", 1);
-}
-
static int load_netdata_conf(char *filename, char overwrite_used) {
errno = 0;
@@ -833,47 +687,6 @@ int get_system_info(struct rrdhost_system_info *system_info) {
return 0;
}
-void send_statistics( const char *action, const char *action_result, const char *action_data) {
- static char *as_script;
-
- if (netdata_anonymous_statistics_enabled == -1) {
- char *optout_file = mallocz(sizeof(char) * (strlen(netdata_configured_user_config_dir) +strlen(".opt-out-from-anonymous-statistics") + 2));
- sprintf(optout_file, "%s/%s", netdata_configured_user_config_dir, ".opt-out-from-anonymous-statistics");
- if (likely(access(optout_file, R_OK) != 0)) {
- as_script = mallocz(sizeof(char) * (strlen(netdata_configured_primary_plugins_dir) + strlen("anonymous-statistics.sh") + 2));
- sprintf(as_script, "%s/%s", netdata_configured_primary_plugins_dir, "anonymous-statistics.sh");
- if (unlikely(access(as_script, R_OK) != 0)) {
- netdata_anonymous_statistics_enabled=0;
- info("Anonymous statistics script %s not found.",as_script);
- freez(as_script);
- } else {
- netdata_anonymous_statistics_enabled=1;
- }
- } else {
- netdata_anonymous_statistics_enabled = 0;
- as_script = NULL;
- }
- freez(optout_file);
- }
- if(!netdata_anonymous_statistics_enabled) return;
- if (!action) return;
- if (!action_result) action_result="";
- if (!action_data) action_data="";
- char *command_to_run=mallocz(sizeof(char) * (strlen(action) + strlen(action_result) + strlen(action_data) + strlen(as_script) + 10));
- pid_t command_pid;
-
- sprintf(command_to_run,"%s '%s' '%s' '%s'", as_script, action, action_result, action_data);
- info("%s", command_to_run);
-
- FILE *fp = mypopen(command_to_run, &command_pid);
- if(fp) {
- char buffer[100 + 1];
- while (fgets(buffer, 100, fp) != NULL);
- mypclose(fp, command_pid);
- }
- freez(command_to_run);
-}
-
void set_silencers_filename() {
char filename[FILENAME_MAX + 1];
snprintfz(filename, FILENAME_MAX, "%s/health.silencers.json", netdata_configured_varlib_dir);
@@ -920,7 +733,7 @@ int main(int argc, char **argv) {
// set the name for logging
program_name = "netdata";
- // parse depercated options
+ // parse deprecated options
// TODO: Remove this block with the next major release.
{
i = 1;
@@ -1106,21 +919,21 @@ int main(int argc, char **argv) {
return 1;
}
- const char *heystack = argv[optind];
+ const char *haystack = argv[optind];
const char *needle = argv[optind + 1];
size_t len = strlen(needle) + 1;
char wildcarded[len];
- SIMPLE_PATTERN *p = simple_pattern_create(heystack, NULL, SIMPLE_PATTERN_EXACT);
+ SIMPLE_PATTERN *p = simple_pattern_create(haystack, NULL, SIMPLE_PATTERN_EXACT);
int ret = simple_pattern_matches_extract(p, needle, wildcarded, len);
simple_pattern_free(p);
if(ret) {
- fprintf(stdout, "RESULT: MATCHED - pattern '%s' matches '%s', wildcarded '%s'\n", heystack, needle, wildcarded);
+ fprintf(stdout, "RESULT: MATCHED - pattern '%s' matches '%s', wildcarded '%s'\n", haystack, needle, wildcarded);
return 0;
}
else {
- fprintf(stdout, "RESULT: NOT MATCHED - pattern '%s' does not match '%s', wildcarded '%s'\n", heystack, needle, wildcarded);
+ fprintf(stdout, "RESULT: NOT MATCHED - pattern '%s' does not match '%s', wildcarded '%s'\n", haystack, needle, wildcarded);
return 1;
}
}
@@ -1437,7 +1250,7 @@ int main(int argc, char **argv) {
netdata_threads_init_after_fork((size_t)config_get_number(CONFIG_SECTION_GLOBAL, "pthread stack size", (long)default_stacksize));
- // initialyze internal registry
+ // initialize internal registry
registry_init();
// fork the spawn server
spawn_init();
@@ -1461,6 +1274,9 @@ int main(int argc, char **argv) {
fatal("Cannot initialize localhost instance with name '%s'.", netdata_configured_hostname);
char agent_crash_file[FILENAME_MAX + 1];
+ char agent_incomplete_shutdown_file[FILENAME_MAX + 1];
+ snprintfz(agent_incomplete_shutdown_file, FILENAME_MAX, "%s/.agent_incomplete_shutdown", netdata_configured_varlib_dir);
+ int incomplete_shutdown_detected = (unlink(agent_incomplete_shutdown_file) == 0);
snprintfz(agent_crash_file, FILENAME_MAX, "%s/.agent_crash", netdata_configured_varlib_dir);
int crash_detected = (unlink(agent_crash_file) == 0);
int fd = open(agent_crash_file, O_WRONLY | O_CREAT | O_TRUNC, 444);
@@ -1509,9 +1325,26 @@ int main(int argc, char **argv) {
info("netdata initialization completed. Enjoy real-time performance monitoring!");
netdata_ready = 1;
+ set_late_global_environment();
+
send_statistics("START", "-", "-");
if (crash_detected)
send_statistics("CRASH", "-", "-");
+ if (incomplete_shutdown_detected)
+ send_statistics("INCOMPLETE_SHUTDOWN", "-", "-");
+
+ //check if ANALYTICS needs to start
+ if (netdata_anonymous_statistics_enabled == 1) {
+ for (i = 0; static_threads[i].name != NULL; i++) {
+ if (!strncmp(static_threads[i].name, "ANALYTICS", 9)) {
+ struct netdata_static_thread *st = &static_threads[i];
+ st->thread = mallocz(sizeof(netdata_thread_t));
+ st->enabled = 1;
+ debug(D_SYSTEM, "Starting thread %s.", st->name);
+ netdata_thread_create(st->thread, st->name, NETDATA_THREAD_OPTION_DEFAULT, st->start_routine, st);
+ }
+ }
+ }
// ------------------------------------------------------------------------
// Report ACLK build failure
diff --git a/daemon/signals.c b/daemon/signals.c
index 9e30bf19..b991d46b 100644
--- a/daemon/signals.c
+++ b/daemon/signals.c
@@ -44,7 +44,7 @@ static void signal_handler(int signo) {
if(signals_waiting[i].action == NETDATA_SIGNAL_FATAL) {
char buffer[200 + 1];
- snprintfz(buffer, 200, "\nSIGNAL HANLDER: received: %s. Oops! This is bad!\n", signals_waiting[i].name);
+ snprintfz(buffer, 200, "\nSIGNAL HANDLER: received: %s. Oops! This is bad!\n", signals_waiting[i].name);
if(write(STDERR_FILENO, buffer, strlen(buffer)) == -1) {
// nothing to do - we cannot write but there is no way to complain about it
;
diff --git a/daemon/unit_test.c b/daemon/unit_test.c
index 9a17aa76..81090736 100644
--- a/daemon/unit_test.c
+++ b/daemon/unit_test.c
@@ -1515,7 +1515,7 @@ static RRDHOST *dbengine_rrdhost_find_or_create(char *name)
);
}
-// costants for test_dbengine
+// constants for test_dbengine
static const int CHARTS = 64;
static const int DIMS = 16; // That gives us 64 * 16 = 1024 metrics
#define REGIONS (3) // 3 regions of update_every