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