Compute timestamps needed for status file (#21272)
Compute timestamps needed for status file -- avoid signal async unsafe calls
Stelios Fragkakis committed
Nov 7, 2025 at 09:09 UTC
3d39146e569235a035fda3107fc2c55ce46ba0bb
4 files changed
+34
-6
src/daemon/machine-guid.c
+3
@@ -99,6 +99,7 @@ static bool machine_guid_read_from_file(const char *filename, ND_MACHINE_GUID *h
99
100
// Update last modified timestamp.
101
h.last_modified_ut = STAT_GET_MTIME_SEC(st) * USEC_PER_SEC + STAT_GET_MTIME_NSEC(st) / 1000;
102
+ rfc3339_datetime_ut(h.last_modified_ut_rfc3339, sizeof(h.last_modified_ut_rfc3339), h.last_modified_ut, 2, true);
103
*host_id = h;
104
105
nd_log(NDLS_DAEMON, NDLP_INFO, "MACHINE_GUID: GUID read from file '%s'", filename);
@@ -125,6 +126,7 @@ static bool machine_guid_write_to_file(const char *filename, ND_MACHINE_GUID *ho
126
if (host_id) {
127
h.uuid = host_id->uuid;
128
h.last_modified_ut = host_id->last_modified_ut;
129
+ safecpy(h.last_modified_ut_rfc3339, host_id->last_modified_ut_rfc3339);
130
}
131
132
// Create the text representation before writing.
@@ -204,6 +206,7 @@ static ND_MACHINE_GUID machine_guid_get_or_create(void) {
206
nd_log(NDLS_DAEMON, NDLP_INFO, "MACHINE_GUID: got previous GUID from daemon status file");
207
208
h.last_modified_ut = now_realtime_usec();
209
+ rfc3339_datetime_ut(h.last_modified_ut_rfc3339, sizeof(h.last_modified_ut_rfc3339), h.last_modified_ut, 2, true);
210
nd_machine_guid = h;
211
212
// Ensure the registry directory exists.
src/daemon/machine-guid.h
+1
@@ -9,6 +9,7 @@ typedef struct nd_machine_guid {
9
char txt[UUID_STR_LEN];
10
ND_UUID uuid;
11
usec_t last_modified_ut;
12
+ char last_modified_ut_rfc3339[RFC3339_MAX_LENGTH]; // pre-calculated RFC3339 for async-signal-safe use
13
} ND_MACHINE_GUID;
14
15
ND_MACHINE_GUID *machine_guid_get(void);
src/daemon/status-file.c
+28
-6
@@ -100,7 +100,7 @@ static void daemon_status_file_to_json(BUFFER *wb, DAEMON_STATUS_FILE *ds) {
100
101
dsf_acquire(*ds);
102
103
- buffer_json_member_add_datetime_rfc3339(wb, "@timestamp", ds->timestamp_ut, true);
103
+ buffer_json_member_add_string(wb, "@timestamp", ds->timestamp_ut_rfc3339);
104
buffer_json_member_add_uint64(wb, "version", STATUS_FILE_VERSION);
105
106
buffer_json_member_add_object(wb, "agent");
@@ -108,7 +108,7 @@ static void daemon_status_file_to_json(BUFFER *wb, DAEMON_STATUS_FILE *ds) {
108
buffer_json_member_add_uuid(wb, "id", ds->host_id.uuid.uuid);
109
110
if(ds->v >= 24 && ds->host_id.last_modified_ut)
111
- buffer_json_member_add_datetime_rfc3339(wb, "since", ds->host_id.last_modified_ut, true);
111
+ buffer_json_member_add_string(wb, "since", ds->host_id.last_modified_ut_rfc3339);
112
113
buffer_json_member_add_uuid_compact(wb, "ephemeral_id", ds->invocation.uuid);
114
buffer_json_member_add_string(wb, "version", ds->version);
@@ -246,7 +246,7 @@ static void daemon_status_file_to_json(BUFFER *wb, DAEMON_STATUS_FILE *ds) {
246
buffer_json_member_add_uint64(wb, "dbengine", ds->disk_footprint.dbengine);
247
buffer_json_member_add_uint64(wb, "sqlite", ds->disk_footprint.sqlite);
248
buffer_json_member_add_uint64(wb, "other", ds->disk_footprint.other);
249
- buffer_json_member_add_datetime_rfc3339(wb, "last_updated", ds->disk_footprint.last_updated_ut, true);
249
+ buffer_json_member_add_string(wb, "last_updated", ds->disk_footprint.last_updated_ut_rfc3339);
250
buffer_json_object_close(wb);
251
}
252
buffer_json_object_close(wb);
@@ -748,6 +748,8 @@ static void daemon_status_file_refresh(DAEMON_STATUS status) {
748
session_status.boottime = now_boottime_sec();
749
session_status.uptime = now_realtime_sec() - netdata_start_time;
750
session_status.timestamp_ut = now_ut;
751
+ rfc3339_datetime_ut(session_status.timestamp_ut_rfc3339, sizeof(session_status.timestamp_ut_rfc3339),
752
+ session_status.timestamp_ut, 2, true);
753
session_status.invocation = nd_log_get_invocation_id();
754
session_status.db_mode = default_rrd_memory_mode;
755
session_status.db_tiers = nd_profile.storage_tiers;
@@ -829,10 +831,13 @@ static void daemon_status_file_refresh(DAEMON_STATUS status) {
831
832
// Calculate other files (total - dbengine - sqlite)
833
session_status.disk_footprint.other = total_size.bytes - dbengine_size.bytes - sqlite_size.bytes;
832
-
834
+
835
// Update last updated timestamp
836
session_status.disk_footprint.last_updated_ut = now_ut;
835
-
837
+ rfc3339_datetime_ut(session_status.disk_footprint.last_updated_ut_rfc3339,
838
+ sizeof(session_status.disk_footprint.last_updated_ut_rfc3339),
839
+ session_status.disk_footprint.last_updated_ut, 2, true);
840
+
841
// Clean up patterns
842
simple_pattern_free(dbengine_pattern);
843
simple_pattern_free(sqlite_pattern);
@@ -1062,10 +1067,27 @@ void daemon_status_file_init(void) {
1067
1068
if(last_session_status.v <= 26)
1069
fill_dmi_info(&last_session_status);
1065
-
1070
+
1071
if(last_session_status.v <= 27)
1072
last_session_status.system_cpus = os_get_system_cpus();
1073
1074
+ // Regenerate RFC3339 strings from loaded timestamps for async-signal-safe compatibility
1075
+ // (these fields don't exist in saved JSON, they're runtime-only for signal handlers)
1076
+ if(last_session_status.timestamp_ut)
1077
+ rfc3339_datetime_ut(last_session_status.timestamp_ut_rfc3339,
1078
+ sizeof(last_session_status.timestamp_ut_rfc3339),
1079
+ last_session_status.timestamp_ut, 2, true);
1080
+
1081
+ if(last_session_status.host_id.last_modified_ut)
1082
+ rfc3339_datetime_ut(last_session_status.host_id.last_modified_ut_rfc3339,
1083
+ sizeof(last_session_status.host_id.last_modified_ut_rfc3339),
1084
+ last_session_status.host_id.last_modified_ut, 2, true);
1085
+
1086
+ if(last_session_status.disk_footprint.last_updated_ut)
1087
+ rfc3339_datetime_ut(last_session_status.disk_footprint.last_updated_ut_rfc3339,
1088
+ sizeof(last_session_status.disk_footprint.last_updated_ut_rfc3339),
1089
+ last_session_status.disk_footprint.last_updated_ut, 2, true);
1090
+
1091
daemon_status_file_migrate_once();
1092
}
1093
src/daemon/status-file.h
+2
@@ -48,6 +48,7 @@ typedef struct daemon_status_file {
48
time_t boottime; // system boottime
49
time_t uptime; // netdata uptime
50
usec_t timestamp_ut; // the timestamp of the status file
51
+ char timestamp_ut_rfc3339[RFC3339_MAX_LENGTH]; // pre-calculated RFC3339 for async-signal-safe use
52
size_t restarts; // the number of times this agent has restarted (ever)
53
size_t crashes; // the number of times this agent has crashed (ever)
54
size_t posts; // the number of posts to the backend
@@ -79,6 +80,7 @@ typedef struct daemon_status_file {
80
uint64_t sqlite; // Size of sqlite files
81
uint64_t other; // Size of other files (total - dbengine - sqlite)
82
usec_t last_updated_ut; // Last time the footprint was updated (microseconds)
83
+ char last_updated_ut_rfc3339[RFC3339_MAX_LENGTH]; // pre-calculated RFC3339 for async-signal-safe use
84
} disk_footprint;
85
86
// Metrics statistics