2 * daemon.c -- turn a process into a daemon under POSIX, SYSV, BSD.
4 * For license terms, see the file COPYING in this directory.
13 #include <sys/types.h>
16 #include <sys/stat.h> /* get umask(2) prototyped */
22 #include <termios.h> /* for TIOCNOTTY under Linux */
24 /* BSD portability hack */
25 #if !defined(SIGCHLD) && defined(SIGCLD)
26 #define SIGCHLD SIGCLD
29 #include "fetchmail.h"
33 sigchld_handler (int sig)
34 /* process SIGCHLD to obtain the exit code of the terminating process */
38 while (waitpid(-1, &status, WNOHANG) > 0)
39 continue; /* swallow 'em up. */
44 void null_signal_handler(int sig) { (void)sig; }
46 SIGHANDLERTYPE set_signal_handler(int sig, SIGHANDLERTYPE handler)
48 * This function is called by other parts of the program to
49 * setup the signal handler after a change to the signal context.
50 * This is done to improve robustness of the signal handling code.
51 * It has the same prototype as signal(2).
54 SIGHANDLERTYPE rethandler;
55 struct sigaction sa_new, sa_old;
57 memset (&sa_new, 0, sizeof sa_new);
58 sigemptyset (&sa_new.sa_mask);
59 sa_new.sa_handler = handler;
61 /* system call should restart on all signals except SIGALRM */
63 sa_new.sa_flags |= SA_RESTART;
65 sa_new.sa_flags |= SA_NOCLDSTOP;
66 sigaction(sig, &sa_new, &sa_old);
67 rethandler = sa_old.sa_handler;
70 sigaction(SIGPWR, &sa_new, NULL);
75 void deal_with_sigchld(void)
77 set_signal_handler(SIGCHLD, sigchld_handler);
81 daemonize (const char *logfile)
82 /* detach from control TTY, become process group leader, catch SIGCHLD */
87 /* if we are started by init (process 1) via /etc/inittab we needn't
88 bother to detach from our process group context */
93 /* Ignore BSD terminal stop signals */
95 set_signal_handler(SIGTTOU, SIG_IGN);
98 set_signal_handler(SIGTTIN, SIG_IGN);
101 set_signal_handler(SIGTSTP, SIG_IGN);
104 /* In case we were not started in the background, fork and let
105 the parent exit. Guarantees that the child is not a process
108 if ((childpid = fork()) < 0) {
109 report(stderr, "fork (%s)\n", strerror(errno));
112 else if (childpid > 0)
113 exit(0); /* parent */
116 /* Make ourselves the leader of a new process group with no
117 controlling terminal */
119 /* POSIX makes this soooo easy to do */
121 report(stderr, "setsid (%s)\n", strerror(errno));
129 /* Reopen stdin descriptor on /dev/null */
130 if (open("/dev/null", O_RDWR) < 0) { /* stdin */
131 report(stderr, "cannot open /dev/null: %s\n", strerror(errno));
137 if ((logfd = open(logfile, O_CREAT|O_WRONLY|O_APPEND, 0666)) < 0) { /* stdout */
138 report(stderr, "cannot open %s: %s\n", logfile, strerror(errno));
142 logfd = 0; /* else use /dev/null */
144 /* Close any/all open file descriptors */
145 #if defined(HAVE_GETDTABLESIZE)
146 fd = getdtablesize() - 1;
147 #elif defined(NOFILE)
149 #else /* make an educated guess */
154 close(fd); /* not checking this should be safe, no writes */
158 if (dup(logfd) < 0 /* stdout */
159 || ((logfd == 0 || logfd >= 3) && dup(logfd) < 0)) { /* stderr */
160 report(stderr, "dup (%s)\n", strerror(errno));
165 /* move to root directory, so we don't prevent filesystem unmounts */
169 /* set our umask to something reasonable (we hope) */
170 #if defined(DEF_UMASK)
181 flag is_a_file(int fd)
182 /* is the given fd attached to a file? (used to control logging) */
187 * We'd like just to return 1 on (S_IFREG | S_IFBLK),
188 * but weirdly enough, Linux ptys seem to have S_IFBLK
189 * so this test would fail when run on an xterm.
191 if (isatty(fd) || fstat(fd, &stbuf))
193 else if (stbuf.st_mode & (S_IFREG))
198 /* daemon.c ends here */