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