| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | |
| 3 | #include "database/rrd.h" |
| 4 | |
| 5 | #ifdef OS_MACOS |
| 6 | #include <AvailabilityMacros.h> |
| 7 | #endif |
| 8 | |
| 9 | #if !defined(OS_MACOS) || (MAC_OS_X_VERSION_MIN_REQUIRED >= 101300) |
| 10 | |
| 11 | #define PLUGIN_TIMEX_NAME "timex.plugin" |
| 12 | |
| 13 | #define CONFIG_SECTION_TIMEX "plugin:timex" |
| 14 | |
| 15 | struct status_codes { |
| 16 | char *name; |
| 17 | int code; |
| 18 | RRDDIM *rd; |
| 19 | } sta_codes[] = { |
| 20 | // {"pll", STA_PLL, NULL}, |
| 21 | // {"ppsfreq", STA_PPSFREQ, NULL}, |
| 22 | // {"ppstime", STA_PPSTIME, NULL}, |
| 23 | // {"fll", STA_FLL, NULL}, |
| 24 | // {"ins", STA_INS, NULL}, |
| 25 | // {"del", STA_DEL, NULL}, |
| 26 | {"unsync", STA_UNSYNC, NULL}, |
| 27 | // {"freqhold", STA_FREQHOLD, NULL}, |
| 28 | // {"ppssignal", STA_PPSSIGNAL, NULL}, |
| 29 | // {"ppsjitter", STA_PPSJITTER, NULL}, |
| 30 | // {"ppswander", STA_PPSWANDER, NULL}, |
| 31 | // {"ppserror", STA_PPSERROR, NULL}, |
| 32 | {"clockerr", STA_CLOCKERR, NULL}, |
| 33 | // {"nano", STA_NANO, NULL}, |
| 34 | // {"clk", STA_CLK, NULL}, |
| 35 | {NULL, 0, NULL}, |
| 36 | }; |
| 37 | |
| 38 | static void timex_main_cleanup(void *pptr) |
| 39 | { |
| 40 | struct netdata_static_thread *static_thread = CLEANUP_FUNCTION_GET_PTR(pptr); |
| 41 | if(!static_thread) return; |
| 42 | |
| 43 | static_thread->enabled = NETDATA_MAIN_THREAD_EXITING; |
| 44 | |
| 45 | worker_unregister(); |
| 46 | |
| 47 | static_thread->enabled = NETDATA_MAIN_THREAD_EXITED; |
| 48 | } |
| 49 | |
| 50 | void timex_main(void *ptr) |
| 51 | { |
| 52 | CLEANUP_FUNCTION_REGISTER(timex_main_cleanup) cleanup_ptr = ptr; |
| 53 | |
| 54 | worker_register("TIMEX"); |
| 55 | worker_register_job_name(0, "clock check"); |
| 56 | |
| 57 | int update_every = (int)inicfg_get_duration_seconds(&netdata_config, CONFIG_SECTION_TIMEX, "update every", 10); |
| 58 | if (update_every < localhost->rrd_update_every) { |
| 59 | update_every = localhost->rrd_update_every; |
| 60 | inicfg_set_duration_seconds(&netdata_config, CONFIG_SECTION_TIMEX, "update every", update_every); |
| 61 | } |
| 62 | |
| 63 | int do_sync = inicfg_get_boolean(&netdata_config, CONFIG_SECTION_TIMEX, "clock synchronization state", CONFIG_BOOLEAN_YES); |
| 64 | int do_offset = inicfg_get_boolean(&netdata_config, CONFIG_SECTION_TIMEX, "time offset", CONFIG_BOOLEAN_YES); |
| 65 | |
| 66 | if (unlikely(do_sync == CONFIG_BOOLEAN_NO && do_offset == CONFIG_BOOLEAN_NO)) { |
| 67 | netdata_log_info("No charts to show"); |
| 68 | return; |
| 69 | } |
| 70 | |
| 71 | usec_t step = update_every * USEC_PER_SEC; |
| 72 | usec_t real_step = USEC_PER_SEC; |
| 73 | heartbeat_t hb; |
| 74 | heartbeat_init(&hb, USEC_PER_SEC); |
| 75 | while (service_running(SERVICE_COLLECTORS)) { |
| 76 | worker_is_idle(); |
| 77 | heartbeat_next(&hb); |
| 78 | |
| 79 | if (real_step < step) { |
| 80 | real_step += USEC_PER_SEC; |
| 81 | continue; |
| 82 | } |
| 83 | real_step = USEC_PER_SEC; |
| 84 | |
| 85 | worker_is_busy(0); |
| 86 | |
| 87 | struct timex timex_buf = {}; |
| 88 | int sync_state = 0; |
| 89 | static int prev_sync_state = 0; |
| 90 | |
| 91 | sync_state = os_adjtimex(&timex_buf); |
| 92 | |
| 93 | int non_seq_failure = (sync_state == -1 && prev_sync_state != -1); |
| 94 | prev_sync_state = sync_state; |
| 95 | |
| 96 | if (non_seq_failure) { |
| 97 | netdata_log_error("Cannot get clock synchronization state"); |
| 98 | continue; |
| 99 | } |
| 100 | |
| 101 | collected_number divisor = USEC_PER_MS; |
| 102 | if (timex_buf.status & STA_NANO) |
| 103 | divisor = NSEC_PER_MSEC; |
| 104 | |
| 105 | // ---------------------------------------------------------------- |
| 106 | |
| 107 | if (do_sync) { |
| 108 | static RRDSET *st_sync_state = NULL; |
| 109 | static RRDDIM *rd_sync_state; |
| 110 | |
| 111 | if (unlikely(!st_sync_state)) { |
| 112 | st_sync_state = rrdset_create_localhost( |
| 113 | "system", |
| 114 | "clock_sync_state", |
| 115 | NULL, |
| 116 | "clock synchronization", |
| 117 | NULL, |
| 118 | "System Clock Synchronization State", |
| 119 | "state", |
| 120 | PLUGIN_TIMEX_NAME, |
| 121 | NULL, |
| 122 | NETDATA_CHART_PRIO_CLOCK_SYNC_STATE, |
| 123 | update_every, |
| 124 | RRDSET_TYPE_LINE); |
| 125 | |
| 126 | rd_sync_state = rrddim_add(st_sync_state, "state", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); |
| 127 | } |
| 128 | |
| 129 | rrddim_set_by_pointer(st_sync_state, rd_sync_state, sync_state != TIME_ERROR ? 1 : 0); |
| 130 | rrdset_done(st_sync_state); |
| 131 | |
| 132 | static RRDSET *st_clock_status = NULL; |
| 133 | |
| 134 | if (unlikely(!st_clock_status)) { |
| 135 | st_clock_status = rrdset_create_localhost( |
| 136 | "system", |
| 137 | "clock_status", |
| 138 | NULL, |
| 139 | "clock synchronization", |
| 140 | NULL, |
| 141 | "System Clock Status", |
| 142 | "status", |
| 143 | PLUGIN_TIMEX_NAME, |
| 144 | NULL, |
| 145 | NETDATA_CHART_PRIO_CLOCK_STATUS, |
| 146 | update_every, |
| 147 | RRDSET_TYPE_LINE); |
| 148 | |
| 149 | for (int i = 0; sta_codes[i].name != NULL; i++) { |
| 150 | sta_codes[i].rd = |
| 151 | rrddim_add(st_clock_status, sta_codes[i].name, NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); |
| 152 | } |
| 153 | } |
| 154 | |
| 155 | for (int i = 0; sta_codes[i].name != NULL; i++) |
| 156 | rrddim_set_by_pointer(st_clock_status, sta_codes[i].rd, timex_buf.status & sta_codes[i].code ? 1 : 0); |
| 157 | |
| 158 | rrdset_done(st_clock_status); |
| 159 | } |
| 160 | |
| 161 | if (do_offset) { |
| 162 | static RRDSET *st_offset = NULL; |
| 163 | static RRDDIM *rd_offset; |
| 164 | |
| 165 | if (unlikely(!st_offset)) { |
| 166 | st_offset = rrdset_create_localhost( |
| 167 | "system", |
| 168 | "clock_sync_offset", |
| 169 | NULL, |
| 170 | "clock synchronization", |
| 171 | NULL, |
| 172 | "Computed Time Offset Between Local System and Reference Clock", |
| 173 | "milliseconds", |
| 174 | PLUGIN_TIMEX_NAME, |
| 175 | NULL, |
| 176 | NETDATA_CHART_PRIO_CLOCK_SYNC_OFFSET, |
| 177 | update_every, |
| 178 | RRDSET_TYPE_LINE); |
| 179 | |
| 180 | rd_offset = rrddim_add(st_offset, "offset", NULL, 1, divisor, RRD_ALGORITHM_ABSOLUTE); |
| 181 | } |
| 182 | |
| 183 | rrddim_set_by_pointer(st_offset, rd_offset, timex_buf.offset); |
| 184 | rrdset_done(st_offset); |
| 185 | } |
| 186 | } |
| 187 | } |
| 188 | |
| 189 | #endif // !defined(OS_MACOS) || (MAC_OS_X_VERSION_MIN_REQUIRED >= 101300) |