summaryrefslogtreecommitdiff
path: root/src/daemon.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/daemon.c')
-rw-r--r--src/daemon.c148
1 files changed, 148 insertions, 0 deletions
diff --git a/src/daemon.c b/src/daemon.c
new file mode 100644
index 0000000..9714a65
--- /dev/null
+++ b/src/daemon.c
@@ -0,0 +1,148 @@
+//#include "daemon.h"
+#include "umbrella.h"
+#include "log.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/signalfd.h>
+
+/*
+ * Create a directory and all parents, like mkdir -p.
+ * Returns 0 if the directory exists or was created, -1 on error.
+ */
+static int mkdirp(const char *path, mode_t mode) {
+ char tmp[MAX_PATH];
+ char *p;
+ size_t len;
+
+ snprintf(tmp, sizeof(tmp), "%s", path);
+ len = strlen(tmp);
+ if (tmp[len - 1] == '/')
+ tmp[len - 1] = '\0';
+
+ for (p = tmp + 1; *p; p++) {
+ if (*p == '/') {
+ *p = '\0';
+ if (mkdir(tmp, mode) != 0 && errno != EEXIST)
+ return -1;
+ *p = '/';
+ }
+ }
+ if (mkdir(tmp, mode) != 0 && errno != EEXIST)
+ return -1;
+ return 0;
+}
+
+int daemon_daemonize(int foreground) {
+ if (foreground)
+ return 0;
+
+ pid_t pid = fork();
+ if (pid < 0) {
+ perror("fork");
+ return -1;
+ }
+ if (pid > 0)
+ exit(0); /* parent exits */
+
+ /* Child: become session leader */
+ if (setsid() < 0) {
+ perror("setsid");
+ return -1;
+ }
+
+ /* Fork again so we're not a session leader (can't acquire a tty) */
+ pid = fork();
+ if (pid < 0) {
+ perror("fork");
+ return -1;
+ }
+ if (pid > 0)
+ exit(0);
+
+ /* Redirect stdin/stdout/stderr to /dev/null */
+ int dev_null = open("/dev/null", O_RDWR);
+ if (dev_null >= 0) {
+ dup2(dev_null, STDIN_FILENO);
+ dup2(dev_null, STDOUT_FILENO);
+ dup2(dev_null, STDERR_FILENO);
+ if (dev_null > STDERR_FILENO)
+ close(dev_null);
+ }
+
+ umask(027);
+ return 0;
+}
+
+int daemon_write_pid(void) {
+ /* Ensure the run directory exists */
+ if (mkdirp("/run/umbrella", 0755) != 0 && errno != EEXIST) {
+ log_error("Could not create /run/umbrella: %s", strerror(errno));
+ return -1;
+ }
+
+ FILE *f = fopen(UMBRELLA_PID_FILE, "w");
+ if (!f) {
+ log_error("Could not write pid file %s: %s",
+ UMBRELLA_PID_FILE, strerror(errno));
+ return -1;
+ }
+ fprintf(f, "%d\n", (int)getpid());
+ fclose(f);
+ return 0;
+}
+
+int daemon_init(void) {
+ /* Ensure runtime and log directories exist */
+ if (mkdirp("/run/umbrella", 0755) != 0 && errno != EEXIST) {
+ log_error("mkdirp /run/umbrella: %s", strerror(errno));
+ return -1;
+ }
+ if (mkdirp("/var/log/umbrella", 0755) != 0 && errno != EEXIST) {
+ log_error("mkdirp /var/log/umbrella: %s", strerror(errno));
+ return -1;
+ }
+
+ if (daemon_write_pid() != 0)
+ return -1;
+
+ /*
+ * Set up a signalfd so signals arrive as readable events on the
+ * epoll loop rather than interrupting syscalls unpredictably.
+ * We block the signals first, then read them via the fd.
+ */
+ sigset_t mask;
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGINT);
+ sigaddset(&mask, SIGTERM);
+ sigaddset(&mask, SIGCHLD); /* child process state changes */
+ sigaddset(&mask, SIGHUP); /* reload config */
+ sigaddset(&mask, SIGPIPE); /* ignore broken pipe */
+
+ if (sigprocmask(SIG_BLOCK, &mask, NULL) != 0) {
+ log_error("sigprocmask: %s", strerror(errno));
+ return -1;
+ }
+
+ int sfd = signalfd(-1, &mask, SFD_NONBLOCK | SFD_CLOEXEC);
+ if (sfd < 0) {
+ log_error("signalfd: %s", strerror(errno));
+ return -1;
+ }
+
+ g.signal_fd = sfd;
+ log_info("Daemon initialized (pid %d)", (int)getpid());
+ return 0;
+}
+
+void daemon_cleanup(void) {
+ log_info("Cleaning up...");
+ unlink(UMBRELLA_PID_FILE);
+ unlink(UMBRELLA_SOCK_PATH);
+}