1
0
Fork 0
You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

159 lines
3.9 KiB
C

/*
* 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 (rlim_t 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;
}