]> Pileus Git - ~andy/fetchmail/blob - daemon.c
Jonathan T. Agnew's massive code cleanup.
[~andy/fetchmail] / daemon.c
1 /*
2  * daemon.c -- turn a process into a daemon under POSIX, SYSV, BSD.
3  *
4  * For license terms, see the file COPYING in this directory.
5  */
6
7 #include "config.h"
8
9 #include <stdio.h>
10 #include <errno.h>
11 #include <signal.h>
12 #include <sys/types.h>
13 #ifdef HAVE_SYS_WAIT_H
14 #include <sys/wait.h>
15 #endif
16 #ifdef HAVE_FCNTL_H
17 #include <fcntl.h>
18 #else /* !HAVE_FCNTL_H */
19 #ifdef HAVE_SYS_FCNTL_H
20 #include <sys/fcntl.h>
21 #endif /* HAVE_SYS_FCNTL_H */
22 #endif /* !HAVE_FCNTL_H */
23 #include <sys/stat.h>   /* get umask(2) prototyped */
24
25 #if defined(HAVE_UNISTD_H)
26 #include <unistd.h>
27 #endif
28
29 #if defined(STDC_HEADERS)
30 #include <stdlib.h>
31 #endif
32
33 #if defined(QNX)
34 #include <unix.h>
35 #endif
36
37 #if !defined(HAVE_SETSID) && defined(SIGTSTP)
38 #if defined(HAVE_TERMIOS_H)
39 #  include <termios.h>          /* for TIOCNOTTY under Linux */
40 #endif
41
42 #if !defined(TIOCNOTTY) && defined(HAVE_SGTTY_H)
43 #  include <sgtty.h>            /* for TIOCNOTTY under NEXTSTEP */
44 #endif
45 #endif /* !defined(HAVE_SETSID) && defined(SIGTSTP) */
46
47 /* BSD portability hack */
48 #if !defined(SIGCHLD) && defined(SIGCLD)
49 #define SIGCHLD SIGCLD
50 #endif
51
52 #include "fetchmail.h"
53 #include "tunable.h"
54
55 RETSIGTYPE
56 sigchld_handler (int sig)
57 /* process SIGCHLD to obtain the exit code of the terminating process */
58 {
59     pid_t pid;
60
61 #if     defined(HAVE_WAITPID)                           /* the POSIX way */
62     int status;
63
64     while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
65         continue; /* swallow 'em up. */
66 #elif   defined(HAVE_WAIT3)                             /* the BSD way */
67 #if defined(HAVE_UNION_WAIT) && !defined(__FreeBSD__)
68     union wait status;
69 #else
70     int status;
71 #endif
72
73     while ((pid = wait3(&status, WNOHANG, 0)) > 0)
74         continue; /* swallow 'em up. */
75 #else   /* Zooks! Nothing to do but wait(), and hope we don't block... */
76     int status;
77
78     wait(&status);
79 #endif
80 }
81
82 int
83 daemonize (const char *logfile, void (*termhook)(int))
84 /* detach from control TTY, become process group leader, catch SIGCHLD */
85 {
86   int fd;
87   pid_t childpid;
88   RETSIGTYPE sigchld_handler(int);
89
90   /* if we are started by init (process 1) via /etc/inittab we needn't 
91      bother to detach from our process group context */
92
93   if (getppid() == 1) 
94     goto nottyDetach;
95
96   /* Ignore BSD terminal stop signals */
97 #ifdef  SIGTTOU
98   signal(SIGTTOU, SIG_IGN);
99 #endif
100 #ifdef  SIGTTIN
101   signal(SIGTTIN, SIG_IGN);
102 #endif
103 #ifdef  SIGTSTP
104   signal(SIGTSTP, SIG_IGN);
105 #endif
106
107   /* In case we were not started in the background, fork and let
108      the parent exit.  Guarantees that the child is not a process
109      group leader */
110
111   if ((childpid = fork()) < 0) {
112     error(0, errno, "fork");
113     return(PS_IOERR);
114   }
115   else if (childpid > 0) 
116     exit(0);  /* parent */
117
118   
119   /* Make ourselves the leader of a new process group with no
120      controlling terminal */
121
122 #if     defined(HAVE_SETSID)            /* POSIX */
123   /* POSIX makes this soooo easy to do */
124   if (setsid() < 0) {
125     error(0, errno, "setsid");
126     return(PS_IOERR);
127   }
128 #elif   defined(SIGTSTP)                /* BSD */
129   /* change process group */
130 #ifndef __EMX__
131   setpgrp(0, getpid());
132 #endif
133   /* lose controlling tty */
134   if ((fd = open("/dev/tty", O_RDWR)) >= 0) {
135     ioctl(fd, TIOCNOTTY, (char *) 0);
136     close(fd);
137   }
138 #else                                   /* SVR3 and older */
139   /* change process group */
140 #ifndef __EMX__
141   setpgrp();
142 #endif
143   
144   /* lose controlling tty */
145   signal(SIGHUP, SIG_IGN);
146   if ((childpid = fork()) < 0) {
147     error(0, errno, "fork");
148     return(PS_IOERR);
149   }
150   else if (childpid > 0) {
151     exit(0);    /* parent */
152   }
153 #endif
154
155 nottyDetach:
156
157   /* Close any/all open file descriptors */
158 #if     defined(HAVE_GETDTABLESIZE)
159   for (fd = getdtablesize()-1;  fd >= 0;  fd--)
160 #elif   defined(NOFILE)
161   for (fd = NOFILE-1;  fd >= 0;  fd--)
162 #else           /* make an educated guess */
163   for (fd = 19;  fd >= 0;  fd--)
164 #endif
165   {
166     close(fd);
167   }
168
169   /* Reopen stdin descriptor on /dev/null */
170   if ((fd = open("/dev/null", O_RDWR)) < 0) {   /* stdin */
171     error(0, errno, "open: /dev/null");
172     return(PS_IOERR);
173   }
174
175   if (logfile)
176     fd = open(logfile, O_CREAT|O_WRONLY|O_APPEND, 0666);        /* stdout */
177   else
178     if (dup(fd) < 0) {                          /* stdout */
179       error(0, errno, "dup");
180       return(PS_IOERR);
181     }
182   if (dup(fd) < 0) {                            /* stderr */
183     error(0, errno, "dup");
184     return(PS_IOERR);
185   }
186
187   /* move to root directory, so we don't prevent filesystem unmounts */
188   chdir("/");
189
190   /* set our umask to something reasonable (we hope) */
191 #if defined(DEF_UMASK)
192   umask(DEF_UMASK);
193 #else
194   umask(022);
195 #endif
196
197   /* set up to catch child process termination signals */ 
198   signal(SIGCHLD, sigchld_handler); 
199 #if defined(SIGPWR)
200   signal(SIGPWR, sigchld_handler); 
201 #endif
202
203   return(0);
204 }
205
206 /* daemon.c ends here */