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