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