daemon.c 6.66 KB
Newer Older
1
/*
Eric S. Raymond's avatar
Eric S. Raymond committed
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.
Eric S. Raymond's avatar
Eric S. Raymond committed
5 6
 */

7
#include "config.h"
Eric S. Raymond's avatar
Eric S. Raymond committed
8 9

#include <stdio.h>
10
#include <errno.h>
Eric S. Raymond's avatar
Eric S. Raymond committed
11
#include <signal.h>
12
#include <string.h>
Eric S. Raymond's avatar
Eric S. Raymond committed
13
#include <sys/types.h>
14
#ifdef HAVE_SYS_WAIT_H
Eric S. Raymond's avatar
Eric S. Raymond committed
15
#include <sys/wait.h>
16
#endif
Eric S. Raymond's avatar
Eric S. Raymond committed
17 18
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
19
#else /* !HAVE_FCNTL_H */
Eric S. Raymond's avatar
Eric S. Raymond committed
20 21 22
#ifdef HAVE_SYS_FCNTL_H
#include <sys/fcntl.h>
#endif /* HAVE_SYS_FCNTL_H */
23
#endif /* !HAVE_FCNTL_H */
Eric S. Raymond's avatar
Eric S. Raymond committed
24
#include <sys/stat.h>	/* get umask(2) prototyped */
Eric S. Raymond's avatar
Eric S. Raymond committed
25 26

#if defined(HAVE_UNISTD_H)
Eric S. Raymond's avatar
Eric S. Raymond committed
27
#include <unistd.h>
Eric S. Raymond's avatar
Eric S. Raymond committed
28 29
#endif

Eric S. Raymond's avatar
Eric S. Raymond committed
30 31 32 33
#if defined(STDC_HEADERS)
#include <stdlib.h>
#endif

Eric S. Raymond's avatar
Eric S. Raymond committed
34
#if defined(QNX)
Eric S. Raymond's avatar
Eric S. Raymond committed
35
#include <unix.h>
Eric S. Raymond's avatar
Eric S. Raymond committed
36 37
#endif

Eric S. Raymond's avatar
Eric S. Raymond committed
38
#if !defined(HAVE_SETSID) && defined(SIGTSTP)
39 40 41 42
#if defined(HAVE_TERMIOS_H)
#  include <termios.h>		/* for TIOCNOTTY under Linux */
#endif

43
#if !defined(TIOCNOTTY) && defined(HAVE_SGTTY_H)
Eric S. Raymond's avatar
Eric S. Raymond committed
44 45
#  include <sgtty.h>		/* for TIOCNOTTY under NEXTSTEP */
#endif
Eric S. Raymond's avatar
Eric S. Raymond committed
46
#endif /* !defined(HAVE_SETSID) && defined(SIGTSTP) */
Eric S. Raymond's avatar
Eric S. Raymond committed
47

Eric S. Raymond's avatar
Eric S. Raymond committed
48
/* BSD portability hack */
49
#if !defined(SIGCHLD) && defined(SIGCLD)
Eric S. Raymond's avatar
Eric S. Raymond committed
50
#define SIGCHLD	SIGCLD
Eric S. Raymond's avatar
Eric S. Raymond committed
51
#endif
Eric S. Raymond's avatar
Eric S. Raymond committed
52

Eric S. Raymond's avatar
Eric S. Raymond committed
53
#include "fetchmail.h"
54
#include "tunable.h"
Eric S. Raymond's avatar
Eric S. Raymond committed
55

56
static RETSIGTYPE
Eric S. Raymond's avatar
Eric S. Raymond committed
57
sigchld_handler (int sig)
Eric S. Raymond's avatar
Eric S. Raymond committed
58
/* process SIGCHLD to obtain the exit code of the terminating process */
Eric S. Raymond's avatar
Eric S. Raymond committed
59
{
60 61 62
#if 	defined(HAVE_WAITPID)				/* the POSIX way */
    int status;

63
    while (waitpid(-1, &status, WNOHANG) > 0)
64 65
	continue; /* swallow 'em up. */
#elif 	defined(HAVE_WAIT3)				/* the BSD way */
66
    pid_t pid;
Eric S. Raymond's avatar
Eric S. Raymond committed
67
#if defined(HAVE_UNION_WAIT) && !defined(__FreeBSD__)
68
    union wait status;
Eric S. Raymond's avatar
Eric S. Raymond committed
69
#else
70
    int status;
Eric S. Raymond's avatar
Eric S. Raymond committed
71 72
#endif

73 74
    while ((pid = wait3(&status, WNOHANG, 0)) > 0)
	continue; /* swallow 'em up. */
Eric S. Raymond's avatar
Eric S. Raymond committed
75
#else	/* Zooks! Nothing to do but wait(), and hope we don't block... */
76 77
    int status;

78
    wait(&status);
Eric S. Raymond's avatar
Eric S. Raymond committed
79
#endif
80
    lastsig = SIGCHLD;
81
    (void)sig;
Eric S. Raymond's avatar
Eric S. Raymond committed
82 83
}

84
RETSIGTYPE null_signal_handler(int sig) { (void)sig; }
Eric S. Raymond's avatar
Eric S. Raymond committed
85 86

SIGHANDLERTYPE set_signal_handler(int sig, SIGHANDLERTYPE handler)
87 88
/* 
 * This function is called by other parts of the program to
Eric S. Raymond's avatar
Eric S. Raymond committed
89
 * setup the signal handler after a change to the signal context.
90
 * This is done to improve robustness of the signal handling code.
Eric S. Raymond's avatar
Eric S. Raymond committed
91
 * It has the same prototype as signal(2).
92
 */
Eric S. Raymond's avatar
Eric S. Raymond committed
93
{
Eric S. Raymond's avatar
Eric S. Raymond committed
94
  SIGHANDLERTYPE rethandler;
Eric S. Raymond's avatar
Eric S. Raymond committed
95
#ifdef HAVE_SIGACTION
Eric S. Raymond's avatar
Eric S. Raymond committed
96
  struct sigaction sa_new, sa_old;
Eric S. Raymond's avatar
Eric S. Raymond committed
97 98 99

  memset (&sa_new, 0, sizeof sa_new);
  sigemptyset (&sa_new.sa_mask);
Eric S. Raymond's avatar
Eric S. Raymond committed
100 101
  sa_new.sa_handler = handler;
  sa_new.sa_flags = 0;
102
#ifdef SA_RESTART	/* SunOS 4.1 portability hack */
Eric S. Raymond's avatar
Eric S. Raymond committed
103 104 105
  /* system call should restart on all signals except SIGALRM */
  if (sig != SIGALRM)
      sa_new.sa_flags |= SA_RESTART;
106
#endif
Eric S. Raymond's avatar
Eric S. Raymond committed
107 108 109 110 111 112
#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;
Eric S. Raymond's avatar
Eric S. Raymond committed
113
#if defined(SIGPWR)
Eric S. Raymond's avatar
Eric S. Raymond committed
114 115
  if (sig == SIGCHLD)
     sigaction(SIGPWR, &sa_new, NULL);
Eric S. Raymond's avatar
Eric S. Raymond committed
116
#endif
Eric S. Raymond's avatar
Eric S. Raymond committed
117
#else /* HAVE_SIGACTION */
Eric S. Raymond's avatar
Eric S. Raymond committed
118
  rethandler = signal(sig, handler);
Eric S. Raymond's avatar
Eric S. Raymond committed
119
#if defined(SIGPWR)
Eric S. Raymond's avatar
Eric S. Raymond committed
120 121
  if (sig == SIGCHLD)
      signal(SIGPWR, handler);
Eric S. Raymond's avatar
Eric S. Raymond committed
122
#endif
Eric S. Raymond's avatar
Eric S. Raymond committed
123 124
  /* system call should restart on all signals except SIGALRM */
  siginterrupt(sig, sig == SIGALRM);
Eric S. Raymond's avatar
Eric S. Raymond committed
125
#endif /* HAVE_SIGACTION */
Eric S. Raymond's avatar
Eric S. Raymond committed
126 127 128 129 130 131
  return rethandler;
}

void deal_with_sigchld(void)
{
  set_signal_handler(SIGCHLD, sigchld_handler);
Eric S. Raymond's avatar
Eric S. Raymond committed
132 133
}

Eric S. Raymond's avatar
Eric S. Raymond committed
134
int
135
daemonize (const char *logfile)
136
/* detach from control TTY, become process group leader, catch SIGCHLD */
Eric S. Raymond's avatar
Eric S. Raymond committed
137
{
138
  int fd, logfd;
Eric S. Raymond's avatar
Eric S. Raymond committed
139 140 141 142 143 144 145 146 147 148
  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
Eric S. Raymond's avatar
Eric S. Raymond committed
149
  set_signal_handler(SIGTTOU, SIG_IGN);
Eric S. Raymond's avatar
Eric S. Raymond committed
150 151
#endif
#ifdef	SIGTTIN
Eric S. Raymond's avatar
Eric S. Raymond committed
152
  set_signal_handler(SIGTTIN, SIG_IGN);
Eric S. Raymond's avatar
Eric S. Raymond committed
153 154
#endif
#ifdef	SIGTSTP
Eric S. Raymond's avatar
Eric S. Raymond committed
155
  set_signal_handler(SIGTSTP, SIG_IGN);
Eric S. Raymond's avatar
Eric S. Raymond committed
156 157 158 159 160 161 162
#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) {
163
    report(stderr, "fork (%s)\n", strerror(errno));
Eric S. Raymond's avatar
Eric S. Raymond committed
164 165 166 167 168 169 170 171 172 173 174 175
    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) {
176
    report(stderr, "setsid (%s)\n", strerror(errno));
Eric S. Raymond's avatar
Eric S. Raymond committed
177 178 179 180
    return(PS_IOERR);
  }
#elif	defined(SIGTSTP)		/* BSD */
  /* change process group */
Eric S. Raymond's avatar
Eric S. Raymond committed
181
#ifndef __EMX__
Eric S. Raymond's avatar
Eric S. Raymond committed
182
  setpgrp(0, getpid());
Eric S. Raymond's avatar
Eric S. Raymond committed
183
#endif
Eric S. Raymond's avatar
Eric S. Raymond committed
184 185 186
  /* lose controlling tty */
  if ((fd = open("/dev/tty", O_RDWR)) >= 0) {
    ioctl(fd, TIOCNOTTY, (char *) 0);
Eric S. Raymond's avatar
Eric S. Raymond committed
187
    close(fd);	/* not checking should be safe, there were no writes */
Eric S. Raymond's avatar
Eric S. Raymond committed
188 189 190
  }
#else					/* SVR3 and older */
  /* change process group */
Eric S. Raymond's avatar
Eric S. Raymond committed
191
#ifndef __EMX__
Eric S. Raymond's avatar
Eric S. Raymond committed
192
  setpgrp();
Eric S. Raymond's avatar
Eric S. Raymond committed
193
#endif
Eric S. Raymond's avatar
Eric S. Raymond committed
194 195
  
  /* lose controlling tty */
Eric S. Raymond's avatar
Eric S. Raymond committed
196
  set_signal_handler(SIGHUP, SIG_IGN);
197
  if ((childpid = fork()) < 0) {
Eric S. Raymond's avatar
Eric S. Raymond committed
198
    report(stderr, "fork (%s)\n", strerror(errno));
Eric S. Raymond's avatar
Eric S. Raymond committed
199 200 201 202 203 204 205 206 207
    return(PS_IOERR);
  }
  else if (childpid > 0) {
    exit(0); 	/* parent */
  }
#endif

nottyDetach:

208
  (void)close(0);
Eric S. Raymond's avatar
Eric S. Raymond committed
209 210

  /* Reopen stdin descriptor on /dev/null */
211
  if (open("/dev/null", O_RDWR) < 0) {   /* stdin */
212
    report(stderr, "cannot open /dev/null: %s\n", strerror(errno));
Eric S. Raymond's avatar
Eric S. Raymond committed
213 214 215
    return(PS_IOERR);
  }

216
  if (logfile)
Eric S. Raymond's avatar
Eric S. Raymond committed
217
  {
218 219 220
      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;
221
      }
222
  } else
223
      logfd = 0;    /* else use /dev/null */
224 225 226 227 228 229 230 231 232 233 234 235 236

  /* 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;
Eric S. Raymond's avatar
Eric S. Raymond committed
237
  }
238 239 240

  if (dup(logfd) < 0						/* stdout */
	  || ((logfd == 0 || logfd >= 3) && dup(logfd) < 0)) {	/* stderr */
Matthias Andree's avatar
Matthias Andree committed
241
      report(stderr, "dup(): %s\n", strerror(errno));
242
      return(PS_IOERR);
Eric S. Raymond's avatar
Eric S. Raymond committed
243 244 245
  }

  /* move to root directory, so we don't prevent filesystem unmounts */
Matthias Andree's avatar
Matthias Andree committed
246 247 248 249
  if (chdir("/")) {
	  report(stderr, "chdir(\"/\"): %s\n", strerror(errno));
	  return PS_IOERR;
  }
Eric S. Raymond's avatar
Eric S. Raymond committed
250 251 252 253 254 255 256 257

  /* set our umask to something reasonable (we hope) */
#if defined(DEF_UMASK)
  umask(DEF_UMASK);
#else
  umask(022);
#endif

Eric S. Raymond's avatar
Eric S. Raymond committed
258
  deal_with_sigchld();
Eric S. Raymond's avatar
Eric S. Raymond committed
259 260

  return(0);
Eric S. Raymond's avatar
Eric S. Raymond committed
261
}
262

263
flag is_a_file(int fd)
264 265 266 267
/* is the given fd attached to a file? (used to control logging) */
{
    struct stat stbuf;

Eric S. Raymond's avatar
Eric S. Raymond committed
268 269 270 271 272 273
    /*
     * 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))
274
	return(0);
Eric S. Raymond's avatar
Eric S. Raymond committed
275
    else if (stbuf.st_mode & (S_IFREG))
276 277 278 279
	return(1);
    return(0);
}

280
/* daemon.c ends here */