]> Pileus Git - ~andy/fetchmail/blob - daemon.c
Ready to ship 6-2-0.
[~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 <string.h>
13 #include <sys/types.h>
14 #ifdef HAVE_SYS_WAIT_H
15 #include <sys/wait.h>
16 #endif
17 #ifdef HAVE_FCNTL_H
18 #include <fcntl.h>
19 #else /* !HAVE_FCNTL_H */
20 #ifdef HAVE_SYS_FCNTL_H
21 #include <sys/fcntl.h>
22 #endif /* HAVE_SYS_FCNTL_H */
23 #endif /* !HAVE_FCNTL_H */
24 #include <sys/stat.h>   /* get umask(2) prototyped */
25
26 #if defined(HAVE_UNISTD_H)
27 #include <unistd.h>
28 #endif
29
30 #if defined(STDC_HEADERS)
31 #include <stdlib.h>
32 #endif
33
34 #if defined(QNX)
35 #include <unix.h>
36 #endif
37
38 #if !defined(HAVE_SETSID) && defined(SIGTSTP)
39 #if defined(HAVE_TERMIOS_H)
40 #  include <termios.h>          /* for TIOCNOTTY under Linux */
41 #endif
42
43 #if !defined(TIOCNOTTY) && defined(HAVE_SGTTY_H)
44 #  include <sgtty.h>            /* for TIOCNOTTY under NEXTSTEP */
45 #endif
46 #endif /* !defined(HAVE_SETSID) && defined(SIGTSTP) */
47
48 /* BSD portability hack */
49 #if !defined(SIGCHLD) && defined(SIGCLD)
50 #define SIGCHLD SIGCLD
51 #endif
52
53 #include "fetchmail.h"
54 #include "tunable.h"
55
56 RETSIGTYPE
57 sigchld_handler (int sig)
58 /* process SIGCHLD to obtain the exit code of the terminating process */
59 {
60     extern volatile int lastsig;                /* last signal received */
61     pid_t pid;
62
63 #if     defined(HAVE_WAITPID)                           /* the POSIX way */
64     int status;
65
66     while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
67         continue; /* swallow 'em up. */
68 #elif   defined(HAVE_WAIT3)                             /* the BSD way */
69 #if defined(HAVE_UNION_WAIT) && !defined(__FreeBSD__)
70     union wait status;
71 #else
72     int status;
73 #endif
74
75     while ((pid = wait3(&status, WNOHANG, 0)) > 0)
76         continue; /* swallow 'em up. */
77 #else   /* Zooks! Nothing to do but wait(), and hope we don't block... */
78     int status;
79
80     wait(&status);
81 #endif
82     lastsig = SIGCHLD;
83 }
84
85 RETSIGTYPE null_signal_handler(int sig) { }
86
87 SIGHANDLERTYPE set_signal_handler(int sig, SIGHANDLERTYPE handler)
88 /* 
89  * This function is called by other parts of the program to
90  * setup the signal handler after a change to the signal context.
91  * This is done to improve robustness of the signal handling code.
92  * It has the same prototype as signal(2).
93  */
94 {
95   SIGHANDLERTYPE rethandler;
96 #ifdef HAVE_SIGACTION
97   struct sigaction sa_new, sa_old;
98
99   memset (&sa_new, 0, sizeof sa_new);
100   sigemptyset (&sa_new.sa_mask);
101   sa_new.sa_handler = handler;
102   sa_new.sa_flags = 0;
103 #ifdef SA_RESTART       /* SunOS 4.1 portability hack */
104   /* system call should restart on all signals except SIGALRM */
105   if (sig != SIGALRM)
106       sa_new.sa_flags |= SA_RESTART;
107 #endif
108 #ifdef SA_NOCLDSTOP     /* SunOS 4.1 portability hack */
109   if (sig == SIGCHLD)
110       sa_new.sa_flags |= SA_NOCLDSTOP;
111 #endif
112   sigaction(sig, &sa_new, &sa_old);
113   rethandler = sa_old.sa_handler;
114 #if defined(SIGPWR)
115   if (sig == SIGCHLD)
116      sigaction(SIGPWR, &sa_new, NULL);
117 #endif
118 #else /* HAVE_SIGACTION */
119   rethandler = signal(sig, handler);
120 #if defined(SIGPWR)
121   if (sig == SIGCHLD)
122       signal(SIGPWR, handler);
123 #endif
124   /* system call should restart on all signals except SIGALRM */
125   siginterrupt(sig, sig == SIGALRM);
126 #endif /* HAVE_SIGACTION */
127   return rethandler;
128 }
129
130 void deal_with_sigchld(void)
131 {
132   set_signal_handler(SIGCHLD, sigchld_handler);
133 }
134
135 int
136 daemonize (const char *logfile, void (*termhook)(int))
137 /* detach from control TTY, become process group leader, catch SIGCHLD */
138 {
139   int fd;
140   pid_t childpid;
141
142   /* if we are started by init (process 1) via /etc/inittab we needn't 
143      bother to detach from our process group context */
144
145   if (getppid() == 1) 
146     goto nottyDetach;
147
148   /* Ignore BSD terminal stop signals */
149 #ifdef  SIGTTOU
150   set_signal_handler(SIGTTOU, SIG_IGN);
151 #endif
152 #ifdef  SIGTTIN
153   set_signal_handler(SIGTTIN, SIG_IGN);
154 #endif
155 #ifdef  SIGTSTP
156   set_signal_handler(SIGTSTP, SIG_IGN);
157 #endif
158
159   /* In case we were not started in the background, fork and let
160      the parent exit.  Guarantees that the child is not a process
161      group leader */
162
163   if ((childpid = fork()) < 0) {
164     report(stderr, "fork (%s)\n", strerror(errno));
165     return(PS_IOERR);
166   }
167   else if (childpid > 0) 
168     exit(0);  /* parent */
169
170   
171   /* Make ourselves the leader of a new process group with no
172      controlling terminal */
173
174 #if     defined(HAVE_SETSID)            /* POSIX */
175   /* POSIX makes this soooo easy to do */
176   if (setsid() < 0) {
177     report(stderr, "setsid (%s)\n", strerror(errno));
178     return(PS_IOERR);
179   }
180 #elif   defined(SIGTSTP)                /* BSD */
181   /* change process group */
182 #ifndef __EMX__
183   setpgrp(0, getpid());
184 #endif
185   /* lose controlling tty */
186   if ((fd = open("/dev/tty", O_RDWR)) >= 0) {
187     ioctl(fd, TIOCNOTTY, (char *) 0);
188     close(fd);  /* not checking should be safe, there were no writes */
189   }
190 #else                                   /* SVR3 and older */
191   /* change process group */
192 #ifndef __EMX__
193   setpgrp();
194 #endif
195   
196   /* lose controlling tty */
197   set_signal_handler(SIGHUP, SIG_IGN);
198   if ((childpid = fork()) < 0) {
199     report(stderr, "fork (%s)\n", strerror(errno));
200     return(PS_IOERR);
201   }
202   else if (childpid > 0) {
203     exit(0);    /* parent */
204   }
205 #endif
206
207 nottyDetach:
208
209   /* Close any/all open file descriptors */
210 #if     defined(HAVE_GETDTABLESIZE)
211   for (fd = getdtablesize()-1;  fd >= 0;  fd--)
212 #elif   defined(NOFILE)
213   for (fd = NOFILE-1;  fd >= 0;  fd--)
214 #else           /* make an educated guess */
215   for (fd = 19;  fd >= 0;  fd--)
216 #endif
217   {
218     close(fd);  /* not checking this should be safe, no writes */
219   }
220
221   /* Reopen stdin descriptor on /dev/null */
222   if ((fd = open("/dev/null", O_RDWR)) < 0) {   /* stdin */
223     report(stderr, "open: /dev/null (%s)\n", strerror(errno));
224     return(PS_IOERR);
225   }
226
227   if (logfile)
228   {
229     if ((fd = open(logfile, O_CREAT|O_WRONLY|O_APPEND, 0666)) < 0) {    /* stdout */
230       report(stderr, "open %s (%s)\n", logfile, strerror(errno));
231       return(PS_IOERR);
232     }
233   }
234   else
235   {
236     if (dup(fd) < 0) {                          /* stdout */
237       report(stderr, "dup (%s)\n", strerror(errno));
238       return(PS_IOERR);
239     }
240   }
241   if (dup(fd) < 0) {                            /* stderr */
242     report(stderr, "dup (%s)\n", strerror(errno));
243     return(PS_IOERR);
244   }
245
246 #ifdef HAVE_GETCWD
247   /* move to root directory, so we don't prevent filesystem unmounts */
248   chdir("/");
249 #endif
250
251   /* set our umask to something reasonable (we hope) */
252 #if defined(DEF_UMASK)
253   umask(DEF_UMASK);
254 #else
255   umask(022);
256 #endif
257
258   deal_with_sigchld();
259
260   return(0);
261 }
262
263 flag isafile(int fd)
264 /* is the given fd attached to a file? (used to control logging) */
265 {
266     struct stat stbuf;
267
268     /*
269      * We'd like just to return 1 on (S_IFREG | S_IFBLK),
270      * but weirdly enough, Linux ptys seem to have S_IFBLK
271      * so this test would fail when run on an xterm.
272      */
273     if (isatty(fd) || fstat(fd, &stbuf))
274         return(0);
275     else if (stbuf.st_mode & (S_IFREG))
276         return(1);
277     return(0);
278 }
279
280 /* daemon.c ends here */