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