1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
|
/*
* daemon.c -- turn a process into a daemon under POSIX, SYSV, BSD.
*
* For license terms, see the file COPYING in this directory.
*/
#include "config.h"
#include <stdio.h>
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <sys/types.h>
#ifdef HAVE_SYS_WAIT_H
#include <sys/wait.h>
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#else /* !HAVE_FCNTL_H */
#ifdef HAVE_SYS_FCNTL_H
#include <sys/fcntl.h>
#endif /* HAVE_SYS_FCNTL_H */
#endif /* !HAVE_FCNTL_H */
#include <sys/stat.h> /* get umask(2) prototyped */
#if defined(HAVE_UNISTD_H)
#include <unistd.h>
#endif
#if defined(STDC_HEADERS)
#include <stdlib.h>
#endif
#if defined(QNX)
#include <unix.h>
#endif
#if !defined(HAVE_SETSID) && defined(SIGTSTP)
#if defined(HAVE_TERMIOS_H)
# include <termios.h> /* for TIOCNOTTY under Linux */
#endif
#if !defined(TIOCNOTTY) && defined(HAVE_SGTTY_H)
# include <sgtty.h> /* for TIOCNOTTY under NEXTSTEP */
#endif
#endif /* !defined(HAVE_SETSID) && defined(SIGTSTP) */
/* BSD portability hack */
#if !defined(SIGCHLD) && defined(SIGCLD)
#define SIGCHLD SIGCLD
#endif
#include "fetchmail.h"
#include "tunable.h"
static RETSIGTYPE
sigchld_handler (int sig)
/* process SIGCHLD to obtain the exit code of the terminating process */
{
pid_t pid;
#if defined(HAVE_WAITPID) /* the POSIX way */
int status;
while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
continue; /* swallow 'em up. */
#elif defined(HAVE_WAIT3) /* the BSD way */
#if defined(HAVE_UNION_WAIT) && !defined(__FreeBSD__)
union wait status;
#else
int status;
#endif
while ((pid = wait3(&status, WNOHANG, 0)) > 0)
continue; /* swallow 'em up. */
#else /* Zooks! Nothing to do but wait(), and hope we don't block... */
int status;
wait(&status);
#endif
lastsig = SIGCHLD;
(void)sig;
}
RETSIGTYPE null_signal_handler(int sig) { (void)sig; }
SIGHANDLERTYPE set_signal_handler(int sig, SIGHANDLERTYPE handler)
/*
* This function is called by other parts of the program to
* setup the signal handler after a change to the signal context.
* This is done to improve robustness of the signal handling code.
* It has the same prototype as signal(2).
*/
{
SIGHANDLERTYPE rethandler;
#ifdef HAVE_SIGACTION
struct sigaction sa_new, sa_old;
memset (&sa_new, 0, sizeof sa_new);
sigemptyset (&sa_new.sa_mask);
sa_new.sa_handler = handler;
sa_new.sa_flags = 0;
#ifdef SA_RESTART /* SunOS 4.1 portability hack */
/* system call should restart on all signals except SIGALRM */
if (sig != SIGALRM)
sa_new.sa_flags |= SA_RESTART;
#endif
#ifdef SA_NOCLDSTOP /* SunOS 4.1 portability hack */
if (sig == SIGCHLD)
sa_new.sa_flags |= SA_NOCLDSTOP;
#endif
sigaction(sig, &sa_new, &sa_old);
rethandler = sa_old.sa_handler;
#if defined(SIGPWR)
if (sig == SIGCHLD)
sigaction(SIGPWR, &sa_new, NULL);
#endif
#else /* HAVE_SIGACTION */
rethandler = signal(sig, handler);
#if defined(SIGPWR)
if (sig == SIGCHLD)
signal(SIGPWR, handler);
#endif
/* system call should restart on all signals except SIGALRM */
siginterrupt(sig, sig == SIGALRM);
#endif /* HAVE_SIGACTION */
return rethandler;
}
void deal_with_sigchld(void)
{
set_signal_handler(SIGCHLD, sigchld_handler);
}
int
daemonize (const char *logfile)
/* detach from control TTY, become process group leader, catch SIGCHLD */
{
int fd, logfd;
pid_t childpid;
/* if we are started by init (process 1) via /etc/inittab we needn't
bother to detach from our process group context */
if (getppid() == 1)
goto nottyDetach;
/* Ignore BSD terminal stop signals */
#ifdef SIGTTOU
set_signal_handler(SIGTTOU, SIG_IGN);
#endif
#ifdef SIGTTIN
set_signal_handler(SIGTTIN, SIG_IGN);
#endif
#ifdef SIGTSTP
set_signal_handler(SIGTSTP, SIG_IGN);
#endif
/* In case we were not started in the background, fork and let
the parent exit. Guarantees that the child is not a process
group leader */
if ((childpid = fork()) < 0) {
report(stderr, "fork (%s)\n", strerror(errno));
return(PS_IOERR);
}
else if (childpid > 0)
exit(0); /* parent */
/* Make ourselves the leader of a new process group with no
controlling terminal */
#if defined(HAVE_SETSID) /* POSIX */
/* POSIX makes this soooo easy to do */
if (setsid() < 0) {
report(stderr, "setsid (%s)\n", strerror(errno));
return(PS_IOERR);
}
#elif defined(SIGTSTP) /* BSD */
/* change process group */
#ifndef __EMX__
setpgrp(0, getpid());
#endif
/* lose controlling tty */
if ((fd = open("/dev/tty", O_RDWR)) >= 0) {
ioctl(fd, TIOCNOTTY, (char *) 0);
close(fd); /* not checking should be safe, there were no writes */
}
#else /* SVR3 and older */
/* change process group */
#ifndef __EMX__
setpgrp();
#endif
/* lose controlling tty */
set_signal_handler(SIGHUP, SIG_IGN);
if ((childpid = fork()) < 0) {
report(stderr, "fork (%s)\n", strerror(errno));
return(PS_IOERR);
}
else if (childpid > 0) {
exit(0); /* parent */
}
#endif
nottyDetach:
(void)close(0);
/* Reopen stdin descriptor on /dev/null */
if ((fd = open("/dev/null", O_RDWR)) < 0) { /* stdin */
report(stderr, "cannot open /dev/null: %s\n", strerror(errno));
return(PS_IOERR);
}
if (logfile)
{
if ((logfd = open(logfile, O_CREAT|O_WRONLY|O_APPEND, 0666)) < 0) { /* stdout */
report(stderr, "cannot open %s: %s\n", logfile, strerror(errno));
return PS_IOERR;
} else
logfd = 0; /* use /dev/null */
} else
logfd = 0; /* this is /dev/null */
/* Close any/all open file descriptors */
#if defined(HAVE_GETDTABLESIZE)
fd = getdtablesize() - 1;
#elif defined(NOFILE)
fd = NOFILE - 1;
#else /* make an educated guess */
fd = 1023;
#endif
while (fd >= 1) {
if (fd != logfd)
close(fd); /* not checking this should be safe, no writes */
-- fd;
}
if (dup(logfd) < 0 /* stdout */
|| ((logfd == 0 || logfd >= 3) && dup(logfd) < 0)) { /* stderr */
report(stderr, "dup (%s)\n", strerror(errno));
return(PS_IOERR);
}
#ifdef HAVE_GETCWD
/* move to root directory, so we don't prevent filesystem unmounts */
chdir("/");
#endif
/* set our umask to something reasonable (we hope) */
#if defined(DEF_UMASK)
umask(DEF_UMASK);
#else
umask(022);
#endif
deal_with_sigchld();
return(0);
}
flag is_a_file(int fd)
/* is the given fd attached to a file? (used to control logging) */
{
struct stat stbuf;
/*
* We'd like just to return 1 on (S_IFREG | S_IFBLK),
* but weirdly enough, Linux ptys seem to have S_IFBLK
* so this test would fail when run on an xterm.
*/
if (isatty(fd) || fstat(fd, &stbuf))
return(0);
else if (stbuf.st_mode & (S_IFREG))
return(1);
return(0);
}
/* daemon.c ends here */
|