@cryptotaxi247 / netdata-1 / commits / 3d39146e5

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