//#include "daemon.h" #include "umbrella.h" #include "log.h" #include #include #include #include #include #include #include #include #include /* * 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); }