|
|
|
/*
|
|
|
|
* daemonize.c -- provides daemonize() function to daemonize a process
|
|
|
|
* according to daemon(7) and given flags.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <stdbool.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <sys/resource.h>
|
|
|
|
#include <syslog.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <signal.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
|
|
|
|
#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;
|
|
|
|
}
|