X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;f=src%2Fsrc%2Fdaemon.c;h=caf8cb24e08cb70da9a9b7195beed3bc83b8e71b;hb=54a4fbd526353c5ec1ce25ebe9c3b48f05b1e5be;hp=d9375eabf2df79b7c1f6e4b82dedff24973b651a;hpb=059ec3d9952740285fb1ebf47961b8aca2eb1b4a;p=exim.git diff --git a/src/src/daemon.c b/src/src/daemon.c index d9375eabf..caf8cb24e 100644 --- a/src/src/daemon.c +++ b/src/src/daemon.c @@ -1,10 +1,10 @@ -/* $Cambridge: exim/src/src/daemon.c,v 1.1 2004/10/07 10:39:01 ph10 Exp $ */ +/* $Cambridge: exim/src/src/daemon.c,v 1.7 2005/01/27 15:15:30 ph10 Exp $ */ /************************************************* * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2004 */ +/* Copyright (c) University of Cambridge 1995 - 2005 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions concerned with running Exim as a daemon */ @@ -84,8 +84,8 @@ static void main_sigchld_handler(int sig) { sig = sig; /* Keep picky compilers happy */ +os_non_restarting_signal(SIGCHLD, SIG_DFL); sigchld_seen = TRUE; -signal(SIGCHLD, SIG_DFL); } @@ -362,6 +362,7 @@ if (pid == 0) int i; int queue_only_reason = 0; int old_pool = store_pool; + int save_debug_selector = debug_selector; BOOL local_queue_only; #ifdef SA_NOCLDWAIT struct sigaction act; @@ -432,15 +433,26 @@ if (pid == 0) /* Attempt to get an id from the sending machine via the RFC 1413 protocol. We do this in the sub-process in order not to hold up the main process if there is any delay. Then set up the fullhost information - in case there is no HELO/EHLO. */ - + in case there is no HELO/EHLO. + + If debugging is enabled only for the daemon, we must turn if off while + finding the id, but turn it on again afterwards so that information about the + incoming connection is output. */ + + if (debug_daemon) debug_selector = 0; verify_get_ident(IDENT_PORT); host_build_sender_fullhost(); + debug_selector = save_debug_selector; DEBUG(D_any) debug_printf("Process %d is handling incoming connection from %s\n", (int)getpid(), sender_fullhost); + /* Now disable debugging permanently if it's required only for the daemon + process. */ + + if (debug_daemon) debug_selector = 0; + /* If there are too many child processes for immediate delivery, set the local_queue_only flag, which is initialized from the configured value and may therefore already be TRUE. Leave logging @@ -886,7 +898,9 @@ if (daemon_listen) order to perform an "open" on the kernel memory file). */ #ifdef LOAD_AVG_NEEDS_ROOT - if (queue_only_load >= 0 || smtp_load_reserve >= 0) (void)os_getloadavg(); + if (queue_only_load >= 0 || smtp_load_reserve >= 0 || + (deliver_queue_load_max >= 0 && deliver_drop_privilege)) + (void)os_getloadavg(); #endif /* If -oX was used, disable the writing of a pid file unless -oP was @@ -1110,26 +1124,35 @@ if (daemon_listen) } } -/* We now close all open file descriptors that we know about, and disconnect -from the controlling terminal, unless background_daemon is unset. This is -always unset when debugging, but can also be forced. Most modern Unixes seem to -have setsid() for getting rid of the controlling terminal. For any OS that -doesn't, setsid() can be #defined as a no-op, or as something else. */ +/* The variable background_daemon is always false when debugging, but +can also be forced false in order to keep a non-debugging daemon in the +foreground. If background_daemon is true, close all open file descriptors that +we know about, but then re-open stdin, stdout, and stderr to /dev/null. + +This is protection against any called functions (in libraries, or in +Perl, or whatever) that think they can write to stderr (or stdout). Before this +was added, it was quite likely that an SMTP connection would use one of these +file descriptors, in which case writing random stuff to it caused chaos. + +Then disconnect from the controlling terminal, Most modern Unixes seem to have +setsid() for getting rid of the controlling terminal. For any OS that doesn't, +setsid() can be #defined as a no-op, or as something else. */ if (background_daemon) { - log_close_all(); /* Just in case anything was logged earlier */ - search_tidyup(); /* Just in case any were used in reading the config. */ - close(0); /* Get rid of stdin/stdout/stderr */ + log_close_all(); /* Just in case anything was logged earlier */ + search_tidyup(); /* Just in case any were used in reading the config. */ + close(0); /* Get rid of stdin/stdout/stderr */ close(1); close(2); + exim_nullstd(); /* Connect stdin/stdout/stderr to /dev/null */ log_stderr = NULL; /* So no attempt to copy paniclog output */ /* If the parent process of this one has pid == 1, we are re-initializing the - daemon as the result of a SIGHUP. In this case, there is no need to do any - forking, because the controlling terminal has long gone. Otherwise, fork, - in case current process is a process group leader (see 'man setsid' for an - explanation). */ + daemon as the result of a SIGHUP. In this case, there is no need to do + anything, because the controlling terminal has long gone. Otherwise, fork, in + case current process is a process group leader (see 'man setsid' for an + explanation) before calling setsid(). */ if (getppid() != 1) { @@ -1365,7 +1388,7 @@ if (queue_interval > 0 && queue_run_max > 0) /* Set up the handler for termination of child processes. */ sigchld_seen = FALSE; -signal(SIGCHLD, main_sigchld_handler); +os_non_restarting_signal(SIGCHLD, main_sigchld_handler); /* If we are to run the queue periodically, pretend the alarm has just gone off. This will cause the first queue-runner to get kicked off straight away. */ @@ -1511,9 +1534,16 @@ for (;;) if ((pid = fork()) == 0) { int sk; + DEBUG(D_any) debug_printf("Starting queue-runner: pid %d\n", (int)getpid()); + /* Disable debugging if it's required only for the daemon process. We + leave the above message, because it ties up with the "child ended" + debugging messages. */ + + if (debug_daemon) debug_selector = 0; + /* Close any open listening sockets in the child */ for (sk = 0; sk < listen_socket_count; sk++) close(listen_sockets[sk]); @@ -1605,17 +1635,34 @@ for (;;) } DEBUG(D_any) debug_printf("Listening...\n"); + + /* In rare cases we may have had a SIGCHLD signal in the time between + setting the handler (below) and getting back here. If so, pretend that the + select() was interrupted so that we reap the child. This might still leave + a small window when a SIGCHLD could get lost. However, since we use SIGCHLD + only to do the reaping more quickly, it shouldn't result in anything other + than a delay until something else causes a wake-up. */ + + if (sigchld_seen) + { + lcount = -1; + errno = EINTR; + } + else + { + lcount = select(max_socket + 1, (SELECT_ARG2_TYPE *)&select_listen, + NULL, NULL, NULL); + } - if ((lcount = select(max_socket + 1, (SELECT_ARG2_TYPE *)&select_listen, - NULL, NULL, NULL)) < 0) + if (lcount < 0) { select_failed = TRUE; lcount = 1; } - + /* Loop for all the sockets that are currently ready to go. If select - actually failed, we have set the count to 1 and a flag, so as to use the - common error code for select/accept below. */ + actually failed, we have set the count to 1 and select_failed=TRUE, so as + to use the common error code for select/accept below. */ while (lcount-- > 0) { @@ -1766,7 +1813,7 @@ for (;;) if (sigchld_seen) { sigchld_seen = FALSE; - signal(SIGCHLD, main_sigchld_handler); + os_non_restarting_signal(SIGCHLD, main_sigchld_handler); } /* Handle being woken by SIGHUP. We know at this point that the result