]> git.seodisparate.com - c_simple_http/commitdiff
Impl. hot reloading of config and on SIGUSR1
authorStephen Seo <seo.disparate@gmail.com>
Tue, 10 Sep 2024 04:55:16 +0000 (13:55 +0900)
committerStephen Seo <seo.disparate@gmail.com>
Tue, 10 Sep 2024 04:55:16 +0000 (13:55 +0900)
Resolves https://git.seodisparate.com/stephenseo/c_simple_http/issues/2

src/arg_parse.c
src/arg_parse.h
src/globals.c
src/globals.h
src/main.c
src/signal_handling.c
src/signal_handling.h

index f981e8303ccabdb4bfe34b28329271da1963d112..7472be5c0da06e87eba06a98b178d9f420806903 100644 (file)
@@ -29,6 +29,7 @@ void print_usage(void) {
   puts("  --req-header-to-print=<header> (can be used multiple times)");
   puts("    For example: --req-header-to-print=User-Agent");
   puts("    Note that this option is case-insensitive");
+  puts("  --enable-reload-config-on-change");
 }
 
 Args parse_args(int argc, char **argv) {
@@ -66,6 +67,8 @@ Args parse_args(int argc, char **argv) {
         free(header_buf);
         exit(1);
       }
+    } else if (strcmp(argv[0], "--enable-reload-config-on-change") == 0) {
+      args.flags |= 2;
     } else {
       puts("ERROR: Invalid args!\n");
       print_usage();
index 6aef9907eec846836799f6553f963175fe4a1748..5943192dd85ca83b39c7733d4d61d45536896fe4 100644 (file)
@@ -23,6 +23,8 @@
 typedef struct Args {
   // xxxx xxx0 - enable peer addr print.
   // xxxx xxx1 - disable peer addr print.
+  // xxxx xx0x - disable listen on config file for reloading.
+  // xxxx xx1x - enable listen on config file for reloading.
   unsigned short flags;
   unsigned short port;
   // Does not need to be free'd, this should point to a string in argv.
index 9262d01d2e6b9a641b52b58d21eebbe2b9a5fd67..26483daae14f5d24ee8938cd94e71e7d38725d12 100644 (file)
@@ -17,5 +17,6 @@
 #include "globals.h"
 
 int C_SIMPLE_HTTP_KEEP_RUNNING = 1;
+int C_SIMPLE_HTTP_SIGUSR1_SET = 0;
 
 // vim: et ts=2 sts=2 sw=2
index 209b32ee211831e42e3f6397d8d0a46299f59d65..9ce14ea6f2b89fbeab7885646484edeeb2d57e12 100644 (file)
@@ -18,6 +18,7 @@
 #define SEODISPARATE_COM_C_SIMPLE_HTTP_GLOBALS_H_
 
 extern int C_SIMPLE_HTTP_KEEP_RUNNING;
+extern int C_SIMPLE_HTTP_SIGUSR1_SET;
 
 #endif
 
index ddc2a6b01897abaed75dc72aae2c22f302e42d02..2ba4e5653db92c032826fb0aa9a8f747273cbdf8 100644 (file)
 #include <stdlib.h>
 #include <stdio.h>
 
-// Unix includes.
+// Linux/Unix includes.
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <unistd.h>
 #include <signal.h>
 #include <time.h>
 #include <errno.h>
+#include <sys/inotify.h>
+#include <linux/limits.h>
 
 // Third party includes.
 #include <SimpleArchiver/src/helpers.h>
@@ -64,6 +66,13 @@ int c_simple_http_headers_check_print(void *data, void *ud) {
   return 0;
 }
 
+void c_simple_http_inotify_fd_cleanup(int *fd) {
+  if (fd && *fd >= 0) {
+    close(*fd);
+    *fd = -1;
+  }
+}
+
 int main(int argc, char **argv) {
   __attribute__((cleanup(c_simple_http_free_args)))
   Args args = parse_args(argc, argv);
@@ -103,6 +112,34 @@ int main(int argc, char **argv) {
     }
   }
 
+  __attribute__((cleanup(c_simple_http_inotify_fd_cleanup)))
+  int inotify_config_fd = -1;
+  __attribute__((cleanup(simple_archiver_helper_cleanup_malloced)))
+  void *inotify_event_buf = NULL;
+  struct inotify_event *inotify_event = NULL;
+  const size_t inotify_event_buf_size =
+    sizeof(struct inotify_event) + NAME_MAX + 1;
+  if ((args.flags & 0x2) != 0) {
+    inotify_config_fd = inotify_init1(IN_NONBLOCK);
+    if (inotify_config_fd < 0) {
+      fprintf(stderr, "ERROR Failed to init listen on config file for hot "
+        "reloading! (error code \"%d\")\n", errno);
+      return 3;
+    }
+
+    if (inotify_add_watch(
+        inotify_config_fd,
+        args.config_file,
+        IN_MODIFY | IN_CLOSE_WRITE) == -1) {
+      fprintf(stderr, "ERROR Failed to set up listen on config file for hot "
+        "reloading! (error code \"%d\")\n", errno);
+      return 4;
+    }
+
+    inotify_event_buf = malloc(inotify_event_buf_size);
+    inotify_event = inotify_event_buf;
+  }
+
   struct timespec sleep_time;
   sleep_time.tv_sec = 0;
   sleep_time.tv_nsec = C_SIMPLE_HTTP_SLEEP_NANOS;
@@ -112,6 +149,7 @@ int main(int argc, char **argv) {
   peer_info.sin6_family = AF_INET6;
 
   signal(SIGINT, C_SIMPLE_HTTP_handle_sigint);
+  signal(SIGUSR1, C_SIMPLE_HTTP_handle_sigusr1);
 
   unsigned char recv_buf[C_SIMPLE_HTTP_RECV_BUF_SIZE];
 
@@ -121,6 +159,63 @@ int main(int argc, char **argv) {
 
   while (C_SIMPLE_HTTP_KEEP_RUNNING) {
     nanosleep(&sleep_time, NULL);
+
+    if (C_SIMPLE_HTTP_SIGUSR1_SET) {
+      // Handle hot-reloading of config file due to SIGUSR1.
+      C_SIMPLE_HTTP_SIGUSR1_SET = 0;
+      fprintf(stderr, "NOTICE SIGUSR1, reloading config file...\n");
+      c_simple_http_clean_up_parsed_config(&parsed_config);
+      parsed_config = c_simple_http_parse_config(
+        args.config_file,
+        "PATH",
+        NULL);
+    }
+    if ((args.flags & 0x2) != 0) {
+      // Handle hot-reloading of config file.
+      read_ret =
+        read(inotify_config_fd, inotify_event_buf, inotify_event_buf_size);
+      if (read_ret == -1) {
+        if (errno == EAGAIN || errno == EWOULDBLOCK) {
+          // No events, do nothing.
+        } else {
+          fprintf(
+            stderr,
+            "WARNING Error code \"%d\" on config file listen for hot "
+            "reloading!\n",
+            errno);
+        }
+      } else if (read_ret > 0) {
+#ifndef NDEBUG
+        printf("DEBUG inotify_event->mask: %x\n", inotify_event->mask);
+#endif
+        if ((inotify_event->mask & IN_MODIFY) != 0
+            || (inotify_event->mask & IN_CLOSE_WRITE) != 0) {
+          fprintf(stderr, "NOTICE Config file modified, reloading...\n");
+          c_simple_http_clean_up_parsed_config(&parsed_config);
+          parsed_config = c_simple_http_parse_config(
+            args.config_file,
+            "PATH",
+            NULL);
+        } else if ((inotify_event->mask & IN_IGNORED) != 0) {
+          fprintf(
+            stderr,
+            "NOTICE Config file modified (IN_IGNORED), reloading...\n");
+          c_simple_http_clean_up_parsed_config(&parsed_config);
+          parsed_config = c_simple_http_parse_config(
+            args.config_file,
+            "PATH",
+            NULL);
+          // Re-initialize inotify.
+          //c_simple_http_inotify_fd_cleanup(&inotify_config_fd);
+          //inotify_config_fd = inotify_init1(IN_NONBLOCK);
+          inotify_add_watch(
+            inotify_config_fd,
+            args.config_file,
+            IN_MODIFY | IN_CLOSE_WRITE);
+        }
+      }
+    }
+
     socket_len = sizeof(struct sockaddr_in6);
     ret = accept(tcp_socket, (struct sockaddr *)&peer_info, &socket_len);
     if (ret == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
index dfe434097026c5bdf95b7d0c4f5e9c43a6d1a63b..3f64586ab7f1df50b7355c8e14ebcb3a32969291 100644 (file)
@@ -34,4 +34,13 @@ void C_SIMPLE_HTTP_handle_sigint(int signal) {
   }
 }
 
+void C_SIMPLE_HTTP_handle_sigusr1(int signal) {
+  if (signal == SIGUSR1) {
+#ifndef NDEBUG
+    puts("Handling SIGUSR1");
+#endif
+    C_SIMPLE_HTTP_SIGUSR1_SET = 1;
+  }
+}
+
 // vim: et ts=2 sts=2 sw=2
index 3456ebf28a47e774bc8e572400180d4e7bf38b2d..0237f40c05e00822faa33fda56c85096c1190e0f 100644 (file)
@@ -18,6 +18,7 @@
 #define SEODISPARATE_COM_C_SIMPLE_HTTP_SIGNAL_HANDLING_H_
 
 void C_SIMPLE_HTTP_handle_sigint(int signal);
+void C_SIMPLE_HTTP_handle_sigusr1(int signal);
 
 #endif