allow coredumps to be generated (#19743)
* allow coredumps * use async safe saving of the status file
Costa Tsaousis committed
Mar 2, 2025 at 15:09 UTC
488d286c9b5f29cdaa29a6f2dfea72a218fbe2ad
3 files changed
+98
-26
src/daemon/daemon-status-file.c
+82
-21
@@ -505,35 +505,94 @@ DAEMON_STATUS_FILE daemon_status_file_load(void) {
505
// save the current status
506
507
static bool save_status_file(const char *directory, const char *content, size_t content_size) {
508
+ // THIS FUNCTION MUST USE ONLY ASYNC-SAFE OPERATIONS
509
+
510
if(!directory || !*directory)
511
return false;
512
513
char filename[FILENAME_MAX];
514
char temp_filename[FILENAME_MAX];
515
514
- snprintfz(filename, sizeof(filename), "%s/%s", directory, STATUS_FILENAME);
515
- snprintfz(temp_filename, sizeof(temp_filename), "%s/%s-%08x", directory, STATUS_FILENAME, (unsigned)gettid_cached());
516
-
517
- FILE *fp = fopen(temp_filename, "w");
518
- if (!fp)
516
+ /* Construct filenames using async-safe string operations */
517
+ /* Using simple string concatenation instead of snprintf */
518
+ size_t dir_len = strlen(directory);
519
+ if (dir_len + 1 + strlen(STATUS_FILENAME) >= FILENAME_MAX)
520
+ return false; /* Path too long */
521
+
522
+ memcpy(filename, directory, dir_len);
523
+ filename[dir_len] = '/';
524
+ memcpy(filename + dir_len + 1, STATUS_FILENAME, strlen(STATUS_FILENAME) + 1);
525
+
526
+ /* Create a unique temp filename using thread id */
527
+ unsigned int tid = (unsigned int)gettid_cached();
528
+ char tid_str[16];
529
+ char *tid_ptr = tid_str + sizeof(tid_str) - 1;
530
+ *tid_ptr = '\0';
531
+
532
+ unsigned int tid_copy = tid;
533
+ do {
534
+ tid_ptr--;
535
+ *tid_ptr = "0123456789abcdef"[tid_copy & 0xf];
536
+ tid_copy >>= 4;
537
+ } while (tid_copy && tid_ptr > tid_str);
538
+
539
+ size_t temp_name_len = dir_len + 1 + strlen(STATUS_FILENAME) + 1 + (sizeof(tid_str) - (tid_ptr - tid_str));
540
+ if (temp_name_len >= FILENAME_MAX)
541
+ return false; /* Path too long */
542
+
543
+ memcpy(temp_filename, directory, dir_len);
544
+ temp_filename[dir_len] = '/';
545
+ char *ptr = temp_filename + dir_len + 1;
546
+ memcpy(ptr, STATUS_FILENAME, strlen(STATUS_FILENAME));
547
+ ptr += strlen(STATUS_FILENAME);
548
+ *ptr++ = '-';
549
+ memcpy(ptr, tid_ptr, strlen(tid_ptr) + 1);
550
+
551
+ /* Open file with O_WRONLY, O_CREAT, and O_TRUNC flags */
552
+ int fd = open(temp_filename, O_WRONLY | O_CREAT | O_TRUNC, 0664);
553
+ if (fd == -1)
554
return false;
555
521
- bool ok = fwrite(content, 1, content_size, fp) == content_size;
522
- fclose(fp);
556
+ /* Write content to file using write() */
557
+ ssize_t bytes_written = 0;
558
+ size_t total_written = 0;
559
+
560
+ while (total_written < content_size) {
561
+ bytes_written = write(fd, content + total_written, content_size - total_written);
562
+
563
+ if (bytes_written == -1) {
564
+ if (errno == EINTR)
565
+ continue; /* Retry if interrupted by signal */
566
+
567
+ close(fd);
568
+ unlink(temp_filename); /* Remove the temp file */
569
+ return false;
570
+ }
571
+
572
+ total_written += bytes_written;
573
+ }
574
524
- if (!ok) {
575
+ /* Fsync to ensure data is written to disk */
576
+ if (fsync(fd) == -1) {
577
+ close(fd);
578
unlink(temp_filename);
579
return false;
580
}
581
582
+ /* Close file */
583
+ if (close(fd) == -1) {
584
+ unlink(temp_filename);
585
+ return false;
586
+ }
587
+
588
+ /* Set permissions using chmod() */
589
if (chmod(temp_filename, 0664) != 0) {
530
- nd_log(NDLS_DAEMON, NDLP_ERR, "Cannot set permissions on status file '%s'", temp_filename);
590
unlink(temp_filename);
591
return false;
592
}
593
594
+ /* Rename temp file to target file */
595
if (rename(temp_filename, filename) != 0) {
536
- nd_log(NDLS_DAEMON, NDLP_ERR, "Cannot rename status file '%s' to '%s'", temp_filename, filename);
596
unlink(temp_filename);
597
return false;
598
}
@@ -549,8 +608,9 @@ static void static_save_buffer_init(void) {
608
buffer_flush(static_save_buffer);
609
}
610
552
-static void daemon_status_file_save(DAEMON_STATUS_FILE *ds) {
553
- spinlock_lock(&dsf_spinlock);
611
+static void daemon_status_file_save(DAEMON_STATUS_FILE *ds, bool have_lock) {
612
+ if(!have_lock)
613
+ spinlock_lock(&dsf_spinlock);
614
615
static_save_buffer_init();
616
@@ -582,12 +642,13 @@ static void daemon_status_file_save(DAEMON_STATUS_FILE *ds) {
642
if (!saved)
643
nd_log(NDLS_DAEMON, NDLP_ERR, "Failed to save status file in any location");
644
585
- spinlock_unlock(&dsf_spinlock);
645
+ if(!have_lock)
646
+ spinlock_unlock(&dsf_spinlock);
647
}
648
649
void daemon_status_file_update_status(DAEMON_STATUS status) {
650
daemon_status_file_refresh(status);
590
- daemon_status_file_save(&session_status);
651
+ daemon_status_file_save(&session_status, false);
652
}
653
654
void daemon_status_file_exit_reason_save(EXIT_REASON reason) {
@@ -595,15 +656,16 @@ void daemon_status_file_exit_reason_save(EXIT_REASON reason) {
656
spinlock_lock(&dsf_spinlock);
657
session_status.exit_reason = exit_initiated;
658
spinlock_unlock(&dsf_spinlock);
598
- daemon_status_file_save(&session_status);
659
+ daemon_status_file_save(&session_status, false);
660
}
661
662
static void daemon_status_file_out_of_memory(void) {
663
daemon_status_file_exit_reason_save(EXIT_REASON_OUT_OF_MEMORY);
664
}
665
605
-void daemon_status_file_bad_signal_received(EXIT_REASON reason) {
606
- spinlock_lock(&dsf_spinlock);
666
+void daemon_status_file_deadly_signal_received(EXIT_REASON reason) {
667
+ // DO NOT LOCK IN THIS FUNCTION - WE CRASHED ALREADY AND WE ARE INSIDE THE SIGNAL HANDLER!
668
+
669
session_status.exit_reason |= reason;
670
671
if(!session_status.fatal.thread[0])
@@ -615,8 +677,7 @@ void daemon_status_file_bad_signal_received(EXIT_REASON reason) {
677
strncpyz(session_status.fatal.stack_trace, buffer_tostring(static_save_buffer), sizeof(session_status.fatal.stack_trace) - 1);
678
}
679
618
- spinlock_unlock(&dsf_spinlock);
619
- daemon_status_file_save(&session_status);
680
+ daemon_status_file_save(&session_status, true);
681
}
682
683
// --------------------------------------------------------------------------------------------------------------------
@@ -715,7 +776,7 @@ void post_status_file(struct post_status_file_thread_data *d) {
776
if(rc == CURLE_OK) {
777
XXH64_hash_t hash = daemon_status_file_hash(d->status, d->msg, d->cause);
778
dedup_keep_hash(&session_status, hash);
718
- daemon_status_file_save(&session_status);
779
+ daemon_status_file_save(&session_status, false);
780
}
781
782
curl_easy_cleanup(curl);
@@ -1001,5 +1062,5 @@ void daemon_status_file_register_fatal(const char *filename, const char *functio
1062
1063
spinlock_unlock(&dsf_spinlock);
1064
1004
- daemon_status_file_save(&session_status);
1065
+ daemon_status_file_save(&session_status, false);
1066
}
src/daemon/daemon-status-file.h
+1
-1
@@ -83,7 +83,7 @@ DAEMON_STATUS_FILE daemon_status_file_load(void);
83
// saves the current status
84
void daemon_status_file_update_status(DAEMON_STATUS status);
85
void daemon_status_file_exit_reason_save(EXIT_REASON reason);
86
-void daemon_status_file_bad_signal_received(EXIT_REASON reason);
86
+void daemon_status_file_deadly_signal_received(EXIT_REASON reason);
87
88
// check for a crash
89
void daemon_status_file_check_crash(void);
src/daemon/signals.c
+15
-4
@@ -47,8 +47,12 @@ static void signal_handler(int signo) {
47
signals_waiting[i].count++;
48
49
if(signals_waiting[i].action == NETDATA_SIGNAL_FATAL) {
50
+ // Update the status file
51
+ daemon_status_file_deadly_signal_received(signals_waiting[i].reason);
52
+
53
+ // log it
54
char buffer[200 + 1];
51
- snprintfz(buffer, sizeof(buffer) - 1, "\nSIGNAL HANDLER: received: %s in thread %d. Oops! This is bad!\n",
55
+ snprintfz(buffer, sizeof(buffer) - 1, "\nSIGNAL HANDLER: received: %s in thread %d!\n",
56
signals_waiting[i].name, gettid_cached());
57
58
if(write(STDERR_FILENO, buffer, strlen(buffer)) == -1) {
@@ -56,8 +60,15 @@ static void signal_handler(int signo) {
60
;
61
}
62
59
- // Always update the status file for fatal signals
60
- daemon_status_file_bad_signal_received(signals_waiting[i].reason);
63
+ // Reset the signal's disposition to the default handler.
64
+ struct sigaction sa;
65
+ sa.sa_handler = SIG_DFL;
66
+ sigemptyset(&sa.sa_mask);
67
+ sa.sa_flags = 0;
68
+ sigaction(signo, &sa, NULL);
69
+
70
+ // Re-raise the signal, which now uses the default action.
71
+ raise(signo);
72
}
73
74
break;
@@ -175,7 +186,7 @@ void nd_process_signals(void) {
186
187
case NETDATA_SIGNAL_FATAL:
188
nd_log_limits_unlimited();
178
- daemon_status_file_bad_signal_received(signals_waiting[i].reason);
189
+ daemon_status_file_deadly_signal_received(signals_waiting[i].reason);
190
fatal("SIGNAL: Received %s. netdata now exits.", name);
191
break;
192