master
c 1,387 lines 48.5 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "analytics.h"
4 #include "common.h"
5 #include "buildinfo.h"
6
7 struct analytics_data analytics_data;
8 extern void analytics_exporting_connectors (BUFFER *b);
9 extern void analytics_exporting_connectors_ssl (BUFFER *b);
10 extern void analytics_build_info (BUFFER *b);
11
12 struct collector {
13 const char *plugin;
14 const char *module;
15 };
16
17 struct array_printer {
18 int c;
19 BUFFER *both;
20 };
21
22 /*
23 * Debug logging
24 */
25 void analytics_log_data(void)
26 {
27 netdata_log_debug(D_ANALYTICS, "NETDATA_CONFIG_STREAM_ENABLED : [%s]", analytics_data.netdata_config_stream_enabled);
28 netdata_log_debug(D_ANALYTICS, "NETDATA_CONFIG_MEMORY_MODE : [%s]", analytics_data.netdata_config_memory_mode);
29 netdata_log_debug(D_ANALYTICS, "NETDATA_CONFIG_EXPORTING_ENABLED : [%s]", analytics_data.netdata_config_exporting_enabled);
30 netdata_log_debug(D_ANALYTICS, "NETDATA_EXPORTING_CONNECTORS : [%s]", analytics_data.netdata_exporting_connectors);
31 netdata_log_debug(D_ANALYTICS, "NETDATA_ALLMETRICS_PROMETHEUS_USED : [%s]", analytics_data.netdata_allmetrics_prometheus_used);
32 netdata_log_debug(D_ANALYTICS, "NETDATA_ALLMETRICS_SHELL_USED : [%s]", analytics_data.netdata_allmetrics_shell_used);
33 netdata_log_debug(D_ANALYTICS, "NETDATA_ALLMETRICS_JSON_USED : [%s]", analytics_data.netdata_allmetrics_json_used);
34 netdata_log_debug(D_ANALYTICS, "NETDATA_DASHBOARD_USED : [%s]", analytics_data.netdata_dashboard_used);
35 netdata_log_debug(D_ANALYTICS, "NETDATA_COLLECTORS : [%s]", analytics_data.netdata_collectors);
36 netdata_log_debug(D_ANALYTICS, "NETDATA_COLLECTORS_COUNT : [%s]", analytics_data.netdata_collectors_count);
37 netdata_log_debug(D_ANALYTICS, "NETDATA_BUILDINFO : [%s]", analytics_data.netdata_buildinfo);
38 netdata_log_debug(D_ANALYTICS, "NETDATA_CONFIG_PAGE_CACHE_SIZE : [%s]", analytics_data.netdata_config_page_cache_size);
39 netdata_log_debug(D_ANALYTICS, "NETDATA_CONFIG_MULTIDB_DISK_QUOTA : [%s]", analytics_data.netdata_config_multidb_disk_quota);
40 netdata_log_debug(D_ANALYTICS, "NETDATA_CONFIG_HTTPS_ENABLED : [%s]", analytics_data.netdata_config_https_enabled);
41 netdata_log_debug(D_ANALYTICS, "NETDATA_CONFIG_WEB_ENABLED : [%s]", analytics_data.netdata_config_web_enabled);
42 netdata_log_debug(D_ANALYTICS, "NETDATA_CONFIG_RELEASE_CHANNEL : [%s]", analytics_data.netdata_config_release_channel);
43 netdata_log_debug(D_ANALYTICS, "NETDATA_MIRRORED_HOST_COUNT : [%s]", analytics_data.netdata_mirrored_host_count);
44 netdata_log_debug(D_ANALYTICS, "NETDATA_MIRRORED_HOSTS_REACHABLE : [%s]", analytics_data.netdata_mirrored_hosts_reachable);
45 netdata_log_debug(D_ANALYTICS, "NETDATA_MIRRORED_HOSTS_UNREACHABLE : [%s]", analytics_data.netdata_mirrored_hosts_unreachable);
46 netdata_log_debug(D_ANALYTICS, "NETDATA_NOTIFICATION_METHODS : [%s]", analytics_data.netdata_notification_methods);
47 netdata_log_debug(D_ANALYTICS, "NETDATA_ALARMS_NORMAL : [%s]", analytics_data.netdata_alarms_normal);
48 netdata_log_debug(D_ANALYTICS, "NETDATA_ALARMS_WARNING : [%s]", analytics_data.netdata_alarms_warning);
49 netdata_log_debug(D_ANALYTICS, "NETDATA_ALARMS_CRITICAL : [%s]", analytics_data.netdata_alarms_critical);
50 netdata_log_debug(D_ANALYTICS, "NETDATA_CHARTS_COUNT : [%s]", analytics_data.netdata_charts_count);
51 netdata_log_debug(D_ANALYTICS, "NETDATA_METRICS_COUNT : [%s]", analytics_data.netdata_metrics_count);
52 netdata_log_debug(D_ANALYTICS, "NETDATA_CONFIG_IS_PARENT : [%s]", analytics_data.netdata_config_is_parent);
53 netdata_log_debug(D_ANALYTICS, "NETDATA_CONFIG_HOSTS_AVAILABLE : [%s]", analytics_data.netdata_config_hosts_available);
54 netdata_log_debug(D_ANALYTICS, "NETDATA_HOST_CLOUD_AVAILABLE : [%s]", analytics_data.netdata_host_cloud_available);
55 netdata_log_debug(D_ANALYTICS, "NETDATA_HOST_ACLK_AVAILABLE : [%s]", analytics_data.netdata_host_aclk_available);
56 netdata_log_debug(D_ANALYTICS, "NETDATA_HOST_ACLK_PROTOCOL : [%s]", analytics_data.netdata_host_aclk_protocol);
57 netdata_log_debug(D_ANALYTICS, "NETDATA_HOST_ACLK_IMPLEMENTATION : [%s]", analytics_data.netdata_host_aclk_implementation);
58 netdata_log_debug(D_ANALYTICS, "NETDATA_HOST_AGENT_CLAIMED : [%s]", analytics_data.netdata_host_agent_claimed);
59 netdata_log_debug(D_ANALYTICS, "NETDATA_HOST_CLOUD_ENABLED : [%s]", analytics_data.netdata_host_cloud_enabled);
60 netdata_log_debug(D_ANALYTICS, "NETDATA_CONFIG_HTTPS_AVAILABLE : [%s]", analytics_data.netdata_config_https_available);
61 netdata_log_debug(D_ANALYTICS, "NETDATA_INSTALL_TYPE : [%s]", analytics_data.netdata_install_type);
62 netdata_log_debug(D_ANALYTICS, "NETDATA_PREBUILT_DISTRO : [%s]", analytics_data.netdata_prebuilt_distro);
63 netdata_log_debug(D_ANALYTICS, "NETDATA_CONFIG_IS_PRIVATE_REGISTRY : [%s]", analytics_data.netdata_config_is_private_registry);
64 netdata_log_debug(D_ANALYTICS, "NETDATA_CONFIG_USE_PRIVATE_REGISTRY: [%s]", analytics_data.netdata_config_use_private_registry);
65 netdata_log_debug(D_ANALYTICS, "NETDATA_CONFIG_OOM_SCORE : [%s]", analytics_data.netdata_config_oom_score);
66 }
67
68 /*
69 * Free data
70 */
71 void analytics_free_data(void)
72 {
73 freez(analytics_data.netdata_config_stream_enabled);
74 analytics_data.netdata_config_stream_enabled = NULL;
75
76 freez(analytics_data.netdata_config_memory_mode);
77 analytics_data.netdata_config_memory_mode = NULL;
78
79 freez(analytics_data.netdata_config_exporting_enabled);
80 analytics_data.netdata_config_exporting_enabled = NULL;
81
82 freez(analytics_data.netdata_exporting_connectors);
83 analytics_data.netdata_exporting_connectors = NULL;
84
85 freez(analytics_data.netdata_allmetrics_prometheus_used);
86 analytics_data.netdata_allmetrics_prometheus_used = NULL;
87
88 freez(analytics_data.netdata_allmetrics_shell_used);
89 analytics_data.netdata_allmetrics_shell_used = NULL;
90
91 freez(analytics_data.netdata_allmetrics_json_used);
92 analytics_data.netdata_allmetrics_json_used = NULL;
93
94 freez(analytics_data.netdata_dashboard_used);
95 analytics_data.netdata_dashboard_used = NULL;
96
97 freez(analytics_data.netdata_collectors);
98 analytics_data.netdata_collectors = NULL;
99
100 freez(analytics_data.netdata_collectors_count);
101 analytics_data.netdata_collectors_count = NULL;
102
103 freez(analytics_data.netdata_buildinfo);
104 analytics_data.netdata_buildinfo = NULL;
105
106 freez(analytics_data.netdata_config_page_cache_size);
107 analytics_data.netdata_config_page_cache_size = NULL;
108
109 freez(analytics_data.netdata_config_multidb_disk_quota);
110 analytics_data.netdata_config_multidb_disk_quota = NULL;
111
112 freez(analytics_data.netdata_config_https_enabled);
113 analytics_data.netdata_config_https_enabled = NULL;
114
115 freez(analytics_data.netdata_config_web_enabled);
116 analytics_data.netdata_config_web_enabled = NULL;
117
118 freez(analytics_data.netdata_config_release_channel);
119 analytics_data.netdata_config_release_channel = NULL;
120
121 freez(analytics_data.netdata_mirrored_host_count);
122 analytics_data.netdata_mirrored_host_count = NULL;
123
124 freez(analytics_data.netdata_mirrored_hosts_reachable);
125 analytics_data.netdata_mirrored_hosts_reachable = NULL;
126
127 freez(analytics_data.netdata_mirrored_hosts_unreachable);
128 analytics_data.netdata_mirrored_hosts_unreachable = NULL;
129
130 freez(analytics_data.netdata_notification_methods);
131 analytics_data.netdata_notification_methods = NULL;
132
133 freez(analytics_data.netdata_alarms_normal);
134 analytics_data.netdata_alarms_normal = NULL;
135
136 freez(analytics_data.netdata_alarms_warning);
137 analytics_data.netdata_alarms_warning = NULL;
138
139 freez(analytics_data.netdata_alarms_critical);
140 analytics_data.netdata_alarms_critical = NULL;
141
142 freez(analytics_data.netdata_charts_count);
143 analytics_data.netdata_charts_count = NULL;
144
145 freez(analytics_data.netdata_metrics_count);
146 analytics_data.netdata_metrics_count = NULL;
147
148 freez(analytics_data.netdata_config_is_parent);
149 analytics_data.netdata_config_is_parent = NULL;
150
151 freez(analytics_data.netdata_config_hosts_available);
152 analytics_data.netdata_config_hosts_available = NULL;
153
154 freez(analytics_data.netdata_host_cloud_available);
155 analytics_data.netdata_host_cloud_available = NULL;
156
157 freez(analytics_data.netdata_host_aclk_available);
158 analytics_data.netdata_host_aclk_available = NULL;
159
160 freez(analytics_data.netdata_host_aclk_protocol);
161 analytics_data.netdata_host_aclk_protocol = NULL;
162
163 freez(analytics_data.netdata_host_aclk_implementation);
164 analytics_data.netdata_host_aclk_implementation = NULL;
165
166 freez(analytics_data.netdata_host_agent_claimed);
167 analytics_data.netdata_host_agent_claimed = NULL;
168
169 freez(analytics_data.netdata_host_cloud_enabled);
170 analytics_data.netdata_host_cloud_enabled = NULL;
171
172 freez(analytics_data.netdata_config_https_available);
173 analytics_data.netdata_config_https_available = NULL;
174
175 freez(analytics_data.netdata_install_type);
176 analytics_data.netdata_install_type = NULL;
177
178 freez(analytics_data.netdata_config_is_private_registry);
179 analytics_data.netdata_config_is_private_registry = NULL;
180
181 freez(analytics_data.netdata_config_use_private_registry);
182 analytics_data.netdata_config_use_private_registry = NULL;
183
184 freez(analytics_data.netdata_config_oom_score);
185 analytics_data.netdata_config_oom_score = NULL;
186
187 freez(analytics_data.netdata_prebuilt_distro);
188 analytics_data.netdata_prebuilt_distro = NULL;
189
190 freez(analytics_data.netdata_fail_reason);
191 analytics_data.netdata_fail_reason = NULL;
192
193 }
194
195 /*
196 * Set a numeric/boolean data with a value
197 */
198 void analytics_set_data(char **name, char *value)
199 {
200 spinlock_lock(&analytics_data.spinlock);
201 if (*name) {
202 analytics_data.data_length -= strlen(*name);
203 freez(*name);
204 }
205 *name = strdupz(value);
206 analytics_data.data_length += strlen(*name);
207 spinlock_unlock(&analytics_data.spinlock);
208 }
209
210 /*
211 * Set a string data with a value
212 */
213 void analytics_set_data_str(char **name, const char *value)
214 {
215 size_t value_string_len;
216 spinlock_lock(&analytics_data.spinlock);
217 if (*name) {
218 analytics_data.data_length -= strlen(*name);
219 freez(*name);
220 }
221 value_string_len = strlen(value) + 4;
222 *name = mallocz(sizeof(char) * value_string_len);
223 snprintfz(*name, value_string_len - 1, "\"%s\"", value);
224 analytics_data.data_length += strlen(*name);
225 spinlock_unlock(&analytics_data.spinlock);
226 }
227
228 /*
229 * Log hits on the allmetrics page, with prometheus parameter
230 */
231 void analytics_log_prometheus(void)
232 {
233 if (netdata_anonymous_statistics_enabled && likely(analytics_data.prometheus_hits < ANALYTICS_MAX_PROMETHEUS_HITS)) {
234 analytics_data.prometheus_hits++;
235 char b[21];
236 snprintfz(b, sizeof(b) - 1, "%zu", analytics_data.prometheus_hits);
237 analytics_set_data(&analytics_data.netdata_allmetrics_prometheus_used, b);
238 }
239 }
240
241 /*
242 * Log hits on the allmetrics page, with shell parameter (or default)
243 */
244 void analytics_log_shell(void)
245 {
246 if (netdata_anonymous_statistics_enabled && likely(analytics_data.shell_hits < ANALYTICS_MAX_SHELL_HITS)) {
247 analytics_data.shell_hits++;
248 char b[21];
249 snprintfz(b, sizeof(b) - 1, "%zu", analytics_data.shell_hits);
250 analytics_set_data(&analytics_data.netdata_allmetrics_shell_used, b);
251 }
252 }
253
254 /*
255 * Log hits on the allmetrics page, with json parameter
256 */
257 void analytics_log_json(void)
258 {
259 if (netdata_anonymous_statistics_enabled && likely(analytics_data.json_hits < ANALYTICS_MAX_JSON_HITS)) {
260 analytics_data.json_hits++;
261 char b[21];
262 snprintfz(b, sizeof(b) - 1, "%zu", analytics_data.json_hits);
263 analytics_set_data(&analytics_data.netdata_allmetrics_json_used, b);
264 }
265 }
266
267 /*
268 * Log hits on the dashboard, (when calling HELLO).
269 */
270 void analytics_log_dashboard(void)
271 {
272 if (netdata_anonymous_statistics_enabled && likely(analytics_data.dashboard_hits < ANALYTICS_MAX_DASHBOARD_HITS)) {
273 analytics_data.dashboard_hits++;
274 char b[21];
275 snprintfz(b, sizeof(b) - 1, "%zu", analytics_data.dashboard_hits);
276 analytics_set_data(&analytics_data.netdata_dashboard_used, b);
277 }
278 }
279
280 /*
281 * Called when setting the oom score
282 */
283 void analytics_report_oom_score(long long int score){
284 char b[21];
285 snprintfz(b, sizeof(b) - 1, "%lld", score);
286 analytics_set_data(&analytics_data.netdata_config_oom_score, b);
287 }
288
289 void analytics_mirrored_hosts(void)
290 {
291 RRDHOST *host;
292 size_t count = 0;
293 size_t reachable = 0;
294 size_t unreachable = 0;
295 char b[21];
296
297 rrd_rdlock();
298 rrdhost_foreach_read(host)
299 {
300 if (rrdhost_flag_check(host, RRDHOST_FLAG_ARCHIVED))
301 continue;
302
303 ((host == localhost || !rrdhost_flag_check(host, RRDHOST_FLAG_ORPHAN)) ? reachable++ : unreachable++);
304
305 count++;
306 }
307 rrd_rdunlock();
308
309 snprintfz(b, sizeof(b) - 1, "%zu", count);
310 analytics_set_data(&analytics_data.netdata_mirrored_host_count, b);
311 snprintfz(b, sizeof(b) - 1, "%zu", reachable);
312 analytics_set_data(&analytics_data.netdata_mirrored_hosts_reachable, b);
313 snprintfz(b, sizeof(b) - 1, "%zu", unreachable);
314 analytics_set_data(&analytics_data.netdata_mirrored_hosts_unreachable, b);
315 }
316
317 void analytics_exporters(void)
318 {
319 //when no exporters are available, an empty string will be sent
320 //decide if something else is more suitable (but probably not null)
321 BUFFER *bi = buffer_create(1000, NULL);
322 analytics_exporting_connectors(bi);
323 analytics_set_data_str(&analytics_data.netdata_exporting_connectors, (char *)buffer_tostring(bi));
324 buffer_free(bi);
325 }
326
327 int collector_counter_callb(const DICTIONARY_ITEM *item __maybe_unused, void *entry, void *data) {
328
329 struct array_printer *ap = (struct array_printer *)data;
330 struct collector *col = (struct collector *)entry;
331
332 BUFFER *bt = ap->both;
333
334 if (likely(ap->c)) {
335 buffer_strcat(bt, ",");
336 }
337
338 buffer_strcat(bt, "{");
339 buffer_strcat(bt, " \"plugin\": \"");
340 buffer_strcat(bt, col->plugin);
341 buffer_strcat(bt, "\", \"module\":\"");
342 buffer_strcat(bt, col->module);
343 buffer_strcat(bt, "\" }");
344
345 (ap->c)++;
346
347 return 0;
348 }
349
350 /*
351 * Create a JSON array of available collectors, same as in api/v1/info
352 */
353 void analytics_collectors(void)
354 {
355 RRDSET *st;
356 DICTIONARY *dict = dictionary_create(DICT_OPTION_SINGLE_THREADED);
357 char name[500];
358 BUFFER *bt = buffer_create(1000, NULL);
359
360 rrdset_foreach_read(st, localhost) {
361 if(!rrdset_is_available_for_viewers(st))
362 continue;
363
364 struct collector col = {
365 .plugin = rrdset_plugin_name(st),
366 .module = rrdset_module_name(st)
367 };
368 snprintfz(name, sizeof(name) - 1, "%s:%s", col.plugin, col.module);
369 dictionary_set(dict, name, &col, sizeof(struct collector));
370 }
371 rrdset_foreach_done(st);
372
373 struct array_printer ap;
374 ap.c = 0;
375 ap.both = bt;
376
377 dictionary_walkthrough_read(dict, collector_counter_callb, &ap);
378 dictionary_destroy(dict);
379
380 analytics_set_data(&analytics_data.netdata_collectors, (char *)buffer_tostring(ap.both));
381
382 {
383 char b[21];
384 snprintfz(b, sizeof(b) - 1, "%d", ap.c);
385 analytics_set_data(&analytics_data.netdata_collectors_count, b);
386 }
387
388 buffer_free(bt);
389 }
390
391 /*
392 * Run alarm-notify.sh script using the dump_methods parameter
393 * SEND_CUSTOM is always available
394 */
395 void analytics_alarms_notifications(void)
396 {
397 char *script;
398 script = mallocz(
399 sizeof(char) * (strlen(netdata_configured_primary_plugins_dir) + strlen("alarm-notify.sh dump_methods") + 2));
400 sprintf(script, "%s/%s", netdata_configured_primary_plugins_dir, "alarm-notify.sh");
401 if (unlikely(access(script, R_OK) != 0)) {
402 netdata_log_info("Alarm notify script %s not found.", script);
403 freez(script);
404 return;
405 }
406
407 strcat(script, " dump_methods");
408
409 netdata_log_debug(D_ANALYTICS, "Executing %s", script);
410
411 BUFFER *b = buffer_create(1000, NULL);
412 int cnt = 0;
413 POPEN_INSTANCE *instance = spawn_popen_run(script);
414 if (instance) {
415 char line[200 + 1];
416
417 while (fgets(line, 200, spawn_popen_stdout(instance)) != NULL) {
418 char *end = line;
419 while (*end && *end != '\n')
420 end++;
421 *end = '\0';
422
423 if (likely(cnt))
424 buffer_strcat(b, "|");
425
426 buffer_strcat(b, line);
427
428 cnt++;
429 }
430 spawn_popen_wait(instance);
431 }
432 freez(script);
433
434 analytics_set_data_str(&analytics_data.netdata_notification_methods, (char *)buffer_tostring(b));
435
436 buffer_free(b);
437 }
438
439 static void analytics_get_install_type(struct rrdhost_system_info *system_info) {
440 const char *install_type = rrdhost_system_info_install_type(system_info);
441 if(!install_type)
442 install_type = "unknown";
443
444 analytics_set_data_str(&analytics_data.netdata_install_type, install_type);
445
446 const char *prebuilt_dist = rrdhost_system_info_prebuilt_dist(system_info);
447 if(prebuilt_dist)
448 analytics_set_data_str(&analytics_data.netdata_prebuilt_distro, prebuilt_dist);
449 }
450
451 /*
452 * Pick up if https is actually used
453 */
454 void analytics_https(void)
455 {
456 BUFFER *b = buffer_create(30, NULL);
457 analytics_exporting_connectors_ssl(b);
458
459 buffer_strcat(b, stream_sender_is_connected_with_ssl(localhost) ? "streaming|" : "|");
460 buffer_strcat(b, netdata_ssl_web_server_ctx ? "web" : "");
461
462 analytics_set_data_str(&analytics_data.netdata_config_https_available, (char *)buffer_tostring(b));
463 buffer_free(b);
464 }
465
466 void analytics_charts(void)
467 {
468 RRDSET *st;
469 size_t c = 0;
470
471 rrdset_foreach_read(st, localhost)
472 if(rrdset_is_available_for_viewers(st)) c++;
473 rrdset_foreach_done(st);
474
475 analytics_data.charts_count = c;
476 {
477 char b[21];
478 snprintfz(b, sizeof(b) - 1, "%zu", c);
479 analytics_set_data(&analytics_data.netdata_charts_count, b);
480 }
481 }
482
483 void analytics_metrics(void)
484 {
485 RRDSET *st;
486 size_t dimensions = 0;
487 rrdset_foreach_read(st, localhost) {
488 if (rrdset_is_available_for_viewers(st)) {
489 RRDDIM *rd;
490 rrddim_foreach_read(rd, st) {
491 if (rrddim_option_check(rd, RRDDIM_OPTION_HIDDEN) || rrddim_flag_check(rd, RRDDIM_FLAG_OBSOLETE))
492 continue;
493 dimensions++;
494 }
495 rrddim_foreach_done(rd);
496 }
497 }
498 rrdset_foreach_done(st);
499
500 analytics_data.metrics_count = dimensions;
501 {
502 char b[21];
503 snprintfz(b, sizeof(b) - 1, "%zu", dimensions);
504 analytics_set_data(&analytics_data.netdata_metrics_count, b);
505 }
506 }
507
508 void analytics_alarms(void)
509 {
510 size_t alarm_warn = 0, alarm_crit = 0, alarm_normal = 0;
511 char b[21];
512 RRDCALC *rc;
513 foreach_rrdcalc_in_rrdhost_read(localhost, rc) {
514 if (unlikely(!rc->rrdset || !rc->rrdset->last_collected_time.tv_sec))
515 continue;
516
517 switch (rc->status) {
518 case RRDCALC_STATUS_WARNING:
519 alarm_warn++;
520 break;
521 case RRDCALC_STATUS_CRITICAL:
522 alarm_crit++;
523 break;
524 default:
525 alarm_normal++;
526 }
527 }
528 foreach_rrdcalc_in_rrdhost_done(rc);
529
530 snprintfz(b, sizeof(b) - 1, "%zu", alarm_normal);
531 analytics_set_data(&analytics_data.netdata_alarms_normal, b);
532 snprintfz(b, sizeof(b) - 1, "%zu", alarm_warn);
533 analytics_set_data(&analytics_data.netdata_alarms_warning, b);
534 snprintfz(b, sizeof(b) - 1, "%zu", alarm_crit);
535 analytics_set_data(&analytics_data.netdata_alarms_critical, b);
536 }
537
538 /*
539 * Misc attributes to get (run from start)
540 */
541 void analytics_misc(void)
542 {
543 analytics_set_data(&analytics_data.netdata_host_cloud_available, "true");
544 analytics_set_data_str(&analytics_data.netdata_host_aclk_implementation, "Next Generation");
545
546 analytics_data.exporting_enabled = inicfg_get_boolean(&exporting_config, CONFIG_SECTION_EXPORTING, "enabled", CONFIG_BOOLEAN_NO);
547 analytics_set_data(&analytics_data.netdata_config_exporting_enabled, analytics_data.exporting_enabled ? "true" : "false");
548
549 analytics_set_data(&analytics_data.netdata_config_is_private_registry, "false");
550 analytics_set_data(&analytics_data.netdata_config_use_private_registry, "false");
551
552 if (strcmp(
553 inicfg_get(&netdata_config, CONFIG_SECTION_REGISTRY, "registry to announce", "https://registry.my-netdata.io"),
554 "https://registry.my-netdata.io") != 0)
555 analytics_set_data(&analytics_data.netdata_config_use_private_registry, "true");
556
557 //do we need both registry to announce and enabled to indicate that this is a private registry ?
558 if (inicfg_get_boolean(&netdata_config, CONFIG_SECTION_REGISTRY, "enabled", CONFIG_BOOLEAN_NO) &&
559 web_server_mode != WEB_SERVER_MODE_NONE)
560 analytics_set_data(&analytics_data.netdata_config_is_private_registry, "true");
561 }
562
563 void analytics_aclk(void)
564 {
565 if (aclk_online()) {
566 analytics_set_data(&analytics_data.netdata_host_aclk_available, "true");
567 analytics_set_data_str(&analytics_data.netdata_host_aclk_protocol, "New");
568 }
569 else
570 analytics_set_data(&analytics_data.netdata_host_aclk_available, "false");
571 }
572
573 /*
574 * Get the meta data, called from the thread once after the original delay
575 * These are values that won't change during agent runtime, and therefore
576 * don't try to read them on each META event send
577 */
578 void analytics_gather_immutable_meta_data(void)
579 {
580 analytics_misc();
581 analytics_exporters();
582 analytics_https();
583 }
584
585 /*
586 * Get the meta data, called from the thread on every heartbeat, and right before the EXIT event
587 * These are values that can change between agent restarts, and therefore
588 * try to read them on each META event send
589 */
590 void analytics_gather_mutable_meta_data(void)
591 {
592 analytics_collectors();
593 analytics_alarms();
594 analytics_charts();
595 analytics_metrics();
596 analytics_aclk();
597 analytics_mirrored_hosts();
598 analytics_alarms_notifications();
599
600 analytics_set_data(
601 &analytics_data.netdata_config_is_parent, (rrdhost_hosts_available() > 1 || netdata_conf_is_parent()) ? "true" : "false");
602
603 analytics_set_data(&analytics_data.netdata_host_agent_claimed, is_agent_claimed() ? "true" : "false");
604
605 {
606 char b[21];
607 snprintfz(b, sizeof(b) - 1, "%zu", analytics_data.prometheus_hits);
608 analytics_set_data(&analytics_data.netdata_allmetrics_prometheus_used, b);
609
610 snprintfz(b, sizeof(b) - 1, "%zu", analytics_data.shell_hits);
611 analytics_set_data(&analytics_data.netdata_allmetrics_shell_used, b);
612
613 snprintfz(b, sizeof(b) - 1, "%zu", analytics_data.json_hits);
614 analytics_set_data(&analytics_data.netdata_allmetrics_json_used, b);
615
616 snprintfz(b, sizeof(b) - 1, "%zu", analytics_data.dashboard_hits);
617 analytics_set_data(&analytics_data.netdata_dashboard_used, b);
618
619 snprintfz(b, sizeof(b) - 1, "%zu", rrdhost_hosts_available());
620 analytics_set_data(&analytics_data.netdata_config_hosts_available, b);
621 }
622 }
623
624 void analytics_main_cleanup(void *pptr)
625 {
626 struct netdata_static_thread *static_thread = CLEANUP_FUNCTION_GET_PTR(pptr);
627 if(!static_thread) return;
628
629 static_thread->enabled = NETDATA_MAIN_THREAD_EXITING;
630
631 analytics_free_data();
632
633 static_thread->enabled = NETDATA_MAIN_THREAD_EXITED;
634 }
635
636 /*
637 * The analytics thread. Sleep for ANALYTICS_INIT_SLEEP_SEC,
638 * gather the data, and then go to a loop where every ANALYTICS_HEARTBEAT
639 * it will send a new META event after gathering data that could be changed
640 * while the agent is running
641 */
642 void *analytics_main(void *ptr)
643 {
644 CLEANUP_FUNCTION_REGISTER(analytics_main_cleanup) cleanup_ptr = ptr;
645 unsigned int sec = 0;
646 heartbeat_t hb;
647 heartbeat_init(&hb, USEC_PER_SEC);
648
649 netdata_log_debug(D_ANALYTICS, "Analytics thread starts");
650
651 // first delay after agent start
652 while (service_running(SERVICE_ANALYTICS) && likely(sec <= ANALYTICS_INIT_SLEEP_SEC)) {
653 heartbeat_next(&hb);
654 sec++;
655 if (sec == ANALYTICS_INIT_IMMUTABLE_DATA_SEC)
656 analytics_gather_immutable_meta_data();
657 }
658
659 if (unlikely(!service_running(SERVICE_ANALYTICS)))
660 goto cleanup;
661
662 analytics_gather_mutable_meta_data();
663 analytics_log_data();
664
665 sec = 0;
666 while (1) {
667 heartbeat_next(&hb);
668 sec++;
669
670 if (unlikely(!service_running(SERVICE_ANALYTICS)))
671 break;
672
673 if (likely(sec < ANALYTICS_HEARTBEAT))
674 continue;
675
676 analytics_gather_mutable_meta_data();
677 analytics_log_data();
678
679 sec = 0;
680 }
681
682 cleanup:
683 return NULL;
684 }
685
686 /*
687 * This is called after the rrdinit
688 * These values will be sent on the START event
689 */
690 void set_late_analytics_variables(struct rrdhost_system_info *system_info)
691 {
692 analytics_set_data(&analytics_data.netdata_config_stream_enabled, stream_send.enabled ? "true" : "false");
693 analytics_set_data_str(&analytics_data.netdata_config_memory_mode, (char *)rrd_memory_mode_name(default_rrd_memory_mode));
694 analytics_set_data(&analytics_data.netdata_host_cloud_enabled, "true");
695
696 #ifdef ENABLE_DBENGINE
697 {
698 char b[16];
699 snprintfz(b, sizeof(b) - 1, "%d", default_rrdeng_page_cache_mb);
700 analytics_set_data(&analytics_data.netdata_config_page_cache_size, b);
701
702 snprintfz(b, sizeof(b) - 1, "%d", default_multidb_disk_quota_mb);
703 analytics_set_data(&analytics_data.netdata_config_multidb_disk_quota, b);
704 }
705 #endif
706
707 analytics_set_data(&analytics_data.netdata_config_https_enabled, "true");
708
709 if (web_server_mode == WEB_SERVER_MODE_NONE)
710 analytics_set_data(&analytics_data.netdata_config_web_enabled, "false");
711 else
712 analytics_set_data(&analytics_data.netdata_config_web_enabled, "true");
713
714 analytics_set_data_str(&analytics_data.netdata_config_release_channel, (char *)get_release_channel());
715
716 {
717 BUFFER *bi = buffer_create(1000, NULL);
718 analytics_build_info(bi);
719 analytics_set_data_str(&analytics_data.netdata_buildinfo, (char *)buffer_tostring(bi));
720 buffer_free(bi);
721 }
722
723 analytics_get_install_type(system_info);
724 }
725
726 #ifdef OS_WINDOWS
727 static void get_timezone_win_id(char *win_id, DWORD win_size)
728 {
729 win_id[0] = '\0';
730 HKEY hKey;
731 LSTATUS ret = RegOpenKeyExA(HKEY_LOCAL_MACHINE,
732 "SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation",
733 0,
734 KEY_READ,
735 &hKey);
736 if (ret != ERROR_SUCCESS)
737 return;
738
739 DWORD valueType;
740 RegQueryValueExA(hKey, "TimeZoneKeyName", NULL, &valueType, (LPBYTE)win_id, &win_size);
741
742 RegCloseKey(hKey);
743 }
744
745 static void get_win_geoiso(char *geo_name, int length) {
746 GEOID id = GetUserGeoID(GEOCLASS_NATION);
747 int res = GetGeoInfoA(id, GEO_ISO2, geo_name, length, 0);
748 if (!res)
749 geo_name[0] = '\0';
750 }
751
752 static int map_windows_tz_to_iana(char *out, char *win_id, char *geo_name) {
753 if (*win_id == '\0')
754 return -1;
755
756 FILE *fp = fopen("c:\\Windows\\Globalization\\Time Zone\\TimezoneMapping.xml", "r");
757 if (!fp)
758 return -1;
759
760 char buffer[CONFIG_FILE_LINE_MAX + 1];
761 char win_id_match[512];
762 bool copied = 0;
763
764 snprintfz(win_id_match, sizeof(win_id_match), "\"%s\"", win_id);
765
766 while (fgets(buffer, CONFIG_FILE_LINE_MAX, fp) != NULL) {
767 buffer[CONFIG_FILE_LINE_MAX] = '\0';
768
769 char *s = strstr(buffer, win_id_match);
770 if (!s) {
771 if (!copied)
772 continue;
773 else // Country codes do not match, but we found the zone
774 break;
775 }
776
777 //Escape:' <MapTZ TZID="'
778 s = &buffer[15];
779 char *end = strchr(s, '"');
780 if (!end)
781 continue;
782
783 *end = '\0';
784
785 strncpyz(out, s, strlen(s));
786
787 //Escape:" Region="
788 char *cmpregion = end+ 10;
789 if (!strncmp(cmpregion, geo_name, 2))
790 break;
791
792 copied = 1;
793 }
794
795 fclose(fp);
796 return 0;
797 }
798 #endif
799
800 // Detect the current IANA timezone name from the system.
801 // Returns a pointer into the provided buffer, or NULL if detection fails.
802 const char *detect_system_timezone_name(char *buffer, size_t buffer_size) {
803 const char *timezone = NULL;
804
805 #ifdef OS_WINDOWS
806 char geo_name[128];
807 char win_zone[256];
808 get_timezone_win_id(win_zone, 256);
809 get_win_geoiso(geo_name, 128);
810 if (!map_windows_tz_to_iana(buffer, win_zone, geo_name))
811 timezone = buffer;
812 #else
813 // read the /etc/localtime symlink first — this is the authoritative source
814 // on modern Linux (timedatectl always updates it, but /etc/timezone can lag)
815 {
816 ssize_t ret = readlink("/etc/localtime", buffer, buffer_size - 1);
817 if (ret > 0) {
818 buffer[ret] = '\0';
819
820 const char *cmp = "/usr/share/zoneinfo/";
821 size_t cmp_len = strlen(cmp);
822
823 char *s = strstr(buffer, cmp);
824 if (s && s[cmp_len])
825 timezone = &s[cmp_len];
826 }
827 }
828
829 // fall back to /etc/timezone (Debian/Ubuntu)
830 if (!timezone && !read_txt_file("/etc/timezone", buffer, buffer_size)) {
831 timezone = buffer;
832 }
833 #endif
834
835 if (timezone && *timezone) {
836 // sanitize in-place: keep only alnum, '_', '/', '-', '+'
837 char *d = buffer;
838 const char *src = timezone;
839 const char *end = buffer + buffer_size - 1;
840 while (*src && d < end) {
841 if (isalnum((uint8_t)*src) || *src == '_' || *src == '/' || *src == '-' || *src == '+')
842 *d++ = *src;
843 src++;
844 }
845 *d = '\0';
846 timezone = buffer;
847 }
848
849 return (timezone && *timezone) ? timezone : NULL;
850 }
851
852 // Set at startup: true when the user explicitly set "timezone" in netdata.conf.
853 static bool timezone_user_configured = false;
854
855 // True when the timezone name came from a proper source (config,
856 // /etc/localtime, /etc/timezone, TZ env var) rather than from the strftime("%Z")
857 // fallback which only produces bare abbreviations like "CEST" or "PST".
858 static bool timezone_is_tzdb_name = false;
859
860 static bool timezone_abbrev_normalize(char *dst, size_t dst_size, const char *src);
861
862 bool system_timezone_is_user_configured(void) {
863 return timezone_user_configured;
864 }
865
866 bool system_timezone_is_tzdb_name(void) {
867 return timezone_is_tzdb_name;
868 }
869
870 void get_system_timezone(void)
871 {
872 char buffer[FILENAME_MAX + 1] = "";
873 const char *timezone = NULL;
874
875 #ifndef OS_WINDOWS
876 // avoid flood calls to stat(/etc/localtime)
877 // http://stackoverflow.com/questions/4554271/how-to-avoid-excessive-stat-etc-localtime-calls-in-strftime-on-linux
878 const char *tz = getenv("TZ");
879 if (!tz || !*tz) {
880 setenv("TZ", inicfg_get(&netdata_config, CONFIG_SECTION_ENV_VARS, "TZ", ":/etc/localtime"), 1);
881 tz = getenv("TZ");
882 }
883
884 // use the TZ variable if it's an explicit IANA name (not a path starting with ':')
885 if (tz && *tz && *tz != ':') {
886 timezone = tz;
887 timezone_is_tzdb_name = true;
888 netdata_log_info("TIMEZONE: using TZ variable '%s'", timezone);
889 }
890 #endif
891
892 // Detect from system sources (/etc/localtime symlink, /etc/timezone, Windows API)
893 if (!timezone) {
894 timezone = detect_system_timezone_name(buffer, sizeof(buffer));
895 if (timezone) {
896 timezone_is_tzdb_name = true;
897 netdata_log_info("TIMEZONE: detected '%s'", timezone);
898 }
899 }
900
901 // Last resort: use strftime %Z (gives abbreviation, not IANA name).
902 // timezone_is_tzdb_name stays false so refresh_system_timezone() won't
903 // try to resolve it via tzalloc() or the tzfile parser.
904 if (!timezone) {
905 time_t t;
906 struct tm *tmp, tmbuf;
907
908 t = now_realtime_sec();
909 tmp = localtime_r(&t, &tmbuf);
910
911 if (tmp != NULL) {
912 if (strftime(buffer, FILENAME_MAX, "%Z", tmp) != 0) {
913 buffer[FILENAME_MAX] = '\0';
914 timezone = buffer;
915 netdata_log_info("TIMEZONE: using strftime(): '%s'", timezone);
916 }
917 }
918 }
919
920 if (!timezone || !*timezone)
921 timezone = "unknown";
922
923 // Check if the user explicitly set "timezone" in netdata.conf BEFORE
924 // inicfg_get auto-populates it with the detected default.
925 timezone_user_configured = inicfg_exists(&netdata_config, CONFIG_SECTION_GLOBAL, "timezone");
926
927 char safe_timezone[FILENAME_MAX + 1];
928 const char *default_timezone = timezone;
929
930 if (timezone_is_tzdb_name) {
931 if (!timezone_name_is_safe_tzdb_path(timezone)) {
932 netdata_log_error("TIMEZONE: detected unsafe tzdb timezone '%s', ignoring", timezone);
933 default_timezone = "unknown";
934 }
935 } else if (!timezone_abbrev_normalize(safe_timezone, sizeof(safe_timezone), timezone)) {
936 default_timezone = "unknown";
937 } else {
938 default_timezone = safe_timezone;
939 }
940
941 // inicfg_get returns a config-system-owned pointer, stable for the process lifetime
942 const char *configured_tz = inicfg_get(&netdata_config, CONFIG_SECTION_GLOBAL, "timezone", default_timezone);
943
944 // Treat "timezone =" (empty value) as not user-configured,
945 // and fall back to the auto-detected timezone.
946 if (timezone_user_configured && (!configured_tz || !*configured_tz)) {
947 timezone_user_configured = false;
948 configured_tz = default_timezone;
949 }
950
951 // If the user explicitly configured a timezone, treat it as a valid tzdb name
952 // (the user is responsible for providing a valid value).
953 if (timezone_user_configured)
954 timezone_is_tzdb_name = true;
955
956 // Compute abbreviation and UTC offset, then set all three atomically
957 refresh_system_timezone(configured_tz, timezone_is_tzdb_name);
958 }
959
960 static inline uint32_t tzif_read_be32(const unsigned char *src) {
961 return ((uint32_t)src[0] << 24) |
962 ((uint32_t)src[1] << 16) |
963 ((uint32_t)src[2] << 8) |
964 (uint32_t)src[3];
965 }
966
967 static inline int64_t tzif_read_be64(const unsigned char *src) {
968 return (int64_t)(
969 ((uint64_t)src[0] << 56) |
970 ((uint64_t)src[1] << 48) |
971 ((uint64_t)src[2] << 40) |
972 ((uint64_t)src[3] << 32) |
973 ((uint64_t)src[4] << 24) |
974 ((uint64_t)src[5] << 16) |
975 ((uint64_t)src[6] << 8) |
976 (uint64_t)src[7]
977 );
978 }
979
980 bool timezone_name_is_safe_tzdb_path(const char *timezone) {
981 if (!timezone || !*timezone || *timezone == '/')
982 return false;
983
984 for (const char *p = timezone; *p; p++) {
985 if (!(isalnum((uint8_t)*p) || *p == '_' || *p == '/' || *p == '-' || *p == '+'))
986 return false;
987 }
988
989 return true;
990 }
991
992 static bool timezone_abbrev_normalize(char *dst, size_t dst_size, const char *src) {
993 if (!dst || dst_size < 2 || !src || !*src)
994 return false;
995
996 size_t len = 0;
997 char *end = dst + dst_size - 1;
998
999 while (*src && dst < end) {
1000 if (!(isalnum((uint8_t)*src) || *src == '_' || *src == '+' || *src == '-'))
1001 return false;
1002
1003 *dst++ = *src++;
1004 len++;
1005 }
1006
1007 *dst = '\0';
1008 return len != 0 && *src == '\0';
1009 }
1010
1011 static bool timezone_info_from_tm(struct tm *tmp, char *abbrev, size_t abbrev_size, int32_t *offset) {
1012 if (!tmp)
1013 return false;
1014
1015 int32_t new_offset = 0;
1016 char offset_str[16];
1017
1018 if (strftime(abbrev, abbrev_size, "%Z", tmp) == 0)
1019 strncpyz(abbrev, "UTC", abbrev_size - 1);
1020
1021 if (strftime(offset_str, sizeof(offset_str), "%z", tmp) != 0) {
1022 char sign = offset_str[0] == '-' || offset_str[0] == '+' ? offset_str[0] : '+';
1023 int hours = (isdigit((uint8_t)offset_str[1]) ? (offset_str[1] - '0') : 0) * 10 +
1024 (isdigit((uint8_t)offset_str[2]) ? (offset_str[2] - '0') : 0);
1025 int minutes = (isdigit((uint8_t)offset_str[3]) ? (offset_str[3] - '0') : 0) * 10 +
1026 (isdigit((uint8_t)offset_str[4]) ? (offset_str[4] - '0') : 0);
1027
1028 new_offset = (hours * 3600) + (minutes * 60);
1029 if (sign == '-')
1030 new_offset = -new_offset;
1031 }
1032
1033 *offset = new_offset;
1034 return true;
1035 }
1036
1037 static bool current_process_timezone_info(time_t t, char *abbrev, size_t abbrev_size, int32_t *offset) {
1038 struct tm *tmp, tmbuf;
1039 tmp = localtime_r(&t, &tmbuf);
1040 return timezone_info_from_tm(tmp, abbrev, abbrev_size, offset);
1041 }
1042
1043 #ifndef OS_WINDOWS
1044 struct tzif_header {
1045 char magic[4];
1046 char version;
1047 char reserved[15];
1048 unsigned char ttisgmtcnt[4];
1049 unsigned char ttisstdcnt[4];
1050 unsigned char leapcnt[4];
1051 unsigned char timecnt[4];
1052 unsigned char typecnt[4];
1053 unsigned char charcnt[4];
1054 };
1055
1056 struct tzif_type {
1057 int32_t gmtoff;
1058 uint8_t isdst;
1059 uint8_t abbrind;
1060 };
1061
1062 static bool tzif_skip_bytes(FILE *fp, size_t bytes) {
1063 char buffer[256];
1064 while (bytes) {
1065 size_t chunk = bytes > sizeof(buffer) ? sizeof(buffer) : bytes;
1066 if (fread(buffer, 1, chunk, fp) != chunk)
1067 return false;
1068 bytes -= chunk;
1069 }
1070 return true;
1071 }
1072
1073 // RFC 8536 sane upper bounds — real tzfiles are far smaller,
1074 // but we allow headroom for unusual / future data.
1075 #define TZIF_MAX_TIMECNT 2048
1076 #define TZIF_MAX_TYPECNT 256
1077 #define TZIF_MAX_CHARCNT 2048
1078 #define TZIF_MAX_LEAPCNT 50
1079 #define TZIF_MAX_ISSTDCNT 256
1080 #define TZIF_MAX_ISGMTCNT 256
1081
1082 static bool tzif_validate_header_counts(uint32_t timecnt, uint32_t typecnt, uint32_t charcnt,
1083 uint32_t leapcnt, uint32_t ttisstdcnt, uint32_t ttisgmtcnt) {
1084 if (timecnt > TZIF_MAX_TIMECNT || typecnt > TZIF_MAX_TYPECNT ||
1085 charcnt > TZIF_MAX_CHARCNT || leapcnt > TZIF_MAX_LEAPCNT ||
1086 ttisstdcnt > TZIF_MAX_ISSTDCNT || ttisgmtcnt > TZIF_MAX_ISGMTCNT)
1087 return false;
1088
1089 // ttisstdcnt and ttisgmtcnt must be 0 or equal to typecnt per RFC 8536
1090 if ((ttisstdcnt != 0 && ttisstdcnt != typecnt) ||
1091 (ttisgmtcnt != 0 && ttisgmtcnt != typecnt))
1092 return false;
1093
1094 return true;
1095 }
1096
1097 static bool tzif_skip_block(FILE *fp, const struct tzif_header *hdr, size_t time_size) {
1098 uint32_t ttisgmtcnt = tzif_read_be32(hdr->ttisgmtcnt);
1099 uint32_t ttisstdcnt = tzif_read_be32(hdr->ttisstdcnt);
1100 uint32_t leapcnt = tzif_read_be32(hdr->leapcnt);
1101 uint32_t timecnt = tzif_read_be32(hdr->timecnt);
1102 uint32_t typecnt = tzif_read_be32(hdr->typecnt);
1103 uint32_t charcnt = tzif_read_be32(hdr->charcnt);
1104
1105 if (!tzif_validate_header_counts(timecnt, typecnt, charcnt, leapcnt, ttisstdcnt, ttisgmtcnt))
1106 return false;
1107
1108 size_t bytes = (size_t)timecnt * time_size +
1109 (size_t)timecnt +
1110 (size_t)typecnt * 6 +
1111 (size_t)charcnt +
1112 (size_t)leapcnt * (time_size + 4) +
1113 (size_t)ttisstdcnt +
1114 (size_t)ttisgmtcnt;
1115
1116 return tzif_skip_bytes(fp, bytes);
1117 }
1118
1119 static int tzif_default_type_index(const struct tzif_type *types, uint32_t typecnt) {
1120 if (!types || !typecnt)
1121 return -1;
1122
1123 for (uint32_t i = 0; i < typecnt; i++) {
1124 if (!types[i].isdst)
1125 return (int)i;
1126 }
1127
1128 return 0;
1129 }
1130
1131 static bool timezone_info_from_tzfile(const char *timezone, time_t t, char *abbrev, size_t abbrev_size, int32_t *offset) {
1132 if (!timezone_name_is_safe_tzdb_path(timezone))
1133 return false;
1134
1135 const char *tzdir = getenv("TZDIR");
1136 if (!tzdir || !*tzdir)
1137 tzdir = "/usr/share/zoneinfo";
1138
1139 char path[FILENAME_MAX + 1];
1140 snprintfz(path, sizeof(path), "%s/%s", tzdir, timezone);
1141
1142 FILE *fp = fopen(path, "rb");
1143 if (!fp)
1144 return false;
1145
1146 bool ok = false;
1147 struct tzif_header hdr;
1148
1149 if (fread(&hdr, 1, sizeof(hdr), fp) != sizeof(hdr))
1150 goto cleanup;
1151
1152 if (memcmp(hdr.magic, "TZif", 4) != 0)
1153 goto cleanup;
1154
1155 if (hdr.version >= '2') {
1156 if (!tzif_skip_block(fp, &hdr, 4))
1157 goto cleanup;
1158
1159 if (fread(&hdr, 1, sizeof(hdr), fp) != sizeof(hdr))
1160 goto cleanup;
1161
1162 if (memcmp(hdr.magic, "TZif", 4) != 0)
1163 goto cleanup;
1164 }
1165
1166 uint32_t timecnt = tzif_read_be32(hdr.timecnt);
1167 uint32_t typecnt = tzif_read_be32(hdr.typecnt);
1168 uint32_t charcnt = tzif_read_be32(hdr.charcnt);
1169 uint32_t leapcnt = tzif_read_be32(hdr.leapcnt);
1170 uint32_t ttisstdcnt = tzif_read_be32(hdr.ttisstdcnt);
1171 uint32_t ttisgmtcnt = tzif_read_be32(hdr.ttisgmtcnt);
1172 size_t time_size = (hdr.version >= '2') ? 8 : 4;
1173
1174 if (!typecnt || !charcnt)
1175 goto cleanup;
1176
1177 if (!tzif_validate_header_counts(timecnt, typecnt, charcnt, leapcnt, ttisstdcnt, ttisgmtcnt))
1178 goto cleanup;
1179
1180 int64_t *transition_times = callocz(timecnt ? timecnt : 1, sizeof(*transition_times));
1181 uint8_t *transition_types = callocz(timecnt ? timecnt : 1, sizeof(*transition_types));
1182 struct tzif_type *types = callocz(typecnt, sizeof(*types));
1183 char *abbrs = callocz(charcnt + 1, sizeof(*abbrs));
1184
1185 unsigned char timebuf[8];
1186 unsigned char typebuf[6];
1187
1188 for (uint32_t i = 0; i < timecnt; i++) {
1189 if (fread(timebuf, 1, time_size, fp) != time_size)
1190 goto free_and_cleanup;
1191 transition_times[i] = (time_size == 8) ? tzif_read_be64(timebuf) : (int32_t)tzif_read_be32(timebuf);
1192 }
1193
1194 if (timecnt && fread(transition_types, 1, timecnt, fp) != timecnt)
1195 goto free_and_cleanup;
1196
1197 // validate all transition type indices before using them
1198 for (uint32_t i = 0; i < timecnt; i++) {
1199 if (transition_types[i] >= typecnt)
1200 goto free_and_cleanup;
1201 }
1202
1203 for (uint32_t i = 0; i < typecnt; i++) {
1204 if (fread(typebuf, 1, sizeof(typebuf), fp) != sizeof(typebuf))
1205 goto free_and_cleanup;
1206
1207 types[i].gmtoff = (int32_t)tzif_read_be32(typebuf);
1208 types[i].isdst = typebuf[4];
1209 types[i].abbrind = typebuf[5];
1210 }
1211
1212 if (fread(abbrs, 1, charcnt, fp) != charcnt)
1213 goto free_and_cleanup;
1214 abbrs[charcnt] = '\0';
1215
1216 int type_index = -1;
1217 if (timecnt == 0) {
1218 type_index = tzif_default_type_index(types, typecnt);
1219 } else {
1220 for (uint32_t i = 0; i < timecnt; i++) {
1221 if ((int64_t)t < transition_times[i])
1222 break;
1223 type_index = transition_types[i];
1224 }
1225
1226 if (type_index < 0)
1227 type_index = tzif_default_type_index(types, typecnt);
1228 }
1229
1230 if (type_index < 0 || (uint32_t)type_index >= typecnt)
1231 goto free_and_cleanup;
1232
1233 if (types[type_index].abbrind >= charcnt)
1234 goto free_and_cleanup;
1235
1236 const char *tz_abbrev = &abbrs[types[type_index].abbrind];
1237 if (!*tz_abbrev)
1238 tz_abbrev = "UTC";
1239
1240 strncpyz(abbrev, tz_abbrev, abbrev_size - 1);
1241 *offset = types[type_index].gmtoff;
1242 ok = true;
1243
1244 free_and_cleanup:
1245 freez(abbrs);
1246 freez(types);
1247 freez(transition_types);
1248 freez(transition_times);
1249
1250 cleanup:
1251 fclose(fp);
1252 return ok;
1253 }
1254 #endif
1255
1256 void refresh_system_timezone(const char *timezone, bool is_tzdb_name) {
1257 time_t t;
1258 char abbrev[64];
1259 char safe_abbrev[64];
1260 const char *new_abbrev = "UTC";
1261 int32_t new_offset = 0;
1262
1263 // Update global flag when we have confirmed tzdb knowledge.
1264 // When is_tzdb_name is false, preserve the existing global flag — a prior
1265 // successful detection may have already promoted it to true.
1266 if (is_tzdb_name)
1267 timezone_is_tzdb_name = true;
1268
1269 t = now_realtime_sec();
1270 bool ok = false;
1271
1272 #if defined(HAVE_TZALLOC) && defined(HAVE_LOCALTIME_RZ) && defined(HAVE_TZFREE)
1273 if (is_tzdb_name) {
1274 timezone_t tz = tzalloc(timezone);
1275 if (tz) {
1276 struct tm *tmp, tmbuf;
1277 tmp = localtime_rz(tz, &t, &tmbuf);
1278 ok = timezone_info_from_tm(tmp, abbrev, sizeof(abbrev), &new_offset);
1279 tzfree(tz);
1280 }
1281 }
1282 #elif !defined(OS_WINDOWS)
1283 if (is_tzdb_name)
1284 ok = timezone_info_from_tzfile(timezone, t, abbrev, sizeof(abbrev), &new_offset);
1285 #endif
1286
1287 if (!ok)
1288 ok = current_process_timezone_info(t, abbrev, sizeof(abbrev), &new_offset);
1289
1290 if (ok && timezone_abbrev_normalize(safe_abbrev, sizeof(safe_abbrev), abbrev))
1291 new_abbrev = safe_abbrev;
1292
1293 // Atomically update the system timezone triplet
1294 system_tz_set(timezone, new_abbrev, new_offset);
1295
1296 // Update localhost if it exists
1297 if (localhost) {
1298 if (rrdhost_update_timezone(localhost, timezone, new_abbrev, new_offset)) {
1299 // Timezone changed — update the two labels directly, persist, and notify.
1300 if (localhost->rrdlabels) {
1301 bool labels_changed = rrdlabels_add_changed(localhost->rrdlabels, "_timezone", timezone, RRDLABEL_SRC_AUTO);
1302 labels_changed |= rrdlabels_add_changed(localhost->rrdlabels, "_abbrev_timezone", new_abbrev, RRDLABEL_SRC_AUTO);
1303 rrdhost_flag_set(localhost, RRDHOST_FLAG_METADATA_LABELS | RRDHOST_FLAG_METADATA_UPDATE);
1304 if(labels_changed)
1305 rrdhost_flag_set(localhost, RRDHOST_FLAG_PENDING_LABEL_RECHECK);
1306 stream_send_host_labels(localhost);
1307 }
1308 aclk_queue_node_info(localhost, false);
1309 }
1310 }
1311 }
1312
1313 bool analytics_check_enabled(void) {
1314 if(!netdata_anonymous_statistics_enabled)
1315 return false;
1316
1317 char filename[FILENAME_MAX + 1];
1318 snprintfz(filename, sizeof(filename), "%s/.opt-out-from-anonymous-statistics", netdata_configured_user_config_dir);
1319
1320 if(access(filename, R_OK) != 0) {
1321 // the file is not there, check the environment variable
1322 const char *s = getenv("DISABLE_TELEMETRY");
1323 netdata_anonymous_statistics_enabled = !s || !*s;
1324 }
1325 else
1326 // the file is there, disable telemetry
1327 netdata_anonymous_statistics_enabled = false;
1328
1329 return netdata_anonymous_statistics_enabled;
1330 }
1331
1332 void analytics_reset(void) {
1333 analytics_data.data_length = 0;
1334 analytics_set_data(&analytics_data.netdata_config_stream_enabled, "null");
1335 analytics_set_data(&analytics_data.netdata_config_memory_mode, "null");
1336 analytics_set_data(&analytics_data.netdata_config_exporting_enabled, "null");
1337 analytics_set_data(&analytics_data.netdata_exporting_connectors, "null");
1338 analytics_set_data(&analytics_data.netdata_allmetrics_prometheus_used, "null");
1339 analytics_set_data(&analytics_data.netdata_allmetrics_shell_used, "null");
1340 analytics_set_data(&analytics_data.netdata_allmetrics_json_used, "null");
1341 analytics_set_data(&analytics_data.netdata_dashboard_used, "null");
1342 analytics_set_data(&analytics_data.netdata_collectors, "null");
1343 analytics_set_data(&analytics_data.netdata_collectors_count, "null");
1344 analytics_set_data(&analytics_data.netdata_buildinfo, "null");
1345 analytics_set_data(&analytics_data.netdata_config_page_cache_size, "null");
1346 analytics_set_data(&analytics_data.netdata_config_multidb_disk_quota, "null");
1347 analytics_set_data(&analytics_data.netdata_config_https_enabled, "null");
1348 analytics_set_data(&analytics_data.netdata_config_web_enabled, "null");
1349 analytics_set_data(&analytics_data.netdata_config_release_channel, "null");
1350 analytics_set_data(&analytics_data.netdata_mirrored_host_count, "null");
1351 analytics_set_data(&analytics_data.netdata_mirrored_hosts_reachable, "null");
1352 analytics_set_data(&analytics_data.netdata_mirrored_hosts_unreachable, "null");
1353 analytics_set_data(&analytics_data.netdata_notification_methods, "null");
1354 analytics_set_data(&analytics_data.netdata_alarms_normal, "null");
1355 analytics_set_data(&analytics_data.netdata_alarms_warning, "null");
1356 analytics_set_data(&analytics_data.netdata_alarms_critical, "null");
1357 analytics_set_data(&analytics_data.netdata_charts_count, "null");
1358 analytics_set_data(&analytics_data.netdata_metrics_count, "null");
1359 analytics_set_data(&analytics_data.netdata_config_is_parent, "null");
1360 analytics_set_data(&analytics_data.netdata_config_hosts_available, "null");
1361 analytics_set_data(&analytics_data.netdata_host_cloud_available, "null");
1362 analytics_set_data(&analytics_data.netdata_host_aclk_implementation, "null");
1363 analytics_set_data(&analytics_data.netdata_host_aclk_available, "null");
1364 analytics_set_data(&analytics_data.netdata_host_aclk_protocol, "null");
1365 analytics_set_data(&analytics_data.netdata_host_agent_claimed, "null");
1366 analytics_set_data(&analytics_data.netdata_host_cloud_enabled, "null");
1367 analytics_set_data(&analytics_data.netdata_config_https_available, "null");
1368 analytics_set_data(&analytics_data.netdata_install_type, "null");
1369 analytics_set_data(&analytics_data.netdata_config_is_private_registry, "null");
1370 analytics_set_data(&analytics_data.netdata_config_use_private_registry, "null");
1371 analytics_set_data(&analytics_data.netdata_config_oom_score, "null");
1372 analytics_set_data(&analytics_data.netdata_prebuilt_distro, "null");
1373 analytics_set_data(&analytics_data.netdata_fail_reason, "null");
1374
1375 analytics_data.prometheus_hits = 0;
1376 analytics_data.shell_hits = 0;
1377 analytics_data.json_hits = 0;
1378 analytics_data.dashboard_hits = 0;
1379 analytics_data.charts_count = 0;
1380 analytics_data.metrics_count = 0;
1381 analytics_data.exporting_enabled = false;
1382 }
1383
1384 void analytics_init(void)
1385 {
1386 spinlock_init(&analytics_data.spinlock);
1387 }