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