1
+// SPDX-License-Identifier: GPL-3.0-or-later
2
+
3
+#include "common.h"
4
+
5
+struct analytics_data analytics_data;
6
+extern void analytics_exporting_connectors (BUFFER *b);
7
+extern void analytics_build_info (BUFFER *b);
8
+extern int aclk_connected;
9
+
10
+struct collector {
11
+ char *plugin;
12
+ char *module;
13
+};
14
+
15
+struct array_printer {
16
+ int c;
17
+ BUFFER *both;
18
+};
19
+
20
+/*
21
+ * Set the env variables
22
+ */
23
+void analytics_setenv_data(void)
24
+{
25
+ setenv("NETDATA_CONFIG_STREAM_ENABLED", analytics_data.netdata_config_stream_enabled, 1);
26
+ setenv("NETDATA_CONFIG_MEMORY_MODE", analytics_data.netdata_config_memory_mode, 1);
27
+ setenv("NETDATA_CONFIG_EXPORTING_ENABLED", analytics_data.netdata_config_exporting_enabled, 1);
28
+ setenv("NETDATA_EXPORTING_CONNECTORS", analytics_data.netdata_exporting_connectors, 1);
29
+ setenv("NETDATA_ALLMETRICS_PROMETHEUS_USED", analytics_data.netdata_allmetrics_prometheus_used, 1);
30
+ setenv("NETDATA_ALLMETRICS_SHELL_USED", analytics_data.netdata_allmetrics_shell_used, 1);
31
+ setenv("NETDATA_ALLMETRICS_JSON_USED", analytics_data.netdata_allmetrics_json_used, 1);
32
+ setenv("NETDATA_DASHBOARD_USED", analytics_data.netdata_dashboard_used, 1);
33
+ setenv("NETDATA_COLLECTORS", analytics_data.netdata_collectors, 1);
34
+ setenv("NETDATA_COLLECTORS_COUNT", analytics_data.netdata_collectors_count, 1);
35
+ setenv("NETDATA_BUILDINFO", analytics_data.netdata_buildinfo, 1);
36
+ setenv("NETDATA_CONFIG_PAGE_CACHE_SIZE", analytics_data.netdata_config_page_cache_size, 1);
37
+ setenv("NETDATA_CONFIG_MULTIDB_DISK_QUOTA", analytics_data.netdata_config_multidb_disk_quota, 1);
38
+ setenv("NETDATA_CONFIG_HTTPS_ENABLED", analytics_data.netdata_config_https_enabled, 1);
39
+ setenv("NETDATA_CONFIG_WEB_ENABLED", analytics_data.netdata_config_web_enabled, 1);
40
+ setenv("NETDATA_CONFIG_RELEASE_CHANNEL", analytics_data.netdata_config_release_channel, 1);
41
+ setenv("NETDATA_MIRRORED_HOST_COUNT", analytics_data.netdata_mirrored_host_count, 1);
42
+ setenv("NETDATA_MIRRORED_HOSTS_REACHABLE", analytics_data.netdata_mirrored_hosts_reachable, 1);
43
+ setenv("NETDATA_MIRRORED_HOSTS_UNREACHABLE", analytics_data.netdata_mirrored_hosts_unreachable, 1);
44
+ setenv("NETDATA_NOTIFICATION_METHODS", analytics_data.netdata_notification_methods, 1);
45
+ setenv("NETDATA_ALARMS_NORMAL", analytics_data.netdata_alarms_normal, 1);
46
+ setenv("NETDATA_ALARMS_WARNING", analytics_data.netdata_alarms_warning, 1);
47
+ setenv("NETDATA_ALARMS_CRITICAL", analytics_data.netdata_alarms_critical, 1);
48
+ setenv("NETDATA_CHARTS_COUNT", analytics_data.netdata_charts_count, 1);
49
+ setenv("NETDATA_METRICS_COUNT", analytics_data.netdata_metrics_count, 1);
50
+ setenv("NETDATA_CONFIG_IS_PARENT", analytics_data.netdata_config_is_parent, 1);
51
+ setenv("NETDATA_CONFIG_HOSTS_AVAILABLE", analytics_data.netdata_config_hosts_available, 1);
52
+ setenv("NETDATA_HOST_CLOUD_AVAILABLE", analytics_data.netdata_host_cloud_available, 1);
53
+ setenv("NETDATA_HOST_ACLK_AVAILABLE", analytics_data.netdata_host_aclk_available, 1);
54
+ setenv("NETDATA_HOST_ACLK_IMPLEMENTATION", analytics_data.netdata_host_aclk_implementation, 1);
55
+ setenv("NETDATA_HOST_AGENT_CLAIMED", analytics_data.netdata_host_agent_claimed, 1);
56
+}
57
+
58
+/*
59
+ * Debug logging
60
+ */
61
+void analytics_log_data(void)
62
+{
63
+ debug(D_ANALYTICS, "NETDATA_CONFIG_STREAM_ENABLED : [%s]", analytics_data.netdata_config_stream_enabled);
64
+ debug(D_ANALYTICS, "NETDATA_CONFIG_MEMORY_MODE : [%s]", analytics_data.netdata_config_memory_mode);
65
+ debug(D_ANALYTICS, "NETDATA_CONFIG_EXPORTING_ENABLED : [%s]", analytics_data.netdata_config_exporting_enabled);
66
+ debug(D_ANALYTICS, "NETDATA_EXPORTING_CONNECTORS : [%s]", analytics_data.netdata_exporting_connectors);
67
+ debug(D_ANALYTICS, "NETDATA_ALLMETRICS_PROMETHEUS_USED : [%s]", analytics_data.netdata_allmetrics_prometheus_used);
68
+ debug(D_ANALYTICS, "NETDATA_ALLMETRICS_SHELL_USED : [%s]", analytics_data.netdata_allmetrics_shell_used);
69
+ debug(D_ANALYTICS, "NETDATA_ALLMETRICS_JSON_USED : [%s]", analytics_data.netdata_allmetrics_json_used);
70
+ debug(D_ANALYTICS, "NETDATA_DASHBOARD_USED : [%s]", analytics_data.netdata_dashboard_used);
71
+ debug(D_ANALYTICS, "NETDATA_COLLECTORS : [%s]", analytics_data.netdata_collectors);
72
+ debug(D_ANALYTICS, "NETDATA_COLLECTORS_COUNT : [%s]", analytics_data.netdata_collectors_count);
73
+ debug(D_ANALYTICS, "NETDATA_BUILDINFO : [%s]", analytics_data.netdata_buildinfo);
74
+ debug(D_ANALYTICS, "NETDATA_CONFIG_PAGE_CACHE_SIZE : [%s]", analytics_data.netdata_config_page_cache_size);
75
+ debug(D_ANALYTICS, "NETDATA_CONFIG_MULTIDB_DISK_QUOTA : [%s]", analytics_data.netdata_config_multidb_disk_quota);
76
+ debug(D_ANALYTICS, "NETDATA_CONFIG_HTTPS_ENABLED : [%s]", analytics_data.netdata_config_https_enabled);
77
+ debug(D_ANALYTICS, "NETDATA_CONFIG_WEB_ENABLED : [%s]", analytics_data.netdata_config_web_enabled);
78
+ debug(D_ANALYTICS, "NETDATA_CONFIG_RELEASE_CHANNEL : [%s]", analytics_data.netdata_config_release_channel);
79
+ debug(D_ANALYTICS, "NETDATA_MIRRORED_HOST_COUNT : [%s]", analytics_data.netdata_mirrored_host_count);
80
+ debug(D_ANALYTICS, "NETDATA_MIRRORED_HOSTS_REACHABLE : [%s]", analytics_data.netdata_mirrored_hosts_reachable);
81
+ debug(D_ANALYTICS, "NETDATA_MIRRORED_HOSTS_UNREACHABLE : [%s]", analytics_data.netdata_mirrored_hosts_unreachable);
82
+ debug(D_ANALYTICS, "NETDATA_NOTIFICATION_METHODS : [%s]", analytics_data.netdata_notification_methods);
83
+ debug(D_ANALYTICS, "NETDATA_ALARMS_NORMAL : [%s]", analytics_data.netdata_alarms_normal);
84
+ debug(D_ANALYTICS, "NETDATA_ALARMS_WARNING : [%s]", analytics_data.netdata_alarms_warning);
85
+ debug(D_ANALYTICS, "NETDATA_ALARMS_CRITICAL : [%s]", analytics_data.netdata_alarms_critical);
86
+ debug(D_ANALYTICS, "NETDATA_CHARTS_COUNT : [%s]", analytics_data.netdata_charts_count);
87
+ debug(D_ANALYTICS, "NETDATA_METRICS_COUNT : [%s]", analytics_data.netdata_metrics_count);
88
+ debug(D_ANALYTICS, "NETDATA_CONFIG_IS_PARENT : [%s]", analytics_data.netdata_config_is_parent);
89
+ debug(D_ANALYTICS, "NETDATA_CONFIG_HOSTS_AVAILABLE : [%s]", analytics_data.netdata_config_hosts_available);
90
+ debug(D_ANALYTICS, "NETDATA_HOST_CLOUD_AVAILABLE : [%s]", analytics_data.netdata_host_cloud_available);
91
+ debug(D_ANALYTICS, "NETDATA_HOST_ACLK_AVAILABLE : [%s]", analytics_data.netdata_host_aclk_available);
92
+ debug(D_ANALYTICS, "NETDATA_HOST_ACLK_IMPLEMENTATION : [%s]", analytics_data.netdata_host_aclk_implementation);
93
+ debug(D_ANALYTICS, "NETDATA_HOST_AGENT_CLAIMED : [%s]", analytics_data.netdata_host_agent_claimed);
94
+}
95
+
96
+/*
97
+ * Free data
98
+ */
99
+void analytics_free_data(void)
100
+{
101
+ freez(analytics_data.netdata_config_stream_enabled);
102
+ freez(analytics_data.netdata_config_memory_mode);
103
+ freez(analytics_data.netdata_config_exporting_enabled);
104
+ freez(analytics_data.netdata_exporting_connectors);
105
+ freez(analytics_data.netdata_allmetrics_prometheus_used);
106
+ freez(analytics_data.netdata_allmetrics_shell_used);
107
+ freez(analytics_data.netdata_allmetrics_json_used);
108
+ freez(analytics_data.netdata_dashboard_used);
109
+ freez(analytics_data.netdata_collectors);
110
+ freez(analytics_data.netdata_collectors_count);
111
+ freez(analytics_data.netdata_buildinfo);
112
+ freez(analytics_data.netdata_config_page_cache_size);
113
+ freez(analytics_data.netdata_config_multidb_disk_quota);
114
+ freez(analytics_data.netdata_config_https_enabled);
115
+ freez(analytics_data.netdata_config_web_enabled);
116
+ freez(analytics_data.netdata_config_release_channel);
117
+ freez(analytics_data.netdata_mirrored_host_count);
118
+ freez(analytics_data.netdata_mirrored_hosts_reachable);
119
+ freez(analytics_data.netdata_mirrored_hosts_unreachable);
120
+ freez(analytics_data.netdata_notification_methods);
121
+ freez(analytics_data.netdata_alarms_normal);
122
+ freez(analytics_data.netdata_alarms_warning);
123
+ freez(analytics_data.netdata_alarms_critical);
124
+ freez(analytics_data.netdata_charts_count);
125
+ freez(analytics_data.netdata_metrics_count);
126
+ freez(analytics_data.netdata_config_is_parent);
127
+ freez(analytics_data.netdata_config_hosts_available);
128
+ freez(analytics_data.netdata_host_cloud_available);
129
+ freez(analytics_data.netdata_host_aclk_available);
130
+ freez(analytics_data.netdata_host_aclk_implementation);
131
+ freez(analytics_data.netdata_host_agent_claimed);
132
+}
133
+
134
+/*
135
+ * Set a numeric/boolean data with a value
136
+ */
137
+void analytics_set_data(char **name, char *value)
138
+{
139
+ if (*name)
140
+ freez(*name);
141
+ *name = strdupz(value);
142
+}
143
+
144
+/*
145
+ * Set a string data with a value
146
+ */
147
+void analytics_set_data_str(char **name, char *value)
148
+{
149
+ size_t value_string_len;
150
+ if (*name)
151
+ freez(*name);
152
+ value_string_len = strlen(value) + 4;
153
+ *name = mallocz(sizeof(char) * value_string_len);
154
+ snprintfz(*name, value_string_len - 1, "\"%s\"", value);
155
+}
156
+
157
+/*
158
+ * Get data, used by web api v1
159
+ */
160
+void analytics_get_data(char *name, BUFFER *wb)
161
+{
162
+ buffer_strcat(wb, name);
163
+}
164
+
165
+/*
166
+ * Log hits on the allmetrics page, with prometheus parameter
167
+ */
168
+void analytics_log_prometheus(void)
169
+{
170
+ if (likely(analytics_data.prometheus_hits < ANALYTICS_MAX_PROMETHEUS_HITS)) {
171
+ analytics_data.prometheus_hits++;
172
+ char b[7];
173
+ snprintfz(b, 6, "%d", analytics_data.prometheus_hits);
174
+ analytics_set_data(&analytics_data.netdata_allmetrics_prometheus_used, b);
175
+ }
176
+}
177
+
178
+/*
179
+ * Log hits on the allmetrics page, with shell parameter (or default)
180
+ */
181
+void analytics_log_shell(void)
182
+{
183
+ if (likely(analytics_data.shell_hits < ANALYTICS_MAX_SHELL_HITS)) {
184
+ analytics_data.shell_hits++;
185
+ char b[7];
186
+ snprintfz(b, 6, "%d", analytics_data.shell_hits);
187
+ analytics_set_data(&analytics_data.netdata_allmetrics_shell_used, b);
188
+ }
189
+}
190
+
191
+/*
192
+ * Log hits on the allmetrics page, with json parameter
193
+ */
194
+void analytics_log_json(void)
195
+{
196
+ if (likely(analytics_data.json_hits < ANALYTICS_MAX_JSON_HITS)) {
197
+ analytics_data.json_hits++;
198
+ char b[7];
199
+ snprintfz(b, 6, "%d", analytics_data.json_hits);
200
+ analytics_set_data(&analytics_data.netdata_allmetrics_json_used, b);
201
+ }
202
+}
203
+
204
+/*
205
+ * Log hits on the dashboard, (when calling HELLO).
206
+ */
207
+void analytics_log_dashboard(void)
208
+{
209
+ if (likely(analytics_data.dashboard_hits < ANALYTICS_MAX_DASHBOARD_HITS)) {
210
+ analytics_data.dashboard_hits++;
211
+ char b[7];
212
+ snprintfz(b, 6, "%d", analytics_data.dashboard_hits);
213
+ analytics_set_data(&analytics_data.netdata_dashboard_used, b);
214
+ }
215
+}
216
+
217
+void analytics_mirrored_hosts(void)
218
+{
219
+ RRDHOST *host;
220
+ int count = 0;
221
+ int reachable = 0;
222
+ int unreachable = 0;
223
+ char b[11];
224
+
225
+ rrd_rdlock();
226
+ rrdhost_foreach_read(host)
227
+ {
228
+ if (rrdhost_flag_check(host, RRDHOST_FLAG_ARCHIVED))
229
+ continue;
230
+
231
+ netdata_mutex_lock(&host->receiver_lock);
232
+ ((host->receiver || host == localhost) ? reachable++ : unreachable++);
233
+ netdata_mutex_unlock(&host->receiver_lock);
234
+
235
+ count++;
236
+ }
237
+ rrd_unlock();
238
+
239
+ snprintfz(b, 10, "%d", count);
240
+ analytics_set_data(&analytics_data.netdata_mirrored_host_count, b);
241
+ snprintfz(b, 10, "%d", reachable);
242
+ analytics_set_data(&analytics_data.netdata_mirrored_hosts_reachable, b);
243
+ snprintfz(b, 10, "%d", unreachable);
244
+ analytics_set_data(&analytics_data.netdata_mirrored_hosts_unreachable, b);
245
+}
246
+
247
+void analytics_exporters(void)
248
+{
249
+ //when no exporters are available, an empty string will be sent
250
+ //decide if something else is more suitable (but propably not null)
251
+ BUFFER *bi = buffer_create(1000);
252
+ analytics_exporting_connectors(bi);
253
+ analytics_set_data_str(&analytics_data.netdata_exporting_connectors, (char *)buffer_tostring(bi));
254
+ buffer_free(bi);
255
+}
256
+
257
+int collector_counter_callb(void *entry, void *data)
258
+{
259
+ struct array_printer *ap = (struct array_printer *)data;
260
+ struct collector *col = (struct collector *)entry;
261
+
262
+ BUFFER *bt = ap->both;
263
+
264
+ if (likely(ap->c)) {
265
+ buffer_strcat(bt, ",");
266
+ }
267
+
268
+ buffer_strcat(bt, "{");
269
+ buffer_strcat(bt, " \"plugin\": \"");
270
+ buffer_strcat(bt, col->plugin);
271
+ buffer_strcat(bt, "\", \"module\":\"");
272
+ buffer_strcat(bt, col->module);
273
+ buffer_strcat(bt, "\" }");
274
+
275
+ (ap->c)++;
276
+
277
+ return 0;
278
+}
279
+
280
+/*
281
+ * Create a JSON array of available collectors, same as in api/v1/info
282
+ */
283
+void analytics_collectors(void)
284
+{
285
+ RRDSET *st;
286
+ DICTIONARY *dict = dictionary_create(DICTIONARY_FLAG_SINGLE_THREADED);
287
+ char name[500];
288
+ BUFFER *bt = buffer_create(1000);
289
+
290
+ rrdset_foreach_read(st, localhost)
291
+ {
292
+ if (rrdset_is_available_for_viewers(st)) {
293
+ struct collector col = { .plugin = st->plugin_name ? st->plugin_name : "",
294
+ .module = st->module_name ? st->module_name : "" };
295
+ snprintfz(name, 499, "%s:%s", col.plugin, col.module);
296
+ dictionary_set(dict, name, &col, sizeof(struct collector));
297
+ }
298
+ }
299
+
300
+ struct array_printer ap;
301
+ ap.c = 0;
302
+ ap.both = bt;
303
+
304
+ dictionary_get_all(dict, collector_counter_callb, &ap);
305
+ dictionary_destroy(dict);
306
+
307
+ analytics_set_data(&analytics_data.netdata_collectors, (char *)buffer_tostring(ap.both));
308
+
309
+ {
310
+ char b[7];
311
+ snprintfz(b, 6, "%d", ap.c);
312
+ analytics_set_data(&analytics_data.netdata_collectors_count, b);
313
+ }
314
+
315
+ buffer_free(bt);
316
+}
317
+
318
+/*
319
+ * Run alarm-notify.sh script using the dump_methods parameter
320
+ * SEND_CUSTOM is always available
321
+ */
322
+void analytics_alarms_notifications(void)
323
+{
324
+ char *script;
325
+ script = mallocz(
326
+ sizeof(char) * (strlen(netdata_configured_primary_plugins_dir) + strlen("alarm-notify.sh dump_methods") + 2));
327
+ sprintf(script, "%s/%s", netdata_configured_primary_plugins_dir, "alarm-notify.sh");
328
+ if (unlikely(access(script, R_OK) != 0)) {
329
+ info("Alarm notify script %s not found.", script);
330
+ freez(script);
331
+ return;
332
+ }
333
+
334
+ strcat(script, " dump_methods");
335
+
336
+ pid_t command_pid;
337
+
338
+ info("Executing %s", script);
339
+
340
+ BUFFER *b = buffer_create(1000);
341
+ int cnt = 0;
342
+ FILE *fp = mypopen(script, &command_pid);
343
+ if (fp) {
344
+ char line[200 + 1];
345
+
346
+ while (fgets(line, 200, fp) != NULL) {
347
+ char *end = line;
348
+ while (*end && *end != '\n')
349
+ end++;
350
+ *end = '\0';
351
+
352
+ if (likely(cnt))
353
+ buffer_strcat(b, "|");
354
+
355
+ buffer_strcat(b, line);
356
+
357
+ cnt++;
358
+ }
359
+ mypclose(fp, command_pid);
360
+ }
361
+ freez(script);
362
+
363
+ analytics_set_data_str(&analytics_data.netdata_notification_methods, (char *)buffer_tostring(b));
364
+
365
+ buffer_free(b);
366
+}
367
+
368
+void analytics_charts(void)
369
+{
370
+ RRDSET *st;
371
+ int c = 0;
372
+ rrdset_foreach_read(st, localhost)
373
+ {
374
+ if (rrdset_is_available_for_viewers(st)) {
375
+ c++;
376
+ }
377
+ }
378
+ {
379
+ char b[7];
380
+ snprintfz(b, 6, "%d", c);
381
+ analytics_set_data(&analytics_data.netdata_charts_count, b);
382
+ }
383
+}
384
+
385
+void analytics_metrics(void)
386
+{
387
+ RRDSET *st;
388
+ long int dimensions = 0;
389
+ RRDDIM *rd;
390
+ rrdset_foreach_read(st, localhost)
391
+ {
392
+ rrdset_rdlock(st);
393
+ rrddim_foreach_read(rd, st)
394
+ {
395
+ if (rrddim_flag_check(rd, RRDDIM_FLAG_HIDDEN) || rrddim_flag_check(rd, RRDDIM_FLAG_OBSOLETE))
396
+ continue;
397
+ dimensions++;
398
+ }
399
+ rrdset_unlock(st);
400
+ }
401
+ {
402
+ char b[7];
403
+ snprintfz(b, 6, "%ld", dimensions);
404
+ analytics_set_data(&analytics_data.netdata_metrics_count, b);
405
+ }
406
+}
407
+
408
+void analytics_alarms(void)
409
+{
410
+ int alarm_warn = 0, alarm_crit = 0, alarm_normal = 0;
411
+ char b[10];
412
+ RRDCALC *rc;
413
+ for (rc = localhost->alarms; rc; rc = rc->next) {
414
+ if (unlikely(!rc->rrdset || !rc->rrdset->last_collected_time.tv_sec))
415
+ continue;
416
+
417
+ switch (rc->status) {
418
+ case RRDCALC_STATUS_WARNING:
419
+ alarm_warn++;
420
+ break;
421
+ case RRDCALC_STATUS_CRITICAL:
422
+ alarm_crit++;
423
+ break;
424
+ default:
425
+ alarm_normal++;
426
+ }
427
+ }
428
+
429
+ snprintfz(b, 9, "%d", alarm_normal);
430
+ analytics_set_data(&analytics_data.netdata_alarms_normal, b);
431
+ snprintfz(b, 9, "%d", alarm_warn);
432
+ analytics_set_data(&analytics_data.netdata_alarms_warning, b);
433
+ snprintfz(b, 9, "%d", alarm_crit);
434
+ analytics_set_data(&analytics_data.netdata_alarms_critical, b);
435
+}
436
+
437
+/*
438
+ * Misc attributes to get (run from meta)
439
+ */
440
+void analytics_misc(void)
441
+{
442
+#ifdef ENABLE_ACLK
443
+ analytics_set_data(&analytics_data.netdata_host_cloud_available, "true");
444
+#ifdef ACLK_NG
445
+ analytics_set_data_str(&analytics_data.netdata_host_aclk_implementation, "Next Generation");
446
+#else
447
+ analytics_set_data_str(&analytics_data.netdata_host_aclk_implementation, "legacy");
448
+#endif
449
+#else
450
+ analytics_set_data(&analytics_data.netdata_host_cloud_available, "false");
451
+#endif
452
+
453
+#ifdef ENABLE_ACLK
454
+ if (aclk_connected)
455
+ analytics_set_data(&analytics_data.netdata_host_aclk_available, "true");
456
+ else
457
+#endif
458
+ analytics_set_data(&analytics_data.netdata_host_aclk_available, "false");
459
+}
460
+
461
+/*
462
+ * Get the meta data, called from the thread once after the original delay
463
+ * These are values that won't change between agent restarts, and therefore
464
+ * don't try to read them on each META event send
465
+ */
466
+void analytics_gather_immutable_meta_data(void)
467
+{
468
+ analytics_misc();
469
+ analytics_exporters();
470
+
471
+ analytics_setenv_data();
472
+}
473
+
474
+/*
475
+ * Get the meta data, called from the thread on every heartbeat, and right before the EXIT event
476
+ * These are values that can change between agent restarts, and therefore
477
+ * try to read them on each META event send
478
+ */
479
+void analytics_gather_mutable_meta_data(void)
480
+{
481
+ rrdhost_rdlock(localhost);
482
+
483
+ analytics_collectors();
484
+ analytics_alarms();
485
+ analytics_charts();
486
+ analytics_metrics();
487
+
488
+ rrdhost_unlock(localhost);
489
+
490
+ analytics_mirrored_hosts();
491
+ analytics_alarms_notifications();
492
+
493
+ analytics_set_data(
494
+ &analytics_data.netdata_config_is_parent, (localhost->next || configured_as_parent()) ? "true" : "false");
495
+
496
+ if (is_agent_claimed())
497
+ analytics_set_data(&analytics_data.netdata_host_agent_claimed, "true");
498
+ else {
499
+ analytics_set_data(&analytics_data.netdata_host_agent_claimed, "false");
500
+ }
501
+
502
+ {
503
+ char b[7];
504
+ snprintfz(b, 6, "%d", analytics_data.prometheus_hits);
505
+ analytics_set_data(&analytics_data.netdata_allmetrics_prometheus_used, b);
506
+
507
+ snprintfz(b, 6, "%d", analytics_data.shell_hits);
508
+ analytics_set_data(&analytics_data.netdata_allmetrics_shell_used, b);
509
+
510
+ snprintfz(b, 6, "%d", analytics_data.json_hits);
511
+ analytics_set_data(&analytics_data.netdata_allmetrics_json_used, b);
512
+
513
+ snprintfz(b, 6, "%d", analytics_data.dashboard_hits);
514
+ analytics_set_data(&analytics_data.netdata_dashboard_used, b);
515
+
516
+ snprintfz(b, 6, "%zu", rrd_hosts_available);
517
+ analytics_set_data(&analytics_data.netdata_config_hosts_available, b);
518
+ }
519
+
520
+ analytics_setenv_data();
521
+}
522
+
523
+void analytics_main_cleanup(void *ptr)
524
+{
525
+ struct netdata_static_thread *static_thread = (struct netdata_static_thread *)ptr;
526
+ static_thread->enabled = NETDATA_MAIN_THREAD_EXITING;
527
+
528
+ debug(D_ANALYTICS, "Cleaning up...");
529
+
530
+ static_thread->enabled = NETDATA_MAIN_THREAD_EXITED;
531
+}
532
+
533
+/*
534
+ * The analytics thread. Sleep for ANALYTICS_INIT_SLEEP_SEC,
535
+ * gather the data, and then go to a loop where every ANALYTICS_HEARTBEAT
536
+ * it will send a new META event after gathering data that could be changed
537
+ * while the agent is running
538
+ */
539
+void *analytics_main(void *ptr)
540
+{
541
+ netdata_thread_cleanup_push(analytics_main_cleanup, ptr);
542
+ unsigned int sec = 0;
543
+ heartbeat_t hb;
544
+ heartbeat_init(&hb);
545
+ usec_t step_ut = USEC_PER_SEC;
546
+
547
+ debug(D_ANALYTICS, "Analytics thread starts");
548
+
549
+ //first delay after agent start
550
+ while (!netdata_exit && likely(sec <= ANALYTICS_INIT_SLEEP_SEC)) {
551
+ heartbeat_next(&hb, step_ut);
552
+ sec++;
553
+ }
554
+
555
+ if (unlikely(netdata_exit))
556
+ goto cleanup;
557
+
558
+ analytics_gather_immutable_meta_data();
559
+ analytics_gather_mutable_meta_data();
560
+ send_statistics("META", "-", "-");
561
+ analytics_log_data();
562
+
563
+ sec = 0;
564
+ while (1) {
565
+ heartbeat_next(&hb, step_ut * 2);
566
+ sec += 2;
567
+
568
+ if (unlikely(netdata_exit))
569
+ break;
570
+
571
+ if (likely(sec < ANALYTICS_HEARTBEAT))
572
+ continue;
573
+
574
+ analytics_gather_mutable_meta_data();
575
+ send_statistics("META", "-", "-");
576
+ analytics_log_data();
577
+ sec = 0;
578
+ }
579
+
580
+cleanup:
581
+ netdata_thread_cleanup_pop(1);
582
+ return NULL;
583
+}
584
+
585
+static const char *verify_required_directory(const char *dir)
586
+{
587
+ if (chdir(dir) == -1)
588
+ fatal("Cannot change directory to '%s'", dir);
589
+
590
+ DIR *d = opendir(dir);
591
+ if (!d)
592
+ fatal("Cannot examine the contents of directory '%s'", dir);
593
+ closedir(d);
594
+
595
+ return dir;
596
+}
597
+
598
+/*
599
+ * This is called after the rrdinit
600
+ * These values will be sent on the START event
601
+ */
602
+void set_late_global_environment()
603
+{
604
+ analytics_set_data(&analytics_data.netdata_config_stream_enabled, default_rrdpush_enabled ? "true" : "false");
605
+ analytics_set_data_str(&analytics_data.netdata_config_memory_mode, (char *)rrd_memory_mode_name(default_rrd_memory_mode));
606
+ analytics_set_data(&analytics_data.netdata_config_exporting_enabled, appconfig_get_boolean(&exporting_config, CONFIG_SECTION_EXPORTING, "enabled", 1) ? "true" : "false");
607
+
608
+#ifdef ENABLE_DBENGINE
609
+ {
610
+ char b[16];
611
+ snprintfz(b, 15, "%d", default_rrdeng_page_cache_mb);
612
+ analytics_set_data(&analytics_data.netdata_config_page_cache_size, b);
613
+
614
+ snprintfz(b, 15, "%d", default_multidb_disk_quota_mb);
615
+ analytics_set_data(&analytics_data.netdata_config_multidb_disk_quota, b);
616
+ }
617
+#endif
618
+
619
+#ifdef ENABLE_HTTPS
620
+ analytics_set_data(&analytics_data.netdata_config_https_enabled, "true");
621
+#else
622
+ analytics_set_data(&analytics_data.netdata_config_https_enabled, "false");
623
+#endif
624
+
625
+ if (web_server_mode == WEB_SERVER_MODE_NONE)
626
+ analytics_set_data(&analytics_data.netdata_config_web_enabled, "false");
627
+ else
628
+ analytics_set_data(&analytics_data.netdata_config_web_enabled, "true");
629
+
630
+ analytics_set_data_str(&analytics_data.netdata_config_release_channel, (char *)get_release_channel());
631
+
632
+ {
633
+ BUFFER *bi = buffer_create(1000);
634
+ analytics_build_info(bi);
635
+ analytics_set_data_str(&analytics_data.netdata_buildinfo, (char *)buffer_tostring(bi));
636
+ buffer_free(bi);
637
+ }
638
+
639
+ /* set what we have, to send the START event */
640
+ analytics_setenv_data();
641
+}
642
+
643
+static void get_system_timezone(void)
644
+{
645
+ // avoid flood calls to stat(/etc/localtime)
646
+ // http://stackoverflow.com/questions/4554271/how-to-avoid-excessive-stat-etc-localtime-calls-in-strftime-on-linux
647
+ const char *tz = getenv("TZ");
648
+ if (!tz || !*tz)
649
+ setenv("TZ", config_get(CONFIG_SECTION_GLOBAL, "TZ environment variable", ":/etc/localtime"), 0);
650
+
651
+ char buffer[FILENAME_MAX + 1] = "";
652
+ const char *timezone = NULL;
653
+ ssize_t ret;
654
+
655
+ // use the TZ variable
656
+ if (tz && *tz && *tz != ':') {
657
+ timezone = tz;
658
+ info("TIMEZONE: using TZ variable '%s'", timezone);
659
+ }
660
+
661
+ // use the contents of /etc/timezone
662
+ if (!timezone && !read_file("/etc/timezone", buffer, FILENAME_MAX)) {
663
+ char *c = buffer;
664
+ while (*c) {
665
+ if (unlikely(*c == '\n'))
666
+ *c = '\0';
667
+ c++;
668
+ }
669
+ timezone = buffer;
670
+ info("TIMEZONE: using the contents of /etc/timezone: '%s'", timezone);
671
+ }
672
+
673
+ // read the link /etc/localtime
674
+ if (!timezone) {
675
+ ret = readlink("/etc/localtime", buffer, FILENAME_MAX);
676
+
677
+ if (ret > 0) {
678
+ buffer[ret] = '\0';
679
+
680
+ char *cmp = "/usr/share/zoneinfo/";
681
+ size_t cmp_len = strlen(cmp);
682
+
683
+ char *s = strstr(buffer, cmp);
684
+ if (s && s[cmp_len]) {
685
+ timezone = &s[cmp_len];
686
+ info("TIMEZONE: using the link of /etc/localtime: '%s'", timezone);
687
+ }
688
+ } else
689
+ buffer[0] = '\0';
690
+ }
691
+
692
+ // find the timezone from strftime()
693
+ if (!timezone) {
694
+ time_t t;
695
+ struct tm *tmp, tmbuf;
696
+
697
+ t = now_realtime_sec();
698
+ tmp = localtime_r(&t, &tmbuf);
699
+
700
+ if (tmp != NULL) {
701
+ if (strftime(buffer, FILENAME_MAX, "%Z", tmp) == 0)
702
+ buffer[0] = '\0';
703
+ else {
704
+ buffer[FILENAME_MAX] = '\0';
705
+ timezone = buffer;
706
+ info("TIMEZONE: using strftime(): '%s'", timezone);
707
+ }
708
+ }
709
+ }
710
+
711
+ if (timezone && *timezone) {
712
+ // make sure it does not have illegal characters
713
+ // info("TIMEZONE: fixing '%s'", timezone);
714
+
715
+ size_t len = strlen(timezone);
716
+ char tmp[len + 1];
717
+ char *d = tmp;
718
+ *d = '\0';
719
+
720
+ while (*timezone) {
721
+ if (isalnum(*timezone) || *timezone == '_' || *timezone == '/')
722
+ *d++ = *timezone++;
723
+ else
724
+ timezone++;
725
+ }
726
+ *d = '\0';
727
+ strncpyz(buffer, tmp, len);
728
+ timezone = buffer;
729
+ // info("TIMEZONE: fixed as '%s'", timezone);
730
+ }
731
+
732
+ if (!timezone || !*timezone)
733
+ timezone = "unknown";
734
+
735
+ netdata_configured_timezone = config_get(CONFIG_SECTION_GLOBAL, "timezone", timezone);
736
+}
737
+
738
+void set_global_environment()
739
+{
740
+ {
741
+ char b[16];
742
+ snprintfz(b, 15, "%d", default_rrd_update_every);
743
+ setenv("NETDATA_UPDATE_EVERY", b, 1);
744
+ }
745
+
746
+ setenv("NETDATA_VERSION", program_version, 1);
747
+ setenv("NETDATA_HOSTNAME", netdata_configured_hostname, 1);
748
+ setenv("NETDATA_CONFIG_DIR", verify_required_directory(netdata_configured_user_config_dir), 1);
749
+ setenv("NETDATA_USER_CONFIG_DIR", verify_required_directory(netdata_configured_user_config_dir), 1);
750
+ setenv("NETDATA_STOCK_CONFIG_DIR", verify_required_directory(netdata_configured_stock_config_dir), 1);
751
+ setenv("NETDATA_PLUGINS_DIR", verify_required_directory(netdata_configured_primary_plugins_dir), 1);
752
+ setenv("NETDATA_WEB_DIR", verify_required_directory(netdata_configured_web_dir), 1);
753
+ setenv("NETDATA_CACHE_DIR", verify_required_directory(netdata_configured_cache_dir), 1);
754
+ setenv("NETDATA_LIB_DIR", verify_required_directory(netdata_configured_varlib_dir), 1);
755
+ setenv("NETDATA_LOCK_DIR", netdata_configured_lock_dir, 1);
756
+ setenv("NETDATA_LOG_DIR", verify_required_directory(netdata_configured_log_dir), 1);
757
+ setenv("HOME", verify_required_directory(netdata_configured_home_dir), 1);
758
+ setenv("NETDATA_HOST_PREFIX", netdata_configured_host_prefix, 1);
759
+
760
+ analytics_set_data(&analytics_data.netdata_config_stream_enabled, "null");
761
+ analytics_set_data(&analytics_data.netdata_config_memory_mode, "null");
762
+ analytics_set_data(&analytics_data.netdata_config_exporting_enabled, "null");
763
+ analytics_set_data(&analytics_data.netdata_exporting_connectors, "null");
764
+ analytics_set_data(&analytics_data.netdata_allmetrics_prometheus_used, "null");
765
+ analytics_set_data(&analytics_data.netdata_allmetrics_shell_used, "null");
766
+ analytics_set_data(&analytics_data.netdata_allmetrics_json_used, "null");
767
+ analytics_set_data(&analytics_data.netdata_dashboard_used, "null");
768
+ analytics_set_data(&analytics_data.netdata_collectors, "null");
769
+ analytics_set_data(&analytics_data.netdata_collectors_count, "null");
770
+ analytics_set_data(&analytics_data.netdata_buildinfo, "null");
771
+ analytics_set_data(&analytics_data.netdata_config_page_cache_size, "null");
772
+ analytics_set_data(&analytics_data.netdata_config_multidb_disk_quota, "null");
773
+ analytics_set_data(&analytics_data.netdata_config_https_enabled, "null");
774
+ analytics_set_data(&analytics_data.netdata_config_web_enabled, "null");
775
+ analytics_set_data(&analytics_data.netdata_config_release_channel, "null");
776
+ analytics_set_data(&analytics_data.netdata_mirrored_host_count, "null");
777
+ analytics_set_data(&analytics_data.netdata_mirrored_hosts_reachable, "null");
778
+ analytics_set_data(&analytics_data.netdata_mirrored_hosts_unreachable, "null");
779
+ analytics_set_data(&analytics_data.netdata_notification_methods, "null");
780
+ analytics_set_data(&analytics_data.netdata_alarms_normal, "null");
781
+ analytics_set_data(&analytics_data.netdata_alarms_warning, "null");
782
+ analytics_set_data(&analytics_data.netdata_alarms_critical, "null");
783
+ analytics_set_data(&analytics_data.netdata_charts_count, "null");
784
+ analytics_set_data(&analytics_data.netdata_metrics_count, "null");
785
+ analytics_set_data(&analytics_data.netdata_config_is_parent, "null");
786
+ analytics_set_data(&analytics_data.netdata_config_hosts_available, "null");
787
+ analytics_set_data(&analytics_data.netdata_host_cloud_available, "null");
788
+ analytics_set_data(&analytics_data.netdata_host_aclk_implementation, "null");
789
+ analytics_set_data(&analytics_data.netdata_host_aclk_available, "null");
790
+ analytics_set_data(&analytics_data.netdata_host_agent_claimed, "null");
791
+
792
+ analytics_data.prometheus_hits = 0;
793
+ analytics_data.shell_hits = 0;
794
+ analytics_data.json_hits = 0;
795
+ analytics_data.dashboard_hits = 0;
796
+
797
+ char *default_port = appconfig_get(&netdata_config, CONFIG_SECTION_WEB, "default port", NULL);
798
+ int clean = 0;
799
+ if (!default_port) {
800
+ default_port = strdupz("19999");
801
+ clean = 1;
802
+ }
803
+
804
+ setenv("NETDATA_LISTEN_PORT", default_port, 1);
805
+ if (clean)
806
+ freez(default_port);
807
+
808
+ get_system_timezone();
809
+
810
+ // set the path we need
811
+ char path[1024 + 1], *p = getenv("PATH");
812
+ if (!p)
813
+ p = "/bin:/usr/bin";
814
+ snprintfz(path, 1024, "%s:%s", p, "/sbin:/usr/sbin:/usr/local/bin:/usr/local/sbin");
815
+ setenv("PATH", config_get(CONFIG_SECTION_PLUGINS, "PATH environment variable", path), 1);
816
+
817
+ // python options
818
+ p = getenv("PYTHONPATH");
819
+ if (!p)
820
+ p = "";
821
+ setenv("PYTHONPATH", config_get(CONFIG_SECTION_PLUGINS, "PYTHONPATH environment variable", p), 1);
822
+
823
+ // disable buffering for python plugins
824
+ setenv("PYTHONUNBUFFERED", "1", 1);
825
+
826
+ // switch to standard locale for plugins
827
+ setenv("LC_ALL", "C", 1);
828
+}
829
+
830
+void send_statistics(const char *action, const char *action_result, const char *action_data)
831
+{
832
+ static char *as_script;
833
+
834
+ if (netdata_anonymous_statistics_enabled == -1) {
835
+ char *optout_file = mallocz(
836
+ sizeof(char) *
837
+ (strlen(netdata_configured_user_config_dir) + strlen(".opt-out-from-anonymous-statistics") + 2));
838
+ sprintf(optout_file, "%s/%s", netdata_configured_user_config_dir, ".opt-out-from-anonymous-statistics");
839
+ if (likely(access(optout_file, R_OK) != 0)) {
840
+ as_script = mallocz(
841
+ sizeof(char) *
842
+ (strlen(netdata_configured_primary_plugins_dir) + strlen("anonymous-statistics.sh") + 2));
843
+ sprintf(as_script, "%s/%s", netdata_configured_primary_plugins_dir, "anonymous-statistics.sh");
844
+ if (unlikely(access(as_script, R_OK) != 0)) {
845
+ netdata_anonymous_statistics_enabled = 0;
846
+ info("Anonymous statistics script %s not found.", as_script);
847
+ freez(as_script);
848
+ } else {
849
+ netdata_anonymous_statistics_enabled = 1;
850
+ }
851
+ } else {
852
+ netdata_anonymous_statistics_enabled = 0;
853
+ as_script = NULL;
854
+ }
855
+ freez(optout_file);
856
+ }
857
+ if (!netdata_anonymous_statistics_enabled)
858
+ return;
859
+ if (!action)
860
+ return;
861
+ if (!action_result)
862
+ action_result = "";
863
+ if (!action_data)
864
+ action_data = "";
865
+ char *command_to_run =
866
+ mallocz(sizeof(char) * (strlen(action) + strlen(action_result) + strlen(action_data) + strlen(as_script) + 10));
867
+ pid_t command_pid;
868
+
869
+ sprintf(command_to_run, "%s '%s' '%s' '%s'", as_script, action, action_result, action_data);
870
+ info("%s", command_to_run);
871
+
872
+ FILE *fp = mypopen(command_to_run, &command_pid);
873
+ if (fp) {
874
+ char buffer[100 + 1];
875
+ while (fgets(buffer, 100, fp) != NULL)
876
+ ;
877
+ mypclose(fp, command_pid);
878
+ }
879
+ freez(command_to_run);
880
+}