/* * daemonize.c -- provides daemonize() function to daemonize a process * according to daemon(7) and given flags. */ #include #include #include #include #include #include #include #include #include #include #include "daemonize.h" #include "config.h" #define unless(x) if(!((x))) bool daemonize(uint32_t flags) { if (flags & NODAEMONIZE) return true; bool retval = true; unless (flags & NOCLOSEFD) { struct rlimit maxfd; if(getrlimit(RLIMIT_NOFILE, &maxfd) == -1) { //Shouldn't ever happen syslog(cfg_log_facility | LOG_WARNING, "daemonize: getrlimit failed: %m"); retval = false; } for (unsigned int fd = 3; fd < maxfd.rlim_cur; fd++) { char tries = 0; while(close(fd) == -1) { // Try 3 times to close fd, retry only if interrupted unless (errno == EINTR) { break; } tries++; if (tries > 3) { syslog(cfg_log_facility | LOG_WARNING, "daemonize: could not close fd %d: %m", fd); retval = false; break; } } } } unless (flags & NORSTSIG) { for (int sig = 0; sig <= SIGRTMAX; sig++) { //FIXME: This may not be the best way signal(sig, SIG_DFL); //EINVAL is not important } } unless (flags & NORSTSIGMASK) { sigset_t *set = malloc(sizeof(sigset_t)); if (set == NULL) { syslog(cfg_log_facility | LOG_WARNING, "daemonize: could not malloc sigset_t: %m"); retval = false; } sigemptyset(set); //shall not have errors if (sigprocmask(SIG_SETMASK, set, NULL) == -1) { //Shall not fail syslog(cfg_log_facility | LOG_WARNING, "daemonize: sigprocmask failed: %m"); retval = false; } } unless (flags & NOSANITY) { // We don't sanitize environment, the user does. } // Create pipe for notification in step 14 int pipe_fds[2]; if(pipe(pipe_fds) == -1) { syslog(cfg_log_facility | LOG_WARNING, "daemonize: could not create pipe: %m"); retval = false; } unless (flags & NOBGFORK) { pid_t val = fork(); if (val == -1) { syslog(cfg_log_facility | LOG_WARNING, "daemonize: fork failed: %m"); retval = false; } if (val > 0) { // We're the grandparent // We only wait for report from our grandson and then exit. close(pipe_fds[1]); char buf[2] = {0,0}; int tries = 0; while (read(pipe_fds[0], buf, 1) == -1) { tries++; if (errno == EINTR && tries <= 3) { continue; } syslog(cfg_log_facility | LOG_WARNING, "daemonize: grandpa: cannot receive data through pipe: %m"); retval = false; } if (buf[0] != 'S') { //Not success syslog(cfg_log_facility | LOG_NOTICE, "daemonize: grandpa: grandson didn't report success"); retval = false; } exit(retval?0:1); } // We're the parent and we continue running at this point. close(pipe_fds[0]); } unless(flags & NOSETSID) { if(setsid() == -1) syslog(cfg_log_facility | LOG_WARNING, "daemonize: father: setsid failed: %m"); } unless (flags & NOSECONDFORK) { pid_t val = fork(); if (val == -1) { syslog(cfg_log_facility | LOG_WARNING, "daemonize: second fork failed: %m"); retval = false; } if (val > 0) { //We're the parent, we exit exit(0); } // We're the son and we continue running. } unless (flags & NOCONNECTIO) { int fd = open("/dev/null", O_RDWR); dup2(fd, STDIN_FILENO); //These shall not fail, maybe exept EINTR (FIXME) dup2(fd, STDOUT_FILENO); dup2(fd, STDERR_FILENO); close(fd); } unless(flags & NORSTUMASK) { umask(0000); //No errors are defined. } unless (flags & NOCHANGEWD) { chdir("/"); //No errors are applicable, maybe except EACCESS (FIXME) } unless (flags & NOPIDFILE) { // Not implemented } unless (flags & NOPRIVDROP) { // We don't drop privileges, as we want to exec programs with privileges of the user. } write(pipe_fds[1], retval?"S":"F", 1); syslog(cfg_log_facility | LOG_INFO, "Now runing as a daemon"); close(pipe_fds[1]); return retval; }