From 7da8f63e27f0f6c25beff6845d76a4c93be43f33 Mon Sep 17 00:00:00 2001
From: "Christian W. Zuckschwerdt" <zany@triq.net>
Date: Tue, 26 Mar 2024 11:54:28 +0100
Subject: [PATCH] Add log rotate support for dumper files (#2876)

---
 include/r_api.h |  2 ++
 src/r_api.c     | 45 +++++++++++++++++++++++++++++++++++++++++++++
 src/rtl_433.c   | 10 ++++++++++
 3 files changed, 57 insertions(+)

diff --git a/include/r_api.h b/include/r_api.h
index 2a4344df..19398f24 100644
--- a/include/r_api.h
+++ b/include/r_api.h
@@ -97,6 +97,8 @@ void start_outputs(struct r_cfg *cfg, char const *const *well_known);
 
 void add_sr_dumper(struct r_cfg *cfg, char const *spec, int overwrite);
 
+void reopen_dumpers(struct r_cfg *cfg);
+
 void close_dumpers(struct r_cfg *cfg);
 
 void add_dumper(struct r_cfg *cfg, char const *spec, int overwrite);
diff --git a/src/r_api.c b/src/r_api.c
index 6ad85ece..27be331c 100644
--- a/src/r_api.c
+++ b/src/r_api.c
@@ -43,6 +43,10 @@
 #include "fatal.h"
 #include "http_server.h"
 
+#ifndef _WIN32
+#include <sys/stat.h>
+#endif
+
 #ifdef _WIN32
 #include <io.h>
 #include <fcntl.h>
@@ -1155,6 +1159,47 @@ void add_sr_dumper(r_cfg_t *cfg, char const *spec, int overwrite)
     cfg->sr_execopen = overwrite;
 }
 
+void reopen_dumpers(struct r_cfg *cfg)
+{
+#ifndef _WIN32
+    for (void **iter = cfg->demod->dumper.elems; iter && *iter; ++iter) {
+        file_info_t *dumper = *iter;
+        if (dumper->file && (dumper->file != stdout)) {
+            // Get current file inode
+            struct stat old_st = {0};
+            int ret = fstat(fileno(dumper->file), &old_st);
+            if (ret) {
+                fprintf(stderr, "Failed to fstat %s (%d)\n", dumper->path, errno);
+                exit(1);
+            }
+
+            // Get new path inode if available
+            struct stat new_st = {0};
+            stat(dumper->path, &new_st);
+            // ok for stat() to fail, the file might not exist
+            if (old_st.st_ino == new_st.st_ino) {
+                continue;
+            }
+
+            // Reopen the file
+            print_logf(LOG_INFO, "Dumper", "Reopening \"%s\"", dumper->path);
+            fclose(dumper->file);
+            dumper->file = fopen(dumper->path, "wb");
+            if (!dumper->file) {
+                fprintf(stderr, "Failed to open %s\n", dumper->path);
+                exit(1);
+            }
+            if (dumper->format == VCD_LOGIC) {
+                pulse_data_print_vcd_header(dumper->file, cfg->samp_rate);
+            }
+            if (dumper->format == PULSE_OOK) {
+                pulse_data_print_pulse_header(dumper->file);
+            }
+        }
+    }
+#endif
+}
+
 void close_dumpers(struct r_cfg *cfg)
 {
     for (void **iter = cfg->demod->dumper.elems; iter && *iter; ++iter) {
diff --git a/src/rtl_433.c b/src/rtl_433.c
index 83684325..b95856e7 100644
--- a/src/rtl_433.c
+++ b/src/rtl_433.c
@@ -1316,6 +1316,7 @@ static void parse_conf_option(r_cfg_t *cfg, int opt, char *arg)
 }
 
 static r_cfg_t g_cfg;
+static volatile sig_atomic_t sig_hup;
 
 // TODO: SIGINFO is not in POSIX...
 #ifndef SIGINFO
@@ -1354,6 +1355,10 @@ static void sighandler(int signum)
         write_err("Ignoring received signal SIGPIPE, Broken pipe.\n");
         return;
     }
+    else if (signum == SIGHUP) {
+        sig_hup = 1;
+        return;
+    }
     else if (signum == SIGINFO/* TODO: maybe SIGUSR1 */) {
         g_cfg.stats_now++;
         return;
@@ -1511,6 +1516,10 @@ static void timer_handler(struct mg_connection *nc, int ev, void *ev_data)
 {
     //fprintf(stderr, "%s: %d, %d, %p, %p\n", __func__, nc->sock, ev, nc->user_data, ev_data);
     r_cfg_t *cfg = (r_cfg_t *)nc->user_data;
+    if (sig_hup) {
+        reopen_dumpers(cfg);
+        sig_hup = 0;
+    }
     switch (ev) {
     case MG_EV_TIMER: {
         double now  = *(double *)ev_data;
@@ -1996,6 +2005,7 @@ int main(int argc, char **argv) {
     sigact.sa_handler = sighandler;
     sigemptyset(&sigact.sa_mask);
     sigact.sa_flags = 0;
+    sigaction(SIGHUP, &sigact, NULL);
     sigaction(SIGINT, &sigact, NULL);
     sigaction(SIGTERM, &sigact, NULL);
     sigaction(SIGQUIT, &sigact, NULL);