aboutsummaryrefslogtreecommitdiffstats
path: root/daemon.c
blob: 43230265d2272903bc3074246f59069b6a1dd824 (plain)
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
/*
 * 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 (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 (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;    /* else use /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 */