more strict checks on log-fw (#19898)
* more strict checks on log-fw * fix coverity issue 410100 * fix coverity issue 456835 * fix coverity issue 457460 * fix coverity issue 457483 * fix coverity issue 457744 * fix coverity issue 457745 * fix coverity issue 456266 * fix coverity issue 457492 * do not free lf if the thread did not join * make sure log_forwarder_stop() is called just once * log thread pointers * do not destroy the main spawn server unless necessary * fix windows exit * If poll is interrupted by signal don't sleep * fix warnings * fix compilation on FreeBSD --------- Co-authored-by: Stelios Fragkakis <52996999+stelfrag@users.noreply.github.com> Co-authored-by: ilyam8 <ilya@netdata.cloud>
Costa Tsaousis committed
Mar 18, 2025 at 23:03 UTC
2198965ed5441f4ab3f0faae1967fd91594dd369
17 files changed
+110
-68
src/collectors/freebsd.plugin/plugin_freebsd.c
+1
-1
@@ -91,7 +91,7 @@ void *freebsd_main(void *ptr)
91
92
// initialize FreeBSD plugin
93
if (freebsd_plugin_init())
94
- netdata_exit_fatal(EXIT_REASON_FATAL);
94
+ netdata_exit_fatal();
95
96
// check the enabled status for each module
97
int i;
src/daemon/commands.c
+1
-1
@@ -164,7 +164,7 @@ static cmd_status_t cmd_exit_execute(char *args, char **message)
164
165
nd_log_limits_unlimited();
166
netdata_log_info("COMMAND: Cleaning up to exit.");
167
- netdata_exit_gracefully(EXIT_REASON_CMD_EXIT);
167
+ netdata_exit_gracefully(EXIT_REASON_CMD_EXIT, true);
168
return CMD_STATUS_SUCCESS;
169
}
170
src/daemon/daemon-shutdown-watcher.c
-3
@@ -109,7 +109,6 @@ void *watcher_main(void *arg)
109
110
usec_t shutdown_start_time = now_monotonic_usec();
111
112
- watcher_wait_for_step(WATCHER_STEP_ID_DESTROY_MAIN_SPAWN_SERVER, shutdown_start_time);
112
watcher_wait_for_step(WATCHER_STEP_ID_CLOSE_WEBRTC_CONNECTIONS, shutdown_start_time);
113
watcher_wait_for_step(WATCHER_STEP_ID_DISABLE_MAINTENANCE_NEW_QUERIES_NEW_WEB_REQUESTS_NEW_STREAMING_CONNECTIONS_AND_ACLK, shutdown_start_time);
114
watcher_wait_for_step(WATCHER_STEP_ID_STOP_MAINTENANCE_THREAD, shutdown_start_time);
@@ -144,8 +143,6 @@ void *watcher_main(void *arg)
143
void watcher_thread_start() {
144
watcher_steps = callocz(WATCHER_STEP_ID_MAX, sizeof(watcher_step_t));
145
147
- watcher_steps[WATCHER_STEP_ID_DESTROY_MAIN_SPAWN_SERVER].msg =
148
- "destroy main spawn server";
146
watcher_steps[WATCHER_STEP_ID_CLOSE_WEBRTC_CONNECTIONS].msg =
147
"close webrtc connections";
148
watcher_steps[WATCHER_STEP_ID_DISABLE_MAINTENANCE_NEW_QUERIES_NEW_WEB_REQUESTS_NEW_STREAMING_CONNECTIONS_AND_ACLK].msg =
src/daemon/daemon-shutdown-watcher.h
-1
@@ -6,7 +6,6 @@
6
#include "libnetdata/libnetdata.h"
7
8
typedef enum {
9
- WATCHER_STEP_ID_DESTROY_MAIN_SPAWN_SERVER,
9
WATCHER_STEP_ID_CLOSE_WEBRTC_CONNECTIONS,
10
WATCHER_STEP_ID_DISABLE_MAINTENANCE_NEW_QUERIES_NEW_WEB_REQUESTS_NEW_STREAMING_CONNECTIONS_AND_ACLK,
11
WATCHER_STEP_ID_STOP_MAINTENANCE_THREAD,
src/daemon/daemon-shutdown.c
+11
-12
@@ -171,8 +171,7 @@ static void rrdeng_flush_everything_and_wait(bool wait_flush, bool wait_collecto
171
}
172
#endif
173
174
-ND_EXIT_NORETURN
175
-static void netdata_cleanup_and_exit(EXIT_REASON reason, bool abnormal) {
174
+static void netdata_cleanup_and_exit(EXIT_REASON reason, bool abnormal, bool exit_when_done) {
175
exit_initiated_set(reason);
176
177
// don't recurse (due to a fatal, while exiting)
@@ -201,9 +200,6 @@ static void netdata_cleanup_and_exit(EXIT_REASON reason, bool abnormal) {
200
//analytics_statistic_t statistic = (analytics_statistic_t) {"EXIT", abnormal?"ERROR":"OK","-"};
201
//analytics_statistic_send(&statistic);
202
204
- netdata_main_spawn_server_cleanup();
205
- watcher_step_complete(WATCHER_STEP_ID_DESTROY_MAIN_SPAWN_SERVER);
206
-
203
webrtc_close_all_connections();
204
watcher_step_complete(WATCHER_STEP_ID_CLOSE_WEBRTC_CONNECTIONS);
205
@@ -330,6 +326,9 @@ static void netdata_cleanup_and_exit(EXIT_REASON reason, bool abnormal) {
326
#if defined(FSANITIZE_ADDRESS)
327
fprintf(stderr, "\n");
328
329
+ fprintf(stderr, "Stopping spawn server...\n");
330
+ netdata_main_spawn_server_cleanup();
331
+
332
fprintf(stderr, "Freeing all RRDHOSTs...\n");
333
rrdhost_free_all();
334
@@ -367,10 +366,10 @@ static void netdata_cleanup_and_exit(EXIT_REASON reason, bool abnormal) {
366
fprintf(stderr, "All done, exiting...\n");
367
#endif
368
370
-#ifdef OS_WINDOWS
371
- curl_global_cleanup();
372
- return;
373
-#endif
369
+ if(!exit_when_done) {
370
+ curl_global_cleanup();
371
+ return;
372
+ }
373
374
#ifdef ENABLE_SENTRY
375
if(abnormal)
@@ -389,14 +388,14 @@ static void netdata_cleanup_and_exit(EXIT_REASON reason, bool abnormal) {
388
#endif
389
}
390
392
-void netdata_exit_gracefully(EXIT_REASON reason) {
391
+void netdata_exit_gracefully(EXIT_REASON reason, bool exit_when_done) {
392
exit_initiated_add(reason);
393
FUNCTION_RUN_ONCE();
395
- netdata_cleanup_and_exit(reason, false);
394
+ netdata_cleanup_and_exit(reason, false, exit_when_done);
395
}
396
397
// the final callback for the fatal() function
398
void netdata_exit_fatal(void) {
400
- netdata_cleanup_and_exit(EXIT_REASON_FATAL, true);
399
+ netdata_cleanup_and_exit(EXIT_REASON_FATAL, true, true);
400
exit(1);
401
}
src/daemon/daemon-shutdown.h
+1
-1
@@ -16,7 +16,7 @@ void abort_on_fatal_enable(void);
16
#define ND_EXIT_NORETURN
17
#endif
18
19
-void netdata_exit_gracefully(EXIT_REASON reason);
19
+void netdata_exit_gracefully(EXIT_REASON reason, bool exit_when_done);
20
void netdata_exit_fatal(void);
21
22
#endif //NETDATA_DAEMON_SHUTDOWN_H
src/daemon/daemon-status-file.c
+3
-4
@@ -678,7 +678,7 @@ static bool load_status_file(const char *filename, DAEMON_STATUS_FILE *status) {
678
679
// Read the file
680
buffer_need_bytes(wb, file_size + 1);
681
- size_t read_bytes = fread(wb->buffer, 1, file_size, fp);
681
+ ssize_t read_bytes = fread(wb->buffer, 1, file_size, fp);
682
fclose(fp);
683
684
if (read_bytes == 0)
@@ -771,13 +771,12 @@ static bool save_status_file(const char *directory, const char *content, size_t
771
return false;
772
773
/* Write content to file using write() */
774
- ssize_t bytes_written = 0;
774
size_t total_written = 0;
775
776
while (total_written < content_size) {
778
- bytes_written = write(fd, content + total_written, content_size - total_written);
777
+ ssize_t bytes_written = write(fd, content + total_written, content_size - total_written);
778
780
- if (bytes_written == -1) {
779
+ if (bytes_written <= 0) {
780
if (errno == EINTR)
781
continue; /* Retry if interrupted by signal */
782
src/daemon/daemon-systemd-watcher.c
+1
-1
@@ -28,7 +28,7 @@ static int shutdown_event_handler(sd_bus_message *m, void *userdata __maybe_unus
28
shutdown ? "true" : "false");
29
30
if(shutdown)
31
- netdata_exit_gracefully(EXIT_REASON_SYSTEM_SHUTDOWN);
31
+ netdata_exit_gracefully(EXIT_REASON_SYSTEM_SHUTDOWN, true);
32
33
return 0;
34
}
src/daemon/signal-handler.c
+15
-14
@@ -82,21 +82,21 @@ void nd_signal_handler(int signo, siginfo_t *info, void *context __maybe_unused)
82
83
// log it
84
char b[1024];
85
- strncpyz(b, "SIGNAL HANDLER: received deadly signal: ", sizeof(b) - 1);
86
- strcat(b, signals_waiting[i].name);
85
+ size_t len = 0;
86
+ len = strcatz(b, len, sizeof(b), "SIGNAL HANDLER: received deadly signal: ");
87
+ len = strcatz(b, len, sizeof(b), signals_waiting[i].name);
88
if(sc) {
89
char buf[128];
90
SIGNAL_CODE_2str_h(sc, buf, sizeof(buf));
90
-
91
- strcat(b, " (");
92
- strcat(b, buf);
93
- strcat(b, ")");
91
+ len = strcatz(b, len, sizeof(b), " (");
92
+ len = strcatz(b, len, sizeof(b), buf);
93
+ len = strcatz(b, len, sizeof(b), ")");
94
}
95
- strcat(b, " in thread ");
96
- print_uint64(&b[strlen(b)], gettid_cached());
97
- strcat(b, " ");
98
- strcat(b, nd_thread_tag_async_safe());
99
- strcat(b, "!\n");
95
+ len = strcatz(b, len, sizeof(b), " in thread ");
96
+ print_uint64(&b[len], gettid_cached());
97
+ len = strcatz(b, len, sizeof(b), " ");
98
+ len = strcatz(b, len, sizeof(b), nd_thread_tag_async_safe());
99
+ len = strcatz(b, len, sizeof(b), "!\n");
100
101
if(write(STDERR_FILENO, b, strlen(b)) == -1) {
102
// nothing to do - we cannot write but there is no way to complain about it
@@ -121,7 +121,7 @@ void nd_signal_handler(int signo, siginfo_t *info, void *context __maybe_unused)
121
sa.sa_handler = SIG_DFL;
122
sigemptyset(&sa.sa_mask);
123
sa.sa_flags = 0;
124
- sigaction(signo, &sa, NULL);
124
+ if(sigaction(signo, &sa, NULL) < 0) { ; }
125
126
// Re-raise the signal, which now uses the default action.
127
raise(signo);
@@ -237,7 +237,7 @@ static void process_triggered_signals(void) {
237
nd_log_limits_unlimited();
238
netdata_log_info("SIGNAL: Received %s. Cleaning up to exit...", name);
239
commands_exit();
240
- netdata_exit_gracefully(signals_waiting[i].reason);
240
+ netdata_exit_gracefully(signals_waiting[i].reason, true);
241
break;
242
243
case NETDATA_SIGNAL_DEADLY:
@@ -264,7 +264,8 @@ void nd_process_signals(void) {
264
last_update_mt += save_every_ut;
265
}
266
267
- poll(NULL, 0, 13 * MSEC_PER_SEC + 379);
267
+ if(poll(NULL, 0, 13 * MSEC_PER_SEC + 379) < 0) { ; }
268
+
269
process_triggered_signals();
270
}
271
}
src/daemon/winsvc.cc
+1
-1
@@ -109,7 +109,7 @@ static void *call_netdata_cleanup(void *arg)
109
reason = EXIT_REASON_SERVICE_STOP;
110
break;
111
}
112
- netdata_exit_gracefully(reason);
112
+ netdata_exit_gracefully(reason, false);
113
114
// Close event handle
115
netdata_service_log("Closing stop event handle...");
src/database/contexts/internal.h
+2
-1
@@ -303,7 +303,8 @@ static ALWAYS_INLINE void rrdmetric_set_collected(RRDMETRIC *rm) {
303
if(!(old & RRD_FLAG_COLLECTED))
304
__atomic_add_fetch(&rm->ri->rc->rrdhost->collected.metrics_count, 1, __ATOMIC_RELAXED);
305
306
- rm->rrddim->rrdcontexts.collected = true;
306
+ if(likely(rm->rrddim))
307
+ rm->rrddim->rrdcontexts.collected = true;
308
}
309
310
static ALWAYS_INLINE void rrdmetric_set_archived(RRDMETRIC *rm) {
src/database/engine/metric.c
+3
-7
@@ -368,14 +368,13 @@ size_t mrg_destroy(MRG *mrg) {
368
// Lock the partition to prevent new entries while we're cleaning up
369
mrg_index_write_lock(mrg, partition);
370
371
- Pvoid_t uuid_judy = mrg->index[partition].uuid_judy;
371
Word_t uuid_index = 0;
372
Pvoid_t *uuid_pvalue;
373
374
// Traverse all UUIDs in this partition
376
- for (uuid_pvalue = JudyLFirst(uuid_judy, &uuid_index, PJE0);
375
+ for (uuid_pvalue = JudyLFirst(mrg->index[partition].uuid_judy, &uuid_index, PJE0);
376
uuid_pvalue != NULL && uuid_pvalue != PJERR;
378
- uuid_pvalue = JudyLNext(uuid_judy, &uuid_index, PJE0)) {
377
+ uuid_pvalue = JudyLNext(mrg->index[partition].uuid_judy, &uuid_index, PJE0)) {
378
379
if (!(*uuid_pvalue))
380
continue;
@@ -407,10 +406,7 @@ size_t mrg_destroy(MRG *mrg) {
406
JudyLFreeArray(§ions_judy, PJE0);
407
}
408
410
- JudyLFreeArray(&uuid_judy, PJE0);
411
-
412
- // Update the main Judy array reference
413
- mrg->index[partition].uuid_judy = uuid_judy;
409
+ JudyLFreeArray(&mrg->index[partition].uuid_judy, PJE0);
410
411
// Unlock the partition
412
mrg_index_write_unlock(mrg, partition);
src/libnetdata/inlined.h
+29
@@ -420,6 +420,35 @@ static inline char *strncpyz(char *dst, const char *src, size_t dst_size_minus_1
420
return p;
421
}
422
423
+// append src to dst, but only if there is space for it
424
+// dst is always null terminated
425
+static inline size_t strcatz(char *dst, size_t len, size_t size, const char *src) {
426
+ // If starting offset is out of bounds, do nothing.
427
+ if (len >= size) {
428
+ dst[size - 1] = '\0';
429
+ return len;
430
+ }
431
+
432
+ // Move pointer to the end of the current string.
433
+ char *dest = dst + len;
434
+
435
+ // Reserve one byte for the null terminator.
436
+ size_t space = size - len - 1;
437
+ size_t initial_space = space;
438
+
439
+ // Append src into dst using pointer operations.
440
+ while (*src && space > 0) {
441
+ *dest++ = *src++;
442
+ space--;
443
+ }
444
+
445
+ // Null-terminate the string.
446
+ *dest = '\0';
447
+
448
+ // Return the new length.
449
+ return len + (initial_space - space);
450
+}
451
+
452
static inline void sanitize_json_string(char *dst, const char *src, size_t dst_size) {
453
while (*src != '\0' && dst_size > 1) {
454
if (*src < 0x1F) {
src/libnetdata/log/nd_log.c
+4
@@ -26,6 +26,10 @@ ALWAYS_INLINE void errno_clear(void) {
26
27
static ND_LOG_METHOD nd_logger_select_output(ND_LOG_SOURCES source, FILE **fpp, SPINLOCK **spinlock) {
28
*spinlock = NULL;
29
+
30
+ if(source >= _NDLS_MAX)
31
+ source = NDLS_DAEMON;
32
+
33
ND_LOG_METHOD output = nd_log.sources[source].method;
34
35
switch(output) {
src/libnetdata/spawn_server/log-forwarder.c
+34
-19
@@ -38,6 +38,8 @@ static inline LOG_FORWARDER_ENTRY *log_forwarder_find_entry_unsafe(LOG_FORWARDER
38
}
39
40
static inline void log_forwarder_del_entry_unsafe(LOG_FORWARDER *lf, LOG_FORWARDER_ENTRY *entry) {
41
+ if(!entry) return;
42
+
43
DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(lf->entries, entry, prev, next);
44
buffer_free(entry->wb);
45
freez(entry->cmd);
@@ -71,6 +73,8 @@ LOG_FORWARDER *log_forwarder_start(void) {
73
lf->running = true;
74
lf->thread = nd_thread_create("log-fw", NETDATA_THREAD_OPTION_JOINABLE, log_forwarder_thread_func, lf);
75
76
+ nd_log(NDLS_COLLECTORS, NDLP_INFO, "Log forwarder: created thread pointer: %p", lf->thread);
77
+
78
return lf;
79
}
80
@@ -84,22 +88,29 @@ void log_forwarder_stop(LOG_FORWARDER *lf) {
88
89
// Signal the thread to stop
90
spinlock_lock(&lf->spinlock);
87
- lf->running = false;
91
89
- // mark them all for deletion
92
+ if(!lf->running) {
93
+ spinlock_unlock(&lf->spinlock);
94
+ return;
95
+ }
96
+
97
+ lf->running = false;
98
mark_all_entries_for_deletion_unsafe(lf);
99
100
// Send a byte to the pipe to wake up the thread
93
- char ch = 0;
94
- if(write(lf->pipe_fds[PIPE_WRITE], &ch, 1) <= 0) { ; }
101
+// char ch = 0;
102
+// if(write(lf->pipe_fds[PIPE_WRITE], &ch, 1) <= 0) { ; }
103
+ close(lf->pipe_fds[PIPE_WRITE]); // force it to quit
104
spinlock_unlock(&lf->spinlock);
105
106
// Wait for the thread to finish
98
- close(lf->pipe_fds[PIPE_WRITE]); // force it to quit
99
- nd_thread_join(lf->thread);
100
- close(lf->pipe_fds[PIPE_READ]);
101
-
102
- freez(lf);
107
+ nd_log(NDLS_COLLECTORS, NDLP_INFO, "Log forwarder: stopping thread pointer: %p", lf->thread);
108
+ if(nd_thread_join(lf->thread) == 0) {
109
+ lf->thread = NULL;
110
+ freez(lf);
111
+ }
112
+ else
113
+ nd_log(NDLS_COLLECTORS, NDLP_ERR, "Log forwarder: not freeing lf due to nd_thread_join() error.");
114
}
115
116
// --------------------------------------------------------------------------------------------------------------------
@@ -179,6 +190,8 @@ void log_forwarder_annotate_fd_pid(LOG_FORWARDER *lf, int fd, pid_t pid) {
190
// log forwarder thread
191
192
static inline void log_forwarder_log(LOG_FORWARDER *lf __maybe_unused, LOG_FORWARDER_ENTRY *entry, const char *msg) {
193
+ if(!msg || !*msg || !entry || !lf) return;
194
+
195
const char *s = msg;
196
while(*s && isspace((uint8_t)*s)) s++;
197
if(*s == '\0') return; // do not log empty lines
@@ -202,7 +215,7 @@ static inline size_t log_forwarder_remove_deleted_unsafe(LOG_FORWARDER *lf) {
215
LOG_FORWARDER_ENTRY *next = entry->next;
216
217
if(entry->delete) {
205
- if (buffer_strlen(entry->wb))
218
+ if (entry->wb && buffer_strlen(entry->wb))
219
// there is something not logged in it - log it
220
log_forwarder_log(lf, entry, buffer_tostring(entry->wb));
221
@@ -223,8 +236,6 @@ static void *log_forwarder_thread_func(void *arg) {
236
while (1) {
237
spinlock_lock(&lf->spinlock);
238
if (!lf->running) {
226
- mark_all_entries_for_deletion_unsafe(lf);
227
- log_forwarder_remove_deleted_unsafe(lf);
239
spinlock_unlock(&lf->spinlock);
240
break;
241
}
@@ -260,20 +271,17 @@ static void *log_forwarder_thread_func(void *arg) {
271
if (bytes_read == -1) {
272
if (errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR) {
273
// Handle read error if necessary
263
- nd_log(NDLS_COLLECTORS, NDLP_ERR, "Failed to read from notification pipe");
264
- return NULL;
274
+ nd_log(NDLS_COLLECTORS, NDLP_ERR, "Log forwarder: Failed to read from notification pipe");
275
+ break;
276
}
277
}
278
}
279
269
- // Now check the other fds
280
spinlock_lock(&lf->spinlock);
281
272
- size_t to_remove = 0;
273
-
282
// read or mark them for deletion
283
for(LOG_FORWARDER_ENTRY *entry = lf->entries; entry ; entry = entry->next) {
276
- if (entry->pfds_idx < 1 || entry->pfds_idx >= nfds || !(pfds[entry->pfds_idx].revents & POLLIN))
284
+ if (entry->pfds_idx < 1 || entry->pfds_idx >= nfds || !(pfds[entry->pfds_idx].revents & POLLIN) || entry->delete || !entry->wb)
285
continue;
286
287
BUFFER *wb = entry->wb;
@@ -285,7 +293,6 @@ static void *log_forwarder_thread_func(void *arg) {
293
else if(bytes_read == 0 || (bytes_read == -1 && errno != EINTR && errno != EAGAIN)) {
294
// EOF or error
295
entry->delete = true;
288
- to_remove++;
296
}
297
298
// log as many lines are they have been received
@@ -319,5 +326,13 @@ static void *log_forwarder_thread_func(void *arg) {
326
nd_log(NDLS_COLLECTORS, NDLP_ERR, "Log forwarder: poll() error");
327
}
328
329
+ nd_log(NDLS_COLLECTORS, NDLP_ERR, "Log forwarder: exiting...");
330
+
331
+ spinlock_lock(&lf->spinlock);
332
+ mark_all_entries_for_deletion_unsafe(lf);
333
+ log_forwarder_remove_deleted_unsafe(lf);
334
+ spinlock_unlock(&lf->spinlock);
335
+ close(lf->pipe_fds[PIPE_READ]);
336
+
337
return NULL;
338
}
src/libnetdata/threads/threads.c
+3
-1
@@ -440,7 +440,9 @@ int nd_thread_join(ND_THREAD *nti) {
440
441
int ret = pthread_join(nti->thread, NULL);
442
if(ret != 0) {
443
- nd_log(NDLS_DAEMON, NDLP_WARNING, "cannot join thread. pthread_join() failed with code %d. (tag=%s)", ret, nti->tag);
443
+ nd_log(NDLS_DAEMON, NDLP_WARNING,
444
+ "cannot join thread. pthread_join() failed with code %d. (tag=%s)",
445
+ ret, nti->tag);
446
}
447
else {
448
nd_thread_status_set(nti, NETDATA_THREAD_STATUS_JOINED);
src/web/server/web_client.c
+1
-1
@@ -1191,7 +1191,7 @@ static inline int web_client_process_url(RRDHOST *host, struct web_client *w, ch
1191
buffer_strcat(w->response.data, "I am doing it already");
1192
1193
netdata_log_error("web request to exit received.");
1194
- netdata_exit_gracefully(EXIT_REASON_API_QUIT);
1194
+ netdata_exit_gracefully(EXIT_REASON_API_QUIT, true);
1195
return HTTP_RESP_OK;
1196
}
1197
else if(unlikely(hash == hash_debug && strcmp(tok, "debug") == 0)) {