| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | |
| 3 | #define PULSE_INTERNALS 1 |
| 4 | #include "pulse-daemon.h" |
| 5 | |
| 6 | static void pulse_daemon_cpu_usage_do(bool extended __maybe_unused) { |
| 7 | struct rusage me; |
| 8 | getrusage(RUSAGE_SELF, &me); |
| 9 | |
| 10 | { |
| 11 | static RRDSET *st_cpu = NULL; |
| 12 | static RRDDIM *rd_cpu_user = NULL, |
| 13 | *rd_cpu_system = NULL; |
| 14 | |
| 15 | if (unlikely(!st_cpu)) { |
| 16 | st_cpu = rrdset_create_localhost( |
| 17 | "netdata" |
| 18 | , "server_cpu" |
| 19 | , NULL |
| 20 | , "CPU usage" |
| 21 | , NULL |
| 22 | , "Netdata CPU usage" |
| 23 | , "milliseconds/s" |
| 24 | , "netdata" |
| 25 | , "pulse" |
| 26 | , 130000 |
| 27 | , localhost->rrd_update_every |
| 28 | , RRDSET_TYPE_STACKED |
| 29 | ); |
| 30 | |
| 31 | rd_cpu_user = rrddim_add(st_cpu, "user", NULL, 1, 1000, RRD_ALGORITHM_INCREMENTAL); |
| 32 | rd_cpu_system = rrddim_add(st_cpu, "system", NULL, 1, 1000, RRD_ALGORITHM_INCREMENTAL); |
| 33 | } |
| 34 | |
| 35 | rrddim_set_by_pointer(st_cpu, rd_cpu_user, (collected_number )(me.ru_utime.tv_sec * 1000000ULL + me.ru_utime.tv_usec)); |
| 36 | rrddim_set_by_pointer(st_cpu, rd_cpu_system, (collected_number )(me.ru_stime.tv_sec * 1000000ULL + me.ru_stime.tv_usec)); |
| 37 | rrdset_done(st_cpu); |
| 38 | } |
| 39 | } |
| 40 | |
| 41 | static void pulse_daemon_uptime_do(bool extended __maybe_unused) { |
| 42 | { |
| 43 | static time_t netdata_boottime_time = 0; |
| 44 | if (!netdata_boottime_time) |
| 45 | netdata_boottime_time = now_boottime_sec(); |
| 46 | |
| 47 | time_t netdata_uptime = now_boottime_sec() - netdata_boottime_time; |
| 48 | |
| 49 | static RRDSET *st_uptime = NULL; |
| 50 | static RRDDIM *rd_uptime = NULL; |
| 51 | |
| 52 | if (unlikely(!st_uptime)) { |
| 53 | st_uptime = rrdset_create_localhost( |
| 54 | "netdata", |
| 55 | "uptime", |
| 56 | NULL, |
| 57 | "Uptime", |
| 58 | NULL, |
| 59 | "Netdata uptime", |
| 60 | "seconds", |
| 61 | "netdata", |
| 62 | "pulse", |
| 63 | 130150, |
| 64 | localhost->rrd_update_every, |
| 65 | RRDSET_TYPE_LINE); |
| 66 | |
| 67 | rd_uptime = rrddim_add(st_uptime, "uptime", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); |
| 68 | } |
| 69 | |
| 70 | rrddim_set_by_pointer(st_uptime, rd_uptime, netdata_uptime); |
| 71 | rrdset_done(st_uptime); |
| 72 | } |
| 73 | } |
| 74 | |
| 75 | // Called from a single pulse daemon thread, so last_refresh_ut needs no synchronization. |
| 76 | static void pulse_daemon_timezone_do(bool extended __maybe_unused) { |
| 77 | static usec_t last_refresh_ut = 0; |
| 78 | |
| 79 | usec_t now_ut = now_monotonic_usec(); |
| 80 | |
| 81 | // refresh every 30 minutes to pick up DST changes |
| 82 | if (now_ut - last_refresh_ut >= 30 * 60 * USEC_PER_SEC) { |
| 83 | if (system_timezone_is_user_configured()) { |
| 84 | // User explicitly configured a timezone in netdata.conf — respect it, |
| 85 | // just refresh the abbreviation and offset (handles DST transitions). |
| 86 | static bool mismatch_logged = false; |
| 87 | if (!mismatch_logged) { |
| 88 | mismatch_logged = true; |
| 89 | char sys_buf[FILENAME_MAX + 1]; |
| 90 | const char *sys_tz = detect_system_timezone_name(sys_buf, sizeof(sys_buf)); |
| 91 | SYSTEM_TZ tz = system_tz_get(); |
| 92 | if (sys_tz && tz.timezone && strcmp(sys_tz, tz.timezone) != 0) |
| 93 | nd_log(NDLS_DAEMON, NDLP_NOTICE, |
| 94 | "TIMEZONE: configured '%s' differs from system '%s'", |
| 95 | tz.timezone, sys_tz); |
| 96 | system_tz_free(&tz); |
| 97 | } |
| 98 | SYSTEM_TZ tz = system_tz_get(); |
| 99 | refresh_system_timezone(tz.timezone, true); |
| 100 | system_tz_free(&tz); |
| 101 | } else { |
| 102 | // Auto-detected timezone — re-detect to pick up system changes. |
| 103 | char buf[FILENAME_MAX + 1]; |
| 104 | const char *detected = detect_system_timezone_name(buf, sizeof(buf)); |
| 105 | if (detected && timezone_name_is_safe_tzdb_path(detected)) { |
| 106 | refresh_system_timezone(detected, true); |
| 107 | } else { |
| 108 | if (detected) |
| 109 | // Detected a timezone that failed the safe-path check; skip it. |
| 110 | netdata_log_error("TIMEZONE: detected unsafe timezone name '%s', ignoring", detected); |
| 111 | |
| 112 | // Detection failed or was rejected — re-use the stored name with its original tzdb flag. |
| 113 | // Validate the stored name before reusing it: a previously persisted unsafe tzdb path |
| 114 | // must not reach refresh_system_timezone() and propagate to labels or ACLK. |
| 115 | SYSTEM_TZ tz = system_tz_get(); |
| 116 | bool is_tzdb = system_timezone_is_tzdb_name(); |
| 117 | if (!is_tzdb || timezone_name_is_safe_tzdb_path(tz.timezone)) |
| 118 | refresh_system_timezone(tz.timezone, is_tzdb); |
| 119 | else |
| 120 | netdata_log_error("TIMEZONE: stored unsafe tzdb timezone name '%s', ignoring", tz.timezone ? tz.timezone : "(null)"); |
| 121 | system_tz_free(&tz); |
| 122 | } |
| 123 | } |
| 124 | last_refresh_ut = now_ut; |
| 125 | } |
| 126 | } |
| 127 | |
| 128 | void pulse_daemon_do(bool extended) { |
| 129 | pulse_daemon_cpu_usage_do(extended); |
| 130 | pulse_daemon_uptime_do(extended); |
| 131 | pulse_daemon_memory_do(extended); |
| 132 | pulse_daemon_timezone_do(extended); |
| 133 | } |