@cryptotaxi247 / netdata-1 / commits / 488d286c9

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