/src/src/daemon.c
C | 2103 lines | 1217 code | 340 blank | 546 comment | 371 complexity | b398103b1da8ca58db117951ee4541cd MD5 | raw file
Possible License(s): GPL-2.0
Large files files are truncated, but you can click here to view the full file
- /*************************************************
- * Exim - an Internet mail transport agent *
- *************************************************/
- /* Copyright (c) University of Cambridge 1995 - 2014 */
- /* See the file NOTICE for conditions of use and distribution. */
- /* Functions concerned with running Exim as a daemon */
- #include "exim.h"
- /* Structure for holding data for each SMTP connection */
- typedef struct smtp_slot {
- pid_t pid; /* pid of the spawned reception process */
- uschar *host_address; /* address of the client host */
- } smtp_slot;
- /* An empty slot for initializing (Standard C does not allow constructor
- expressions in assigments except as initializers in declarations). */
- static smtp_slot empty_smtp_slot = { 0, NULL };
- /*************************************************
- * Local static variables *
- *************************************************/
- static SIGNAL_BOOL sigchld_seen;
- static SIGNAL_BOOL sighup_seen;
- static int accept_retry_count = 0;
- static int accept_retry_errno;
- static BOOL accept_retry_select_failed;
- static int queue_run_count = 0;
- static pid_t *queue_pid_slots = NULL;
- static smtp_slot *smtp_slots = NULL;
- static BOOL write_pid = TRUE;
- /*************************************************
- * SIGHUP Handler *
- *************************************************/
- /* All this handler does is to set a flag and re-enable the signal.
- Argument: the signal number
- Returns: nothing
- */
- static void
- sighup_handler(int sig)
- {
- sig = sig; /* Keep picky compilers happy */
- sighup_seen = TRUE;
- signal(SIGHUP, sighup_handler);
- }
- /*************************************************
- * SIGCHLD handler for main daemon process *
- *************************************************/
- /* Don't re-enable the handler here, since we aren't doing the
- waiting here. If the signal is re-enabled, there will just be an
- infinite sequence of calls to this handler. The SIGCHLD signal is
- used just as a means of waking up the daemon so that it notices
- terminated subprocesses as soon as possible.
- Argument: the signal number
- Returns: nothing
- */
- static void
- main_sigchld_handler(int sig)
- {
- sig = sig; /* Keep picky compilers happy */
- os_non_restarting_signal(SIGCHLD, SIG_DFL);
- sigchld_seen = TRUE;
- }
- /*************************************************
- * Unexpected errors in SMTP calls *
- *************************************************/
- /* This function just saves a bit of repetitious coding.
- Arguments:
- log_msg Text of message to be logged
- smtp_msg Text of SMTP error message
- was_errno The failing errno
- Returns: nothing
- */
- static void
- never_error(uschar *log_msg, uschar *smtp_msg, int was_errno)
- {
- uschar *emsg = (was_errno <= 0)? US"" :
- string_sprintf(": %s", strerror(was_errno));
- log_write(0, LOG_MAIN|LOG_PANIC, "%s%s", log_msg, emsg);
- if (smtp_out != NULL) smtp_printf("421 %s\r\n", smtp_msg);
- }
- /*************************************************
- * Handle a connected SMTP call *
- *************************************************/
- /* This function is called when an SMTP connection has been accepted.
- If there are too many, give an error message and close down. Otherwise
- spin off a sub-process to handle the call. The list of listening sockets
- is required so that they can be closed in the sub-process. Take care not to
- leak store in this process - reset the stacking pool at the end.
- Arguments:
- listen_sockets sockets which are listening for incoming calls
- listen_socket_count count of listening sockets
- accept_socket socket of the current accepted call
- accepted socket information about the current call
- Returns: nothing
- */
- static void
- handle_smtp_call(int *listen_sockets, int listen_socket_count,
- int accept_socket, struct sockaddr *accepted)
- {
- pid_t pid;
- union sockaddr_46 interface_sockaddr;
- EXIM_SOCKLEN_T ifsize = sizeof(interface_sockaddr);
- int dup_accept_socket = -1;
- int max_for_this_host = 0;
- int wfsize = 0;
- int wfptr = 0;
- int use_log_write_selector = log_write_selector;
- uschar *whofrom = NULL;
- void *reset_point = store_get(0);
- /* Make the address available in ASCII representation, and also fish out
- the remote port. */
- sender_host_address = host_ntoa(-1, accepted, NULL, &sender_host_port);
- DEBUG(D_any) debug_printf("Connection request from %s port %d\n",
- sender_host_address, sender_host_port);
- /* Set up the output stream, check the socket has duplicated, and set up the
- input stream. These operations fail only the exceptional circumstances. Note
- that never_error() won't use smtp_out if it is NULL. */
- smtp_out = fdopen(accept_socket, "wb");
- if (smtp_out == NULL)
- {
- never_error(US"daemon: fdopen() for smtp_out failed", US"", errno);
- goto ERROR_RETURN;
- }
- dup_accept_socket = dup(accept_socket);
- if (dup_accept_socket < 0)
- {
- never_error(US"daemon: couldn't dup socket descriptor",
- US"Connection setup failed", errno);
- goto ERROR_RETURN;
- }
- smtp_in = fdopen(dup_accept_socket, "rb");
- if (smtp_in == NULL)
- {
- never_error(US"daemon: fdopen() for smtp_in failed",
- US"Connection setup failed", errno);
- goto ERROR_RETURN;
- }
- /* Get the data for the local interface address. Panic for most errors, but
- "connection reset by peer" just means the connection went away. */
- if (getsockname(accept_socket, (struct sockaddr *)(&interface_sockaddr),
- &ifsize) < 0)
- {
- log_write(0, LOG_MAIN | ((errno == ECONNRESET)? 0 : LOG_PANIC),
- "getsockname() failed: %s", strerror(errno));
- smtp_printf("421 Local problem: getsockname() failed; please try again later\r\n");
- goto ERROR_RETURN;
- }
- interface_address = host_ntoa(-1, &interface_sockaddr, NULL, &interface_port);
- DEBUG(D_interface) debug_printf("interface address=%s port=%d\n",
- interface_address, interface_port);
- /* Build a string identifying the remote host and, if requested, the port and
- the local interface data. This is for logging; at the end of this function the
- memory is reclaimed. */
- whofrom = string_append(whofrom, &wfsize, &wfptr, 3, "[", sender_host_address, "]");
- if ((log_extra_selector & LX_incoming_port) != 0)
- whofrom = string_append(whofrom, &wfsize, &wfptr, 2, ":", string_sprintf("%d",
- sender_host_port));
- if ((log_extra_selector & LX_incoming_interface) != 0)
- whofrom = string_append(whofrom, &wfsize, &wfptr, 4, " I=[",
- interface_address, "]:", string_sprintf("%d", interface_port));
- whofrom[wfptr] = 0; /* Terminate the newly-built string */
- /* Check maximum number of connections. We do not check for reserved
- connections or unacceptable hosts here. That is done in the subprocess because
- it might take some time. */
- if (smtp_accept_max > 0 && smtp_accept_count >= smtp_accept_max)
- {
- DEBUG(D_any) debug_printf("rejecting SMTP connection: count=%d max=%d\n",
- smtp_accept_count, smtp_accept_max);
- smtp_printf("421 Too many concurrent SMTP connections; "
- "please try again later.\r\n");
- log_write(L_connection_reject,
- LOG_MAIN, "Connection from %s refused: too many connections",
- whofrom);
- goto ERROR_RETURN;
- }
- /* If a load limit above which only reserved hosts are acceptable is defined,
- get the load average here, and if there are in fact no reserved hosts, do
- the test right away (saves a fork). If there are hosts, do the check in the
- subprocess because it might take time. */
- if (smtp_load_reserve >= 0)
- {
- load_average = OS_GETLOADAVG();
- if (smtp_reserve_hosts == NULL && load_average > smtp_load_reserve)
- {
- DEBUG(D_any) debug_printf("rejecting SMTP connection: load average = %.2f\n",
- (double)load_average/1000.0);
- smtp_printf("421 Too much load; please try again later.\r\n");
- log_write(L_connection_reject,
- LOG_MAIN, "Connection from %s refused: load average = %.2f",
- whofrom, (double)load_average/1000.0);
- goto ERROR_RETURN;
- }
- }
- /* Check that one specific host (strictly, IP address) is not hogging
- resources. This is done here to prevent a denial of service attack by someone
- forcing you to fork lots of times before denying service. The value of
- smtp_accept_max_per_host is a string which is expanded. This makes it possible
- to provide host-specific limits according to $sender_host address, but because
- this is in the daemon mainline, only fast expansions (such as inline address
- checks) should be used. The documentation is full of warnings. */
- if (smtp_accept_max_per_host != NULL)
- {
- uschar *expanded = expand_string(smtp_accept_max_per_host);
- if (expanded == NULL)
- {
- if (!expand_string_forcedfail)
- log_write(0, LOG_MAIN|LOG_PANIC, "expansion of smtp_accept_max_per_host "
- "failed for %s: %s", whofrom, expand_string_message);
- }
- /* For speed, interpret a decimal number inline here */
- else
- {
- uschar *s = expanded;
- while (isdigit(*s))
- max_for_this_host = max_for_this_host * 10 + *s++ - '0';
- if (*s != 0)
- log_write(0, LOG_MAIN|LOG_PANIC, "expansion of smtp_accept_max_per_host "
- "for %s contains non-digit: %s", whofrom, expanded);
- }
- }
- /* If we have fewer connections than max_for_this_host, we can skip the tedious
- per host_address checks. Note that at this stage smtp_accept_count contains the
- count of *other* connections, not including this one. */
- if ((max_for_this_host > 0) &&
- (smtp_accept_count >= max_for_this_host))
- {
- int i;
- int host_accept_count = 0;
- int other_host_count = 0; /* keep a count of non matches to optimise */
- for (i = 0; i < smtp_accept_max; ++i)
- {
- if (smtp_slots[i].host_address != NULL)
- {
- if (Ustrcmp(sender_host_address, smtp_slots[i].host_address) == 0)
- host_accept_count++;
- else
- other_host_count++;
- /* Testing all these strings is expensive - see if we can drop out
- early, either by hitting the target, or finding there are not enough
- connections left to make the target. */
- if ((host_accept_count >= max_for_this_host) ||
- ((smtp_accept_count - other_host_count) < max_for_this_host))
- break;
- }
- }
- if (host_accept_count >= max_for_this_host)
- {
- DEBUG(D_any) debug_printf("rejecting SMTP connection: too many from this "
- "IP address: count=%d max=%d\n",
- host_accept_count, max_for_this_host);
- smtp_printf("421 Too many concurrent SMTP connections "
- "from this IP address; please try again later.\r\n");
- log_write(L_connection_reject,
- LOG_MAIN, "Connection from %s refused: too many connections "
- "from that IP address", whofrom);
- goto ERROR_RETURN;
- }
- }
- /* OK, the connection count checks have been passed. Before we can fork the
- accepting process, we must first log the connection if requested. This logging
- used to happen in the subprocess, but doing that means that the value of
- smtp_accept_count can be out of step by the time it is logged. So we have to do
- the logging here and accept the performance cost. Note that smtp_accept_count
- hasn't yet been incremented to take account of this connection.
- In order to minimize the cost (because this is going to happen for every
- connection), do a preliminary selector test here. This saves ploughing through
- the generalized logging code each time when the selector is false. If the
- selector is set, check whether the host is on the list for logging. If not,
- arrange to unset the selector in the subprocess. */
- if ((log_write_selector & L_smtp_connection) != 0)
- {
- uschar *list = hosts_connection_nolog;
- if (list != NULL && verify_check_host(&list) == OK)
- use_log_write_selector &= ~L_smtp_connection;
- else
- log_write(L_smtp_connection, LOG_MAIN, "SMTP connection from %s "
- "(TCP/IP connection count = %d)", whofrom, smtp_accept_count + 1);
- }
- /* Now we can fork the accepting process; do a lookup tidy, just in case any
- expansion above did a lookup. */
- search_tidyup();
- pid = fork();
- /* Handle the child process */
- 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;
- BOOL session_local_queue_only;
- #ifdef SA_NOCLDWAIT
- struct sigaction act;
- #endif
- smtp_accept_count++; /* So that it includes this process */
- /* May have been modified for the subprocess */
- log_write_selector = use_log_write_selector;
- /* Get the local interface address into permanent store */
- store_pool = POOL_PERM;
- interface_address = string_copy(interface_address);
- store_pool = old_pool;
- /* Check for a tls-on-connect port */
- if (host_is_tls_on_connect_port(interface_port)) tls_in.on_connect = TRUE;
- /* Expand smtp_active_hostname if required. We do not do this any earlier,
- because it may depend on the local interface address (indeed, that is most
- likely what it depends on.) */
- smtp_active_hostname = primary_hostname;
- if (raw_active_hostname != NULL)
- {
- uschar *nah = expand_string(raw_active_hostname);
- if (nah == NULL)
- {
- if (!expand_string_forcedfail)
- {
- log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand \"%s\" "
- "(smtp_active_hostname): %s", raw_active_hostname,
- expand_string_message);
- smtp_printf("421 Local configuration error; "
- "please try again later.\r\n");
- mac_smtp_fflush();
- search_tidyup();
- _exit(EXIT_FAILURE);
- }
- }
- else if (nah[0] != 0) smtp_active_hostname = nah;
- }
- /* Initialize the queueing flags */
- queue_check_only();
- session_local_queue_only = queue_only;
- /* Close the listening sockets, and set the SIGCHLD handler to SIG_IGN.
- We also attempt to set things up so that children are automatically reaped,
- but just in case this isn't available, there's a paranoid waitpid() in the
- loop too (except for systems where we are sure it isn't needed). See the more
- extensive comment before the reception loop in exim.c for a fuller
- explanation of this logic. */
- for (i = 0; i < listen_socket_count; i++) (void)close(listen_sockets[i]);
- /* Set FD_CLOEXEC on the SMTP socket. We don't want any rogue child processes
- to be able to communicate with them, under any circumstances. */
- (void)fcntl(accept_socket, F_SETFD,
- fcntl(accept_socket, F_GETFD) | FD_CLOEXEC);
- (void)fcntl(dup_accept_socket, F_SETFD,
- fcntl(dup_accept_socket, F_GETFD) | FD_CLOEXEC);
- #ifdef SA_NOCLDWAIT
- act.sa_handler = SIG_IGN;
- sigemptyset(&(act.sa_mask));
- act.sa_flags = SA_NOCLDWAIT;
- sigaction(SIGCHLD, &act, NULL);
- #else
- signal(SIGCHLD, SIG_IGN);
- #endif
- /* 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.
- 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 session_local_queue_only flag, which is initialized from the
- configured value and may therefore already be TRUE. Leave logging
- till later so it will have a message id attached. Note that there is no
- possibility of re-calculating this per-message, because the value of
- smtp_accept_count does not change in this subprocess. */
- if (smtp_accept_queue > 0 && smtp_accept_count > smtp_accept_queue)
- {
- session_local_queue_only = TRUE;
- queue_only_reason = 1;
- }
- /* Handle the start of the SMTP session, then loop, accepting incoming
- messages from the SMTP connection. The end will come at the QUIT command,
- when smtp_setup_msg() returns 0. A break in the connection causes the
- process to die (see accept.c).
- NOTE: We do *not* call smtp_log_no_mail() if smtp_start_session() fails,
- because a log line has already been written for all its failure exists
- (usually "connection refused: <reason>") and writing another one is
- unnecessary clutter. */
- if (!smtp_start_session())
- {
- mac_smtp_fflush();
- search_tidyup();
- _exit(EXIT_SUCCESS);
- }
- for (;;)
- {
- int rc;
- message_id[0] = 0; /* Clear out any previous message_id */
- reset_point = store_get(0); /* Save current store high water point */
- DEBUG(D_any)
- debug_printf("Process %d is ready for new message\n", (int)getpid());
- /* Smtp_setup_msg() returns 0 on QUIT or if the call is from an
- unacceptable host or if an ACL "drop" command was triggered, -1 on
- connection lost, and +1 on validly reaching DATA. Receive_msg() almost
- always returns TRUE when smtp_input is true; just retry if no message was
- accepted (can happen for invalid message parameters). However, it can yield
- FALSE if the connection was forcibly dropped by the DATA ACL. */
- if ((rc = smtp_setup_msg()) > 0)
- {
- BOOL ok = receive_msg(FALSE);
- search_tidyup(); /* Close cached databases */
- if (!ok) /* Connection was dropped */
- {
- mac_smtp_fflush();
- smtp_log_no_mail(); /* Log no mail if configured */
- _exit(EXIT_SUCCESS);
- }
- if (message_id[0] == 0) continue; /* No message was accepted */
- }
- else
- {
- mac_smtp_fflush();
- search_tidyup();
- smtp_log_no_mail(); /* Log no mail if configured */
- _exit((rc == 0)? EXIT_SUCCESS : EXIT_FAILURE);
- }
- /* Show the recipients when debugging */
- DEBUG(D_receive)
- {
- int i;
- if (sender_address != NULL)
- debug_printf("Sender: %s\n", sender_address);
- if (recipients_list != NULL)
- {
- debug_printf("Recipients:\n");
- for (i = 0; i < recipients_count; i++)
- debug_printf(" %s\n", recipients_list[i].address);
- }
- }
- /* A message has been accepted. Clean up any previous delivery processes
- that have completed and are defunct, on systems where they don't go away
- by themselves (see comments when setting SIG_IGN above). On such systems
- (if any) these delivery processes hang around after termination until
- the next message is received. */
- #ifndef SIG_IGN_WORKS
- while (waitpid(-1, NULL, WNOHANG) > 0);
- #endif
- /* Reclaim up the store used in accepting this message */
- store_reset(reset_point);
- /* If queue_only is set or if there are too many incoming connections in
- existence, session_local_queue_only will be TRUE. If it is not, check
- whether we have received too many messages in this session for immediate
- delivery. */
- if (!session_local_queue_only &&
- smtp_accept_queue_per_connection > 0 &&
- receive_messagecount > smtp_accept_queue_per_connection)
- {
- session_local_queue_only = TRUE;
- queue_only_reason = 2;
- }
- /* Initialize local_queue_only from session_local_queue_only. If it is not
- true, and queue_only_load is set, check that the load average is below it.
- If local_queue_only is set by this means, we also set if for the session if
- queue_only_load_latch is true (the default). This means that, once set,
- local_queue_only remains set for any subsequent messages on the same SMTP
- connection. This is a deliberate choice; even though the load average may
- fall, it doesn't seem right to deliver later messages on the same call when
- not delivering earlier ones. However, the are special circumstances such as
- very long-lived connections from scanning appliances where this is not the
- best strategy. In such cases, queue_only_load_latch should be set false. */
- local_queue_only = session_local_queue_only;
- if (!local_queue_only && queue_only_load >= 0)
- {
- local_queue_only = (load_average = OS_GETLOADAVG()) > queue_only_load;
- if (local_queue_only)
- {
- queue_only_reason = 3;
- if (queue_only_load_latch) session_local_queue_only = TRUE;
- }
- }
- /* Log the queueing here, when it will get a message id attached, but
- not if queue_only is set (case 0). */
- if (local_queue_only) switch(queue_only_reason)
- {
- case 1:
- log_write(L_delay_delivery,
- LOG_MAIN, "no immediate delivery: too many connections "
- "(%d, max %d)", smtp_accept_count, smtp_accept_queue);
- break;
- case 2:
- log_write(L_delay_delivery,
- LOG_MAIN, "no immediate delivery: more than %d messages "
- "received in one connection", smtp_accept_queue_per_connection);
- break;
- case 3:
- log_write(L_delay_delivery,
- LOG_MAIN, "no immediate delivery: load average %.2f",
- (double)load_average/1000.0);
- break;
- }
- /* If a delivery attempt is required, spin off a new process to handle it.
- If we are not root, we have to re-exec exim unless deliveries are being
- done unprivileged. */
- else if (!queue_only_policy && !deliver_freeze)
- {
- pid_t dpid;
- /* Before forking, ensure that the C output buffer is flushed. Otherwise
- anything that it in it will get duplicated, leading to duplicate copies
- of the pending output. */
- mac_smtp_fflush();
- if ((dpid = fork()) == 0)
- {
- (void)fclose(smtp_in);
- (void)fclose(smtp_out);
- /* Don't ever molest the parent's SSL connection, but do clean up
- the data structures if necessary. */
- #ifdef SUPPORT_TLS
- tls_close(TRUE, FALSE);
- #endif
- /* Reset SIGHUP and SIGCHLD in the child in both cases. */
- signal(SIGHUP, SIG_DFL);
- signal(SIGCHLD, SIG_DFL);
- if (geteuid() != root_uid && !deliver_drop_privilege)
- {
- signal(SIGALRM, SIG_DFL);
- (void)child_exec_exim(CEE_EXEC_PANIC, FALSE, NULL, FALSE, 2, US"-Mc",
- message_id);
- /* Control does not return here. */
- }
- /* No need to re-exec; SIGALRM remains set to the default handler */
- (void)deliver_message(message_id, FALSE, FALSE);
- search_tidyup();
- _exit(EXIT_SUCCESS);
- }
- if (dpid > 0)
- {
- DEBUG(D_any) debug_printf("forked delivery process %d\n", (int)dpid);
- }
- else
- {
- log_write(0, LOG_MAIN|LOG_PANIC, "daemon: delivery process fork "
- "failed: %s", strerror(errno));
- }
- }
- }
- }
- /* Carrying on in the parent daemon process... Can't do much if the fork
- failed. Otherwise, keep count of the number of accepting processes and
- remember the pid for ticking off when the child completes. */
- if (pid < 0)
- {
- never_error(US"daemon: accept process fork failed", US"Fork failed", errno);
- }
- else
- {
- int i;
- for (i = 0; i < smtp_accept_max; ++i)
- {
- if (smtp_slots[i].pid <= 0)
- {
- smtp_slots[i].pid = pid;
- if (smtp_accept_max_per_host != NULL)
- smtp_slots[i].host_address = string_copy_malloc(sender_host_address);
- smtp_accept_count++;
- break;
- }
- }
- DEBUG(D_any) debug_printf("%d SMTP accept process%s running\n",
- smtp_accept_count, (smtp_accept_count == 1)? "" : "es");
- }
- /* Get here via goto in error cases */
- ERROR_RETURN:
- /* Close the streams associated with the socket which will also close the
- socket fds in this process. We can't do anything if fclose() fails, but
- logging brings it to someone's attention. However, "connection reset by peer"
- isn't really a problem, so skip that one. On Solaris, a dropped connection can
- manifest itself as a broken pipe, so drop that one too. If the streams don't
- exist, something went wrong while setting things up. Make sure the socket
- descriptors are closed, in order to drop the connection. */
- if (smtp_out != NULL)
- {
- if (fclose(smtp_out) != 0 && errno != ECONNRESET && errno != EPIPE)
- log_write(0, LOG_MAIN|LOG_PANIC, "daemon: fclose(smtp_out) failed: %s",
- strerror(errno));
- smtp_out = NULL;
- }
- else (void)close(accept_socket);
- if (smtp_in != NULL)
- {
- if (fclose(smtp_in) != 0 && errno != ECONNRESET && errno != EPIPE)
- log_write(0, LOG_MAIN|LOG_PANIC, "daemon: fclose(smtp_in) failed: %s",
- strerror(errno));
- smtp_in = NULL;
- }
- else (void)close(dup_accept_socket);
- /* Release any store used in this process, including the store used for holding
- the incoming host address and an expanded active_hostname. */
- store_reset(reset_point);
- sender_host_address = NULL;
- }
- /*************************************************
- * Check wildcard listen special cases *
- *************************************************/
- /* This function is used when binding and listening on lists of addresses and
- ports. It tests for special cases of wildcard listening, when IPv4 and IPv6
- sockets may interact in different ways in different operating systems. It is
- passed an error number, the list of listening addresses, and the current
- address. Two checks are available: for a previous wildcard IPv6 address, or for
- a following wildcard IPv4 address, in both cases on the same port.
- In practice, pairs of wildcard addresses should be adjacent in the address list
- because they are sorted that way below.
- Arguments:
- eno the error number
- addresses the list of addresses
- ipa the current IP address
- back if TRUE, check for previous wildcard IPv6 address
- if FALSE, check for a following wildcard IPv4 address
- Returns: TRUE or FALSE
- */
- static BOOL
- check_special_case(int eno, ip_address_item *addresses, ip_address_item *ipa,
- BOOL back)
- {
- ip_address_item *ipa2;
- /* For the "back" case, if the failure was "address in use" for a wildcard IPv4
- address, seek a previous IPv6 wildcard address on the same port. As it is
- previous, it must have been successfully bound and be listening. Flag it as a
- "6 including 4" listener. */
- if (back)
- {
- if (eno != EADDRINUSE || ipa->address[0] != 0) return FALSE;
- for (ipa2 = addresses; ipa2 != ipa; ipa2 = ipa2->next)
- {
- if (ipa2->address[1] == 0 && ipa2->port == ipa->port)
- {
- ipa2->v6_include_v4 = TRUE;
- return TRUE;
- }
- }
- }
- /* For the "forward" case, if the current address is a wildcard IPv6 address,
- we seek a following wildcard IPv4 address on the same port. */
- else
- {
- if (ipa->address[0] != ':' || ipa->address[1] != 0) return FALSE;
- for (ipa2 = ipa->next; ipa2 != NULL; ipa2 = ipa2->next)
- if (ipa2->address[0] == 0 && ipa->port == ipa2->port) return TRUE;
- }
- return FALSE;
- }
- /*************************************************
- * Handle terminating subprocesses *
- *************************************************/
- /* Handle the termination of child processes. Theoretically, this need be done
- only when sigchld_seen is TRUE, but rumour has it that some systems lose
- SIGCHLD signals at busy times, so to be on the safe side, this function is
- called each time round. It shouldn't be too expensive.
- Arguments: none
- Returns: nothing
- */
- static void
- handle_ending_processes(void)
- {
- int status;
- pid_t pid;
- while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
- {
- int i;
- DEBUG(D_any)
- {
- debug_printf("child %d ended: status=0x%x\n", (int)pid, status);
- #ifdef WCOREDUMP
- if (WIFEXITED(status))
- debug_printf(" normal exit, %d\n", WEXITSTATUS(status));
- else if (WIFSIGNALED(status))
- debug_printf(" signal exit, signal %d%s\n", WTERMSIG(status),
- WCOREDUMP(status) ? " (core dumped)" : "");
- #endif
- }
- /* If it's a listening daemon for which we are keeping track of individual
- subprocesses, deal with an accepting process that has terminated. */
- if (smtp_slots != NULL)
- {
- for (i = 0; i < smtp_accept_max; i++)
- {
- if (smtp_slots[i].pid == pid)
- {
- if (smtp_slots[i].host_address != NULL)
- store_free(smtp_slots[i].host_address);
- smtp_slots[i] = empty_smtp_slot;
- if (--smtp_accept_count < 0) smtp_accept_count = 0;
- DEBUG(D_any) debug_printf("%d SMTP accept process%s now running\n",
- smtp_accept_count, (smtp_accept_count == 1)? "" : "es");
- break;
- }
- }
- if (i < smtp_accept_max) continue; /* Found an accepting process */
- }
- /* If it wasn't an accepting process, see if it was a queue-runner
- process that we are tracking. */
- if (queue_pid_slots != NULL)
- {
- for (i = 0; i < queue_run_max; i++)
- {
- if (queue_pid_slots[i] == pid)
- {
- queue_pid_slots[i] = 0;
- if (--queue_run_count < 0) queue_run_count = 0;
- DEBUG(D_any) debug_printf("%d queue-runner process%s now running\n",
- queue_run_count, (queue_run_count == 1)? "" : "es");
- break;
- }
- }
- }
- }
- }
- /*************************************************
- * Exim Daemon Mainline *
- *************************************************/
- /* The daemon can do two jobs, either of which is optional:
- (1) Listens for incoming SMTP calls and spawns off a sub-process to handle
- each one. This is requested by the -bd option, with -oX specifying the SMTP
- port on which to listen (for testing).
- (2) Spawns a queue-running process every so often. This is controlled by the
- -q option with a an interval time. (If no time is given, a single queue run
- is done from the main function, and control doesn't get here.)
- Root privilege is required in order to attach to port 25. Some systems require
- it when calling socket() rather than bind(). To cope with all cases, we run as
- root for both socket() and bind(). Some systems also require root in order to
- write to the pid file directory. This function must therefore be called as root
- if it is to work properly in all circumstances. Once the socket is bound and
- the pid file written, root privilege is given up if there is an exim uid.
- There are no arguments to this function, and it never returns. */
- void
- daemon_go(void)
- {
- struct passwd *pw;
- int *listen_sockets = NULL;
- int listen_socket_count = 0;
- ip_address_item *addresses = NULL;
- time_t last_connection_time = (time_t)0;
- /* If any debugging options are set, turn on the D_pid bit so that all
- debugging lines get the pid added. */
- DEBUG(D_any|D_v) debug_selector |= D_pid;
- if (inetd_wait_mode)
- {
- int on = 1;
- listen_socket_count = 1;
- listen_sockets = store_get(sizeof(int *));
- (void) close(3);
- if (dup2(0, 3) == -1)
- {
- log_write(0, LOG_MAIN|LOG_PANIC_DIE,
- "failed to dup inetd socket safely away: %s", strerror(errno));
- }
- listen_sockets[0] = 3;
- (void) close(0);
- (void) close(1);
- (void) close(2);
- exim_nullstd();
- if (debug_file == stderr)
- {
- /* need a call to log_write before call to open debug_file, so that
- log.c:file_path has been initialised. This is unfortunate. */
- log_write(0, LOG_MAIN, "debugging Exim in inetd wait mode starting");
- fclose(debug_file);
- debug_file = NULL;
- exim_nullstd(); /* re-open fd2 after we just closed it again */
- debug_logging_activate(US"-wait", NULL);
- }
- DEBUG(D_any) debug_printf("running in inetd wait mode\n");
- /* As per below, when creating sockets ourselves, we handle tcp_nodelay for
- our own buffering; we assume though that inetd set the socket REUSEADDR. */
- if (tcp_nodelay) setsockopt(3, IPPROTO_TCP, TCP_NODELAY,
- (uschar *)(&on), sizeof(on));
- }
- if (inetd_wait_mode || daemon_listen)
- {
- /* If any option requiring a load average to be available during the
- reception of a message is set, call os_getloadavg() while we are root
- for those OS for which this is necessary the first time it is called (in
- order to perform an "open" on the kernel memory file). */
- #ifdef LOAD_AVG_NEEDS_ROOT
- if (queue_only_load >= 0 || smtp_load_reserve >= 0 ||
- (deliver_queue_load_max >= 0 && deliver_drop_privilege))
- (void)os_getloadavg();
- #endif
- }
- /* Do the preparation for setting up a listener on one or more interfaces, and
- possible on various ports. This is controlled by the combination of
- local_interfaces (which can set IP addresses and ports) and daemon_smtp_port
- (which is a list of default ports to use for those items in local_interfaces
- that do not specify a port). The -oX command line option can be used to
- override one or both of these options.
- If local_interfaces is not set, the default is to listen on all interfaces.
- When it is set, it can include "all IPvx interfaces" as an item. This is useful
- when different ports are in use.
- It turns out that listening on all interfaces is messy in an IPv6 world,
- because several different implementation approaches have been taken. This code
- is now supposed to work with all of them. The point of difference is whether an
- IPv6 socket that is listening on all interfaces will receive incoming IPv4
- calls or not. We also have to cope with the case when IPv6 libraries exist, but
- there is no IPv6 support in the kernel.
- . On Solaris, an IPv6 socket will accept IPv4 calls, and give them as mapped
- addresses. However, if an IPv4 socket is also listening on all interfaces,
- calls are directed to the appropriate socket.
- . On (some versions of) Linux, an IPv6 socket will accept IPv4 calls, and
- give them as mapped addresses, but an attempt also to listen on an IPv4
- socket on all interfaces causes an error.
- . On OpenBSD, an IPv6 socket will not accept IPv4 calls. You have to set up
- two sockets if you want to accept both kinds of call.
- . FreeBSD is like OpenBSD, but it has the IPV6_V6ONLY socket option, which
- can be turned off, to make it behave like the versions of Linux described
- above.
- . I heard a report that the USAGI IPv6 stack for Linux has implemented
- IPV6_V6ONLY.
- So, what we do when IPv6 is supported is as follows:
- (1) After it is set up, the list of interfaces is scanned for wildcard
- addresses. If an IPv6 and an IPv4 wildcard are both found for the same
- port, the list is re-arranged so that they are together, with the IPv6
- wildcard first.
- (2) If the creation of a wildcard IPv6 socket fails, we just log the error and
- carry on if an IPv4 wildcard socket for the same port follows later in the
- list. This allows Exim to carry on in the case when the kernel has no IPv6
- support.
- (3) Having created an IPv6 wildcard socket, we try to set IPV6_V6ONLY if that
- option is defined. However, if setting fails, carry on regardless (but log
- the incident).
- (4) If binding or listening on an IPv6 wildcard socket fails, it is a serious
- error.
- (5) If binding or listening on an IPv4 wildcard socket fails with the error
- EADDRINUSE, and a previous interface was an IPv6 wildcard for the same
- port (which must have succeeded or we wouldn't have got this far), we
- assume we are in the situation where just a single socket is permitted,
- and ignore the error.
- Phew!
- The preparation code decodes options and sets up the relevant data. We do this
- first, so that we can return non-zero if there are any syntax errors, and also
- write to stderr. */
- if (daemon_listen && !inetd_wait_mode)
- {
- int *default_smtp_port;
- int sep;
- int pct = 0;
- uschar *s;
- const uschar * list;
- uschar *local_iface_source = US"local_interfaces";
- ip_address_item *ipa;
- ip_address_item **pipa;
- /* If -oX was used, disable the writing of a pid file unless -oP was
- explicitly used to force it. Then scan the string given to -oX. Any items
- that contain neither a dot nor a colon are used to override daemon_smtp_port.
- Any other items are used to override local_interfaces. */
- if (override_local_interfaces != NULL)
- {
- uschar *new_smtp_port = NULL;
- uschar *new_local_interfaces = NULL;
- int portsize = 0;
- int portptr = 0;
- int ifacesize = 0;
- int ifaceptr = 0;
- if (override_pid_file_path == NULL) write_pid = FALSE;
- list = override_local_interfaces;
- sep = 0;
- while ((s = string_nextinlist(&list, &sep, big_buffer, big_buffer_size)))
- {
- uschar joinstr[4];
- uschar **ptr;
- int *sizeptr;
- int *ptrptr;
- if (Ustrpbrk(s, ".:") == NULL)
- {
- ptr = &new_smtp_port;
- sizeptr = &portsize;
- ptrptr = &portptr;
- }
- else
- {
- ptr = &new_local_interfaces;
- sizeptr = &ifacesize;
- ptrptr = &ifaceptr;
- }
- if (*ptr == NULL)
- {
- joinstr[0] = sep;
- joinstr[1] = ' ';
- *ptr = string_cat(*ptr, sizeptr, ptrptr, US"<", 1);
- }
- *ptr = string_cat(*ptr, sizeptr, ptrptr, joinstr, 2);
- *ptr = string_cat(*ptr, sizeptr, ptrptr, s, Ustrlen(s));
- }
- if (new_smtp_port != NULL)
- {
- new_smtp_port[portptr] = 0;
- daemon_smtp_port = new_smtp_port;
- DEBUG(D_any) debug_printf("daemon_smtp_port overridden by -oX:\n %s\n",
- daemon_smtp_port);
- }
- if (new_local_interfaces != NULL)
- {
- new_local_interfaces[ifaceptr] = 0;
- local_interfaces = new_local_interfaces;
- local_iface_source = US"-oX data";
- DEBUG(D_any) debug_printf("local_interfaces overridden by -oX:\n %s\n",
- local_interfaces);
- }
- }
- /* Create a list of default SMTP ports, to be used if local_interfaces
- contains entries without explict ports. First count the number of ports, then
- build a translated list in a vector. */
- list = daemon_smtp_port;
- sep = 0;
- while ((s = string_nextinlist(&list, &sep, big_buffer, big_buffer_size)))
- pct++;
- default_smtp_port = store_get((pct+1) * sizeof(int));
- list = daemon_smtp_port;
- sep = 0;
- for (pct = 0;
- (s = string_nextinlist(&list, &sep, big_buffer, big_buffer_size));
- pct++)
- {
- if (isdigit(*s))
- {
- uschar *end;
- default_smtp_port[pct] = Ustrtol(s, &end, 0);
- if (end != s + Ustrlen(s))
- log_write(0, LOG_PANIC_DIE|LOG_CONFIG, "invalid SMTP port: %s", s);
- }
- else
- {
- struct servent *smtp_service = getservbyname(CS s, "tcp");
- if (!smtp_service)
- log_write(0, LOG_PANIC_DIE|LOG_CONFIG, "TCP port \"%s\" not found", s);
- default_smtp_port[pct] = ntohs(smtp_service->s_port);
- }
- }
- default_smtp_port[pct] = 0;
- /* Check the list of TLS-on-connect ports and do name lookups if needed */
- list = tls_in.on_connect_ports;
- sep = 0;
- while ((s = string_nextinlist(&list, &sep, big_buffer, big_buffer_size)))
- if (!isdigit(*s))
- {
- list = tls_in.on_connect_ports;
- tls_in.on_connect_ports = NULL;
- sep = 0;
- while ((s = string_nextinlist(&list, &sep, big_buffer, big_buffer_size)))
- {
- if (!isdigit(*s))
- {
- struct servent *smtp_service = getservbyname(CS s, "tcp");
- if (!smtp_service)
- log_write(0, LOG_PANIC_DIE|LOG_CONFIG, "TCP port \"%s\" not found", s);
- s= string_sprintf("%d", (int)ntohs(smtp_service->s_port));
- }
- tls_in.on_connect_ports = string_append_listele(tls_in.on_connect_ports,
- ':', s);
- }
- break;
- }
- /* Create the list of local interfaces, possibly with ports included. This
- list may contain references to 0.0.0.0 and ::0 as wildcards. These special
- values are converted below. */
- addresses = host_build_ifacelist(local_interfaces, local_iface_source);
- /* In the list of IP addresses, convert 0.0.0.0 into an empty string, and ::0
- into the string ":". We use these to recognize wildcards in IPv4 and IPv6. In
- fact, many IP stacks recognize 0.0.0.0 and ::0 and handle them as wildcards
- anyway, but we need to know which are the wildcard addresses, and the shorter
- strings are neater.
- In the same scan, fill in missing port numbers from the default list. When
- there is more than one item in the list, extra items are created. */
- for (ipa = addresses; ipa != NULL; ipa = ipa->next)
- {
- int i;
- if (Ustrcmp(ipa->address, "0.0.0.0") == 0) ipa->address[0] = 0;
- else if (Ustrcmp(ipa->address, "::0") == 0)
- {
- ipa->address[0] = ':';
- ipa->address[1] = 0;
- }
- if (ipa->port > 0) continue;
- if (daemon_smtp_port[0] <= 0)
- log_write(0, LOG_MAIN|LOG_PANIC_DIE, "no port specified for interface "
- "%s and daemon_smtp_port is unset; cannot start daemon",
- (ipa->address[0] == 0)? US"\"all IPv4\"" :
- (ipa->address[1] == 0)? US"\"all IPv6\"" : ipa->address);
- ipa->port = default_smtp_port[0];
- for (i = 1; default_smtp_port[i] > 0; i++)
- {
- ip_address_item *new = store_get(sizeof(ip_address_item));
- memcpy(new->address, ipa->address, Ustrlen(ipa->address) + 1);
- new->port = default_smtp_port[i];
- new->next = ipa->next;
- ipa->next = new;
- ipa = new;
- }
- }
- /* Scan the list of addresses for wildcards. If we find an IPv4 and an IPv6
- wildcard for the same port, ensure that (a) they are together and (b) the
- IPv6 address comes first. This makes handling the messy features easier, and
- also simplifies the construction of the "daemon started" log line. */
- pipa = &addresses;
- for (ipa = addresses; ipa != NULL; pipa = &(ipa->next), ipa = ipa->next)
- {
- ip_address_item *ipa2;
- /* Handle an IPv4 wildcard */
- if (ipa->address[0] == 0)
- {
- for (ipa2 = ipa; ipa2->next != NULL; ipa2 = ipa2->next)
- {
- ip_address_item *ipa3 = ipa2->next;
- if (ipa3->address[0] == ':' &&
- ipa3->address[1] == 0 &&
- ipa3->port == ipa->port)
- {
- ipa2->next = ipa3->next;
- ipa3->next = ipa;
- *pipa = ipa3;
- break;
- }
- }
- }
- /* Handle an IPv6 wildcard. */
- else if (ipa->address[0] == ':' && ipa->address[1] == 0)
- {
- for (ipa2 = ipa; ipa2->next != NULL; ipa2 = ipa2->next)
- {
- ip_address_item *ipa3 = ipa2->next;
- if (ipa3->address[0] == 0 && ipa3->port == ipa->port)
- {
- ipa2->next = ipa3->next;
- ipa3->next = ipa->next;
- ipa->next = ipa3;
- ipa = ipa3;
- break;
- }
- }
- }
- }
- /* Get a vector to remember all the sockets in */
- for (ipa = addresses; ipa != NULL; ipa = ipa->next)
- listen_socket_count++;
- listen_sockets = store_get(sizeof(int *) * listen_socket_count);
- } /* daemon_listen but not inetd_wait_mode */
- if (daemon_listen)
- {
- /* Do a sanity check on the max connects value just to save us from getting
- a huge amount of store. */
- if (smtp_accept_max > 4095) smtp_accept_max = 4096;
- /* There's no point setting smtp_accept_queue unless it is less than the max
- connects limit. The configuration reader ensures that the max is set if the
- queue-only option is set. */
- if (smtp_accept_queue > smtp_accept_max) smtp_accept_queue = 0;
- /* Get somewhere to keep the list of SMTP accepting pids if we are keeping
- track of them for total number and queue/host limits. */
- if (smtp_accept_max > 0)
- {
- int i;
- smtp_slots = store_get(smtp_accept_max * sizeof(smtp_slot));
- for (i = 0; i < smtp_accept_max; i++) smtp_slots[i] = empty_smtp_slot;
- }
- }
- /* 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. Also
- do this for inetd_wait mode.
- 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 || inetd_wait_mode)
- {
- log_close_all(); /* Just in case anything was logged earlier */
- search_tidyup(); /* Just in case any were used in reading the config. */
- (void)close(0); /* Get rid of stdin/stdout/stderr */
- (void)close(1);
- (void)close(2);
- exim_nullstd(); /* Connect stdin/stdout/stderr to /dev/null */
- log_stderr = NULL; /* So no attempt to copy paniclog output */
- }
- if (background_daemon)
- {
- /* 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
- 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)
- {
- pid_t pid = fork();
- if (pid < 0) log_write(0, LOG_MAIN|LOG_PANIC_DIE,
- "fork() failed when starting daemon: %s", strerror(errno));
- if (pid > 0) exit(EXIT_SUCCESS); /* in parent process, just exit */
- (void)setsid(); /* release controlling terminal */
- }
- }
- /* We are now in the disconnected, daemon process (unless debugging). Set up
- the listening sockets if required. */
- if (daemon_listen && !inetd_wait_mode)
- {
- int sk;
- int on = 1;
- ip_address_item *ipa;
- /* For each IP address, create a socket, bind it to the appropriate port, and
- start listening. See comments above about IPv6 sockets that may or may not
- accept IPv4 calls when listening on all interfaces. We also have to cope with
- the case of a system with IPv6 libraries, but no IPv6 support in the kernel.
- listening, provided a wildcard IPv4 socket for the same port follows. */
- for (ipa = addresses, sk = 0; sk < listen_socket_count; ipa = ipa->next, sk++)
- {
- BOOL wildcard;
- ip_address_item *ipa2;
- int af;
- if (Ustrchr(ipa->address, ':') != NULL)
- {
- af = AF_INET6;
- wildcard = ipa->address[1] == 0;
- }
- else
- {
- af = AF_INET;
- wildcard = ipa->address[0] == 0;
- }
- listen_sockets[sk] = ip_socket(SOCK_STREAM, af);
- if (listen_sockets[sk] < 0)
- {
- if (check_special_case(0, addresses, ipa, FALSE))
- {
- log_write(0, LOG_MAIN, "Failed to create IPv6 socket for wildcard "
- "listening (%s): will use IPv4", strerror(errno));
- goto SKIP_SOCKET;
- }
- log_write(0, LOG_PANIC_DIE, "IPv%c socket creation failed: %s",
- (af == AF_INET6)? '6' : '4', strerror(errno));
- }
- /* If this is an IPv6 wildcard socket, set IPV6_V6ONLY if that option is
- available. Just log failure (can get protocol not available, just like
- socket creation can). */
- #ifdef IPV6_V6ONLY
- if (af == AF_INET6 && wildcard &&
- setsockopt(listen_sockets[sk], IPPROTO_IPV6, IPV6_V6ONLY, (char *)(&on),
- sizeof(on)) < 0)
- log_write(0, LOG_MAIN, "Setting IPV6_V6ONLY on daemon's IPv6 wildcard "
- "socket failed (%s): carrying on without it", strerror(errno));
- #endif /* IPV6_V6ONLY */
- /* Set SO_REUSEADDR so that the daemon can be restarted while a connection
- is being handled. Without this, a connection will prevent reuse of the
- smtp port for listening. */
- if (setsockopt(listen_sockets[sk], SOL_SOCKET, SO_REUSEADDR,
- (uschar *)(&on), sizeof(on)) < 0)
- log_write(0, LOG_MAIN|LOG_PANIC_DIE, "setting SO_REUSEADDR on socket "
- "failed when starting daemon: %s", strerror(errno));
- /* Set TCP_NODELAY; Exim does its own buffering. There is a switch to
- disable this because it breaks some broken clients. */
- if (tcp_nodelay) setsockopt(listen_sockets[sk], IPPROTO_TCP, TCP_NODELAY,
- (uschar *)(&on), sizeof(on));
- /* Now bind the socket to the required port; if Exim is being restarted
- it may not always be possible to bind immediately, even with SO_REUSEADDR
- set, so try 10 times, waiting between each try. After 10 failures, we give
- up. In an IPv6 environment, if bind () fails with the error EADDRINUSE and
- we are doing wildcard IPv4 listening and there was a previous IPv6 wildcard
- address for the same port, ignore the error on the grounds that we must be
- in a system where the IPv6 socket accepts both kinds of call. This is
- necessary for (some release of) USAGI Linux; other IP stacks fail at the
- listen() stage instead. */
- for(;;)
- {
- uschar *msg, *addr;
- if (ip_bind(listen_sockets[sk], af, ipa->address, ipa->port) >= 0) break;
- if (check_special_case(errno, addresses, ipa, TRUE))
- {
- DEBUG(D_any) debug_printf("wildcard IPv4 bind() failed after IPv6 "
- "listen() success; EADDRINUSE ignored\n");
- (void)close(listen_sockets[sk]);
- goto SKIP_SOCKET;
- }
- msg = US strerror(errno);
- addr = wildcard? ((af == AF_INET6)? US"(any IPv6)" : US"(any IPv4)") :
- ipa->address;
- if (daemon_startup_retries <= 0)
- log_write(0, LOG_MAIN|LOG_PANIC_DIE,
- "socket bind() to port %d for address %s failed: %s: "
- "daemon abandoned", ipa->port, addr, msg);
- log_write(0, LOG_MAIN, "socket bind() to port %d for address %s "
- "failed: %s: waiting %s before trying again (%d more %s)",
- ipa->port, addr, msg, readconf_printtime(daemon_startup_sleep),
- daemon_startup_retries, (daemon_startup_retries > 1)? "tries" : "try");
- daemon_startup_retries--;
- sleep(daemon_startup…
Large files files are truncated, but you can click here to view the full file