]> Pileus Git - ~andy/fetchmail/blob - daemon.c
Fix typo repsonsible -> responsible.
[~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     int status;
37
38     while (waitpid(-1, &status, WNOHANG) > 0)
39         continue; /* swallow 'em up. */
40     lastsig = SIGCHLD;
41     (void)sig;
42 }
43
44 void null_signal_handler(int sig) { (void)sig; }
45
46 SIGHANDLERTYPE set_signal_handler(int sig, SIGHANDLERTYPE handler)
47 /* 
48  * This function is called by other parts of the program to
49  * setup the signal handler after a change to the signal context.
50  * This is done to improve robustness of the signal handling code.
51  * It has the same prototype as signal(2).
52  */
53 {
54   SIGHANDLERTYPE rethandler;
55   struct sigaction sa_new, sa_old;
56
57   memset (&sa_new, 0, sizeof sa_new);
58   sigemptyset (&sa_new.sa_mask);
59   sa_new.sa_handler = handler;
60   sa_new.sa_flags = 0;
61   /* system call should restart on all signals except SIGALRM */
62   if (sig != SIGALRM)
63       sa_new.sa_flags |= SA_RESTART;
64   if (sig == SIGCHLD)
65       sa_new.sa_flags |= SA_NOCLDSTOP;
66   sigaction(sig, &sa_new, &sa_old);
67   rethandler = sa_old.sa_handler;
68 #if defined(SIGPWR)
69   if (sig == SIGCHLD)
70      sigaction(SIGPWR, &sa_new, NULL);
71 #endif
72   return rethandler;
73 }
74
75 void deal_with_sigchld(void)
76 {
77   set_signal_handler(SIGCHLD, sigchld_handler);
78 }
79
80 int
81 daemonize (const char *logfile)
82 /* detach from control TTY, become process group leader, catch SIGCHLD */
83 {
84   int fd, logfd;
85   pid_t childpid;
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   set_signal_handler(SIGTTOU, SIG_IGN);
96 #endif
97 #ifdef  SIGTTIN
98   set_signal_handler(SIGTTIN, SIG_IGN);
99 #endif
100 #ifdef  SIGTSTP
101   set_signal_handler(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     report(stderr, "fork (%s)\n", strerror(errno));
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   /* POSIX makes this soooo easy to do */
120   if (setsid() < 0) {
121     report(stderr, "setsid (%s)\n", strerror(errno));
122     return(PS_IOERR);
123   }
124
125 nottyDetach:
126
127   (void)close(0);
128
129   /* Reopen stdin descriptor on /dev/null */
130   if (open("/dev/null", O_RDWR) < 0) {   /* stdin */
131     report(stderr, "cannot open /dev/null: %s\n", strerror(errno));
132     return(PS_IOERR);
133   }
134
135   if (logfile)
136   {
137       if ((logfd = open(logfile, O_CREAT|O_WRONLY|O_APPEND, 0666)) < 0) {       /* stdout */
138           report(stderr, "cannot open %s: %s\n", logfile, strerror(errno));
139           return PS_IOERR;
140       }
141   } else
142       logfd = 0;    /* else use /dev/null */
143
144   /* Close any/all open file descriptors */
145 #if     defined(HAVE_GETDTABLESIZE)
146   fd = getdtablesize() - 1;
147 #elif   defined(NOFILE)
148   fd = NOFILE - 1;
149 #else           /* make an educated guess */
150   fd = 1023;
151 #endif
152   while (fd >= 1) {
153       if (fd != logfd)
154           close(fd);    /* not checking this should be safe, no writes */
155       -- fd;
156   }
157
158   if (dup(logfd) < 0                                            /* stdout */
159           || ((logfd == 0 || logfd >= 3) && dup(logfd) < 0)) {  /* stderr */
160       report(stderr, "dup (%s)\n", strerror(errno));
161       return(PS_IOERR);
162   }
163
164 #ifdef HAVE_GETCWD
165   /* move to root directory, so we don't prevent filesystem unmounts */
166   chdir("/");
167 #endif
168
169   /* set our umask to something reasonable (we hope) */
170 #if defined(DEF_UMASK)
171   umask(DEF_UMASK);
172 #else
173   umask(022);
174 #endif
175
176   deal_with_sigchld();
177
178   return(0);
179 }
180
181 flag is_a_file(int fd)
182 /* is the given fd attached to a file? (used to control logging) */
183 {
184     struct stat stbuf;
185
186     /*
187      * We'd like just to return 1 on (S_IFREG | S_IFBLK),
188      * but weirdly enough, Linux ptys seem to have S_IFBLK
189      * so this test would fail when run on an xterm.
190      */
191     if (isatty(fd) || fstat(fd, &stbuf))
192         return(0);
193     else if (stbuf.st_mode & (S_IFREG))
194         return(1);
195     return(0);
196 }
197
198 /* daemon.c ends here */