* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) University of Cambridge 1995 - 2016 */
+/* Copyright (c) University of Cambridge 1995 - 2018 */
/* See the file NOTICE for conditions of use and distribution. */
/* Functions concerned with running Exim as a daemon */
} smtp_slot;
/* An empty slot for initializing (Standard C does not allow constructor
-expressions in assigments except as initializers in declarations). */
+expressions in assignments except as initializers in declarations). */
-static smtp_slot empty_smtp_slot = { 0, NULL };
+static smtp_slot empty_smtp_slot = { .pid = 0, .host_address = NULL };
static SIGNAL_BOOL sigchld_seen;
static SIGNAL_BOOL sighup_seen;
+static SIGNAL_BOOL sigterm_seen;
static int accept_retry_count = 0;
static int accept_retry_errno;
}
+/* SIGTERM handler. Try to get the damon pif file removed
+before exiting. */
+
+static void
+main_sigterm_handler(int sig)
+{
+sigterm_seen = TRUE;
+}
+
+
/*************************************************
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));
+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);
+if (smtp_out) smtp_printf("421 %s\r\n", FALSE, smtp_msg);
}
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 save_log_selector = *log_selector;
-uschar *whofrom = NULL;
+gstring * whofrom;
-void *reset_point = store_get(0);
+rmark reset_point = store_mark();
/* Make the address available in ASCII representation, and also fish out
the remote port. */
{
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");
+ smtp_printf("421 Local problem: getsockname() failed; please try again later\r\n", FALSE);
goto ERROR_RETURN;
}
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, "]");
+whofrom = string_append(NULL, 3, "[", sender_host_address, "]");
if (LOGGING(incoming_port))
- whofrom = string_append(whofrom, &wfsize, &wfptr, 2, ":", string_sprintf("%d",
- sender_host_port));
+ whofrom = string_fmt_append(whofrom, ":%d", sender_host_port);
if (LOGGING(incoming_interface))
- whofrom = string_append(whofrom, &wfsize, &wfptr, 4, " I=[",
- interface_address, "]:", string_sprintf("%d", interface_port));
+ whofrom = string_fmt_append(whofrom, " I=[%s]:%d",
+ interface_address, interface_port);
-whofrom[wfptr] = 0; /* Terminate the newly-built string */
+(void) string_from_gstring(whofrom); /* 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
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");
+ "please try again later.\r\n", FALSE);
log_write(L_connection_reject,
LOG_MAIN, "Connection from %s refused: too many connections",
- whofrom);
+ whofrom->s);
goto ERROR_RETURN;
}
{
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");
+ smtp_printf("421 Too much load; please try again later.\r\n", FALSE);
log_write(L_connection_reject,
LOG_MAIN, "Connection from %s refused: load average = %.2f",
- whofrom, (double)load_average/1000.0);
+ whofrom->s, (double)load_average/1000.0);
goto ERROR_RETURN;
}
}
uschar *expanded = expand_string(smtp_accept_max_per_host);
if (expanded == NULL)
{
- if (!expand_string_forcedfail)
+ if (!f.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);
+ "failed for %s: %s", whofrom->s, expand_string_message);
}
/* For speed, interpret a decimal number inline here */
else
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);
+ "for %s contains non-digit: %s", whofrom->s, expanded);
}
}
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)
+ for (int i = 0; i < smtp_accept_max; ++i)
+ if (smtp_slots[i].host_address)
{
if (Ustrcmp(sender_host_address, smtp_slots[i].host_address) == 0)
host_accept_count++;
"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");
+ "from this IP address; please try again later.\r\n", FALSE);
log_write(L_connection_reject,
LOG_MAIN, "Connection from %s refused: too many connections "
- "from that IP address", whofrom);
+ "from that IP address", whofrom->s);
goto ERROR_RETURN;
}
}
save_log_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);
+ "(TCP/IP connection count = %d)", whofrom->s, smtp_accept_count + 1);
}
/* Now we can fork the accepting process; do a lookup tidy, just in case any
uschar * nah = expand_string(raw_active_hostname);
if (!nah)
{
- if (!expand_string_forcedfail)
+ if (!f.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");
+ "please try again later.\r\n", FALSE);
mac_smtp_fflush();
search_tidyup();
- _exit(EXIT_FAILURE);
+ exim_underbar_exit(EXIT_FAILURE);
}
}
else if (*nah) smtp_active_hostname = nah;
#else
signal(SIGCHLD, SIG_IGN);
#endif
+ signal(SIGTERM, SIG_DFL);
/* 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
finding the id, but turn it on again afterwards so that information about the
incoming connection is output. */
- if (debug_daemon) debug_selector = 0;
+ if (f.debug_daemon) debug_selector = 0;
verify_get_ident(IDENT_PORT);
host_build_sender_fullhost();
debug_selector = save_debug_selector;
/* Now disable debugging permanently if it's required only for the daemon
process. */
- if (debug_daemon) debug_selector = 0;
+ if (f.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
{
mac_smtp_fflush();
search_tidyup();
- _exit(EXIT_SUCCESS);
+ exim_underbar_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 */
+ reset_point = store_mark(); /* Save current store high water point */
DEBUG(D_any)
debug_printf("Process %d is ready for new message\n", (int)getpid());
search_tidyup(); /* Close cached databases */
if (!ok) /* Connection was dropped */
{
+ cancel_cutthrough_connection(TRUE, US"receive dropped");
mac_smtp_fflush();
smtp_log_no_mail(); /* Log no mail if configured */
- _exit(EXIT_SUCCESS);
+ exim_underbar_exit(EXIT_SUCCESS);
}
if (message_id[0] == 0) continue; /* No message was accepted */
}
{
if (smtp_out)
{
- int i, fd = fileno(smtp_in);
+ int fd = fileno(smtp_in);
uschar buf[128];
mac_smtp_fflush();
/* drain socket, for clean TCP FINs */
if (fcntl(fd, F_SETFL, O_NONBLOCK) == 0)
- for(i = 16; read(fd, buf, sizeof(buf)) > 0 && i > 0; ) i--;
+ for(int i = 16; read(fd, buf, sizeof(buf)) > 0 && i > 0; ) i--;
}
+ cancel_cutthrough_connection(TRUE, US"message setup dropped");
search_tidyup();
smtp_log_no_mail(); /* Log no mail if configured */
/*XXX should we pause briefly, hoping that the client will be the
active TCP closer hence get the TCP_WAIT endpoint? */
DEBUG(D_receive) debug_printf("SMTP>>(close on process exit)\n");
- _exit(rc ? EXIT_FAILURE : EXIT_SUCCESS);
+ exim_underbar_exit(rc ? EXIT_FAILURE : EXIT_SUCCESS);
}
/* Show the recipients when debugging */
DEBUG(D_receive)
{
- int i;
- if (sender_address != NULL)
+ if (sender_address)
debug_printf("Sender: %s\n", sender_address);
- if (recipients_list != NULL)
+ if (recipients_list)
{
debug_printf("Recipients:\n");
- for (i = 0; i < recipients_count; i++)
+ for (int i = 0; i < recipients_count; i++)
debug_printf(" %s\n", recipients_list[i].address);
}
}
/* Reclaim up the store used in accepting this message */
- store_reset(reset_point);
+ {
+ int r = receive_messagecount;
+ BOOL q = f.queue_only_policy;
+ smtp_reset(reset_point);
+ reset_point = NULL;
+ f.queue_only_policy = q;
+ receive_messagecount = r;
+ }
/* 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
If we are not root, we have to re-exec exim unless deliveries are being
done unprivileged. */
- else if (!queue_only_policy && !deliver_freeze)
+ else if (!f.queue_only_policy && !f.deliver_freeze)
{
pid_t dpid;
/* 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
+#ifndef DISABLE_TLS
+ tls_close(NULL, TLS_NO_SHUTDOWN);
+#endif
/* Reset SIGHUP and SIGCHLD in the child in both cases. */
signal(SIGHUP, SIG_DFL);
signal(SIGCHLD, SIG_DFL);
+ signal(SIGTERM, 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);
+ delivery_re_exec(CEE_EXEC_PANIC);
/* Control does not return here. */
}
/* No need to re-exec; SIGALRM remains set to the default handler */
- (void)deliver_message(message_id, FALSE, FALSE);
+ (void) deliver_message(message_id, FALSE, FALSE);
search_tidyup();
- _exit(EXIT_SUCCESS);
+ exim_underbar_exit(EXIT_SUCCESS);
}
if (dpid > 0)
{
+ release_cutthrough_connection(US"passed for delivery");
DEBUG(D_any) debug_printf("forked delivery process %d\n", (int)dpid);
}
else
+ {
+ cancel_cutthrough_connection(TRUE, US"delivery fork failed");
log_write(0, LOG_MAIN|LOG_PANIC, "daemon: delivery process fork "
"failed: %s", strerror(errno));
+ }
}
}
}
never_error(US"daemon: accept process fork failed", US"Fork failed", errno);
else
{
- int i;
- for (i = 0; i < smtp_accept_max; ++i)
+ for (int 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)
+ /* Connection closes come asyncronously, so we cannot stack this store */
+ if (smtp_accept_max_per_host)
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");
+ smtp_accept_count, smtp_accept_count == 1 ? "" : "es");
}
/* Get here via goto in error cases */
the incoming host address and an expanded active_hostname. */
log_close_all();
+interface_address =
+sender_host_address = NULL;
store_reset(reset_point);
sender_host_address = NULL;
}
while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
{
- int i;
DEBUG(D_any)
{
debug_printf("child %d ended: status=0x%x\n", (int)pid, status);
/* 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)
+ if (smtp_slots)
{
+ int i;
for (i = 0; i < smtp_accept_max; i++)
- {
if (smtp_slots[i].pid == pid)
{
- if (smtp_slots[i].host_address != NULL)
+ if (smtp_slots[i].host_address)
store_free(smtp_slots[i].host_address);
smtp_slots[i] = empty_smtp_slot;
if (--smtp_accept_count < 0) smtp_accept_count = 0;
smtp_accept_count, (smtp_accept_count == 1)? "" : "es");
break;
}
- }
if (i < smtp_accept_max) continue; /* Found an accepting process */
}
if (queue_pid_slots)
{
int max = atoi(CS expand_string(queue_run_max));
- for (i = 0; i < max; i++)
+ for (int i = 0; i < max; i++)
if (queue_pid_slots[i] == pid)
{
queue_pid_slots[i] = 0;
+static void
+set_pid_file_path(void)
+{
+if (override_pid_file_path)
+ pid_file_path = override_pid_file_path;
+
+if (!*pid_file_path)
+ pid_file_path = string_sprintf("%s/exim-daemon.pid", spool_directory);
+}
+
+
+/* Remove the daemon's pidfile. Note: runs with root privilege,
+as a direct child of the daemon. Does not return. */
+
+void
+delete_pid_file(void)
+{
+uschar * daemon_pid = string_sprintf("%d\n", (int)getppid());
+FILE * f;
+
+set_pid_file_path();
+if ((f = Ufopen(pid_file_path, "rb")))
+ {
+ if ( fgets(CS big_buffer, big_buffer_size, f)
+ && Ustrcmp(daemon_pid, big_buffer) == 0
+ )
+ if (Uunlink(pid_file_path) == 0)
+ {
+ DEBUG(D_any)
+ debug_printf("%s unlink: %s\n", pid_file_path, strerror(errno));
+ }
+ else
+ DEBUG(D_any)
+ debug_printf("unlinked %s\n", pid_file_path);
+ fclose(f);
+ }
+else
+ DEBUG(D_any)
+ debug_printf("%s\n", string_open_failed(errno, "pid file %s",
+ pid_file_path));
+exim_exit(EXIT_SUCCESS, US"pid file remover");
+}
+
+
+/* Called by the daemon; exec a child to get the pid file deleted
+since we may require privs for the containing directory */
+
+static void
+daemon_die(void)
+{
+int pid;
+
+if (f.running_in_test_harness || write_pid)
+ {
+ if ((pid = fork()) == 0)
+ {
+ if (override_pid_file_path)
+ (void)child_exec_exim(CEE_EXEC_PANIC, FALSE, NULL, FALSE, 3,
+ "-oP", override_pid_file_path, "-oPX");
+ else
+ (void)child_exec_exim(CEE_EXEC_PANIC, FALSE, NULL, FALSE, 1, "-oPX");
+
+ /* Control never returns here. */
+ }
+ if (pid > 0)
+ child_close(pid, 1);
+ }
+exim_exit(EXIT_SUCCESS, US"daemon");
+}
+
+
/*************************************************
* Exim Daemon Mainline *
*************************************************/
DEBUG(D_any|D_v) debug_selector |= D_pid;
-if (inetd_wait_mode)
+if (f.inetd_wait_mode)
{
listen_socket_count = 1;
- listen_sockets = store_get(sizeof(int));
+ listen_sockets = store_get(sizeof(int), FALSE);
(void) close(3);
if (dup2(0, 3) == -1)
log_write(0, LOG_MAIN|LOG_PANIC_DIE,
}
-if (inetd_wait_mode || daemon_listen)
+if (f.inetd_wait_mode || f.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
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)
+if (f.daemon_listen && !f.inetd_wait_mode)
{
int *default_smtp_port;
int sep;
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)
+ if (override_local_interfaces)
{
- uschar *new_smtp_port = NULL;
- uschar *new_local_interfaces = NULL;
- int portsize = 0;
- int portptr = 0;
- int ifacesize = 0;
- int ifaceptr = 0;
+ gstring * new_smtp_port = NULL;
+ gstring * new_local_interfaces = NULL;
- if (override_pid_file_path == NULL) write_pid = FALSE;
+ if (!override_pid_file_path) 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;
+ gstring ** gp = Ustrpbrk(s, ".:") ? &new_local_interfaces : &new_smtp_port;
- if (Ustrpbrk(s, ".:") == NULL)
- {
- ptr = &new_smtp_port;
- sizeptr = &portsize;
- ptrptr = &portptr;
- }
- else
- {
- ptr = &new_local_interfaces;
- sizeptr = &ifacesize;
- ptrptr = &ifaceptr;
- }
-
- if (*ptr == NULL)
+ if (!*gp)
{
joinstr[0] = sep;
joinstr[1] = ' ';
- *ptr = string_catn(*ptr, sizeptr, ptrptr, US"<", 1);
+ *gp = string_catn(*gp, US"<", 1);
}
- *ptr = string_catn(*ptr, sizeptr, ptrptr, joinstr, 2);
- *ptr = string_cat (*ptr, sizeptr, ptrptr, s);
+ *gp = string_catn(*gp, joinstr, 2);
+ *gp = string_cat (*gp, s);
}
- if (new_smtp_port != NULL)
+ if (new_smtp_port)
{
- new_smtp_port[portptr] = 0;
- daemon_smtp_port = new_smtp_port;
+ daemon_smtp_port = string_from_gstring(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)
+ if (new_local_interfaces)
{
- new_local_interfaces[ifaceptr] = 0;
- local_interfaces = new_local_interfaces;
+ local_interfaces = string_from_gstring(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
+ contains entries without explicit 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));
+ default_smtp_port = store_get((pct+1) * sizeof(int), FALSE);
list = daemon_smtp_port;
sep = 0;
for (pct = 0;
while ((s = string_nextinlist(&list, &sep, big_buffer, big_buffer_size)))
if (!isdigit(*s))
{
+ gstring * g = NULL;
+
list = tls_in.on_connect_ports;
tls_in.on_connect_ports = NULL;
sep = 0;
{
if (!isdigit(*s))
{
- struct servent *smtp_service = getservbyname(CS s, "tcp");
+ 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));
+ s = string_sprintf("%d", (int)ntohs(smtp_service->s_port));
}
- tls_in.on_connect_ports = string_append_listele(tls_in.on_connect_ports,
- ':', s);
+ g = string_append_listele(g, ':', s);
}
+ if (g)
+ tls_in.on_connect_ports = g->s;
break;
}
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)
+ for (ipa = addresses; ipa; ipa = ipa->next)
{
- int i;
-
- if (Ustrcmp(ipa->address, "0.0.0.0") == 0) ipa->address[0] = 0;
+ if (Ustrcmp(ipa->address, "0.0.0.0") == 0)
+ ipa->address[0] = 0;
else if (Ustrcmp(ipa->address, "::0") == 0)
{
ipa->address[0] = ':';
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->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++)
+ for (int i = 1; default_smtp_port[i] > 0; i++)
{
- ip_address_item *new = store_get(sizeof(ip_address_item));
+ ip_address_item *new = store_get(sizeof(ip_address_item), FALSE);
+
memcpy(new->address, ipa->address, Ustrlen(ipa->address) + 1);
new->port = default_smtp_port[i];
new->next = ipa->next;
also simplifies the construction of the "daemon started" log line. */
pipa = &addresses;
- for (ipa = addresses; ipa != NULL; pipa = &(ipa->next), ipa = ipa->next)
+ for (ipa = addresses; ipa; 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)
+ for (ipa2 = ipa; ipa2->next; ipa2 = ipa2->next)
{
ip_address_item *ipa3 = ipa2->next;
if (ipa3->address[0] == ':' &&
break;
}
}
- }
/* Handle an IPv6 wildcard. */
else if (ipa->address[0] == ':' && ipa->address[1] == 0)
- {
- for (ipa2 = ipa; ipa2->next != NULL; ipa2 = ipa2->next)
+ for (ipa2 = ipa; ipa2->next; ipa2 = ipa2->next)
{
ip_address_item *ipa3 = ipa2->next;
if (ipa3->address[0] == 0 && ipa3->port == ipa->port)
break;
}
}
- }
}
/* Get a vector to remember all the sockets in */
- for (ipa = addresses; ipa != NULL; ipa = ipa->next)
+ for (ipa = addresses; ipa; ipa = ipa->next)
listen_socket_count++;
- listen_sockets = store_get(sizeof(int) * listen_socket_count);
+ listen_sockets = store_get(sizeof(int) * listen_socket_count, FALSE);
} /* daemon_listen but not inetd_wait_mode */
-if (daemon_listen)
+if (f.daemon_listen)
{
/* Do a sanity check on the max connects value just to save us from getting
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;
+ smtp_slots = store_get(smtp_accept_max * sizeof(smtp_slot), FALSE);
+ for (int i = 0; i < smtp_accept_max; i++) smtp_slots[i] = empty_smtp_slot;
}
}
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)
+if (f.background_daemon || f.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. */
log_stderr = NULL; /* So no attempt to copy paniclog output */
}
-if (background_daemon)
+if (f.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
/* We are now in the disconnected, daemon process (unless debugging). Set up
the listening sockets if required. */
-if (daemon_listen && !inetd_wait_mode)
+if (f.daemon_listen && !f.inetd_wait_mode)
{
int sk;
ip_address_item *ipa;
available. Just log failure (can get protocol not available, just like
socket creation can). */
- #ifdef IPV6_V6ONLY
+#ifdef IPV6_V6ONLY
if (af == AF_INET6 && wildcard &&
- setsockopt(listen_sockets[sk], IPPROTO_IPV6, IPV6_V6ONLY, (char *)(&on),
+ setsockopt(listen_sockets[sk], IPPROTO_IPV6, IPV6_V6ONLY, CS (&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 */
+#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)
+ US (&on), sizeof(on)) < 0)
log_write(0, LOG_MAIN|LOG_PANIC_DIE, "setting SO_REUSEADDR on socket "
"failed when starting daemon: %s", strerror(errno));
disable this because it breaks some broken clients. */
if (tcp_nodelay) setsockopt(listen_sockets[sk], IPPROTO_TCP, TCP_NODELAY,
- (uschar *)(&on), sizeof(on));
+ US (&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
necessary for (some release of) USAGI Linux; other IP stacks fail at the
listen() stage instead. */
+#ifdef TCP_FASTOPEN
+ f.tcp_fastopen_ok = TRUE;
+#endif
for(;;)
{
uschar *msg, *addr;
goto SKIP_SOCKET;
}
msg = US strerror(errno);
- addr = wildcard? ((af == AF_INET6)? US"(any IPv6)" : US"(any IPv4)") :
- ipa->address;
+ 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: "
else
debug_printf("listening on %s port %d\n", ipa->address, ipa->port);
-#ifdef TCP_FASTOPEN
- if (setsockopt(listen_sockets[sk], IPPROTO_TCP, TCP_FASTOPEN,
+#if defined(TCP_FASTOPEN) && !defined(__APPLE__)
+ if ( f.tcp_fastopen_ok
+ && setsockopt(listen_sockets[sk], IPPROTO_TCP, TCP_FASTOPEN,
&smtp_connect_backlog, sizeof(smtp_connect_backlog)))
+ {
DEBUG(D_any) debug_printf("setsockopt FASTOPEN: %s\n", strerror(errno));
+ f.tcp_fastopen_ok = FALSE;
+ }
#endif
/* Start listening on the bound socket, establishing the maximum backlog of
connections that is allowed. On success, continue to the next address. */
- if (listen(listen_sockets[sk], smtp_connect_backlog) >= 0) continue;
+ if (listen(listen_sockets[sk], smtp_connect_backlog) >= 0)
+ {
+#if defined(TCP_FASTOPEN) && defined(__APPLE__)
+ if ( f.tcp_fastopen_ok
+ && setsockopt(listen_sockets[sk], IPPROTO_TCP, TCP_FASTOPEN,
+ &on, sizeof(on)))
+ {
+ DEBUG(D_any) debug_printf("setsockopt FASTOPEN: %s\n", strerror(errno));
+ f.tcp_fastopen_ok = FALSE;
+ }
+#endif
+ continue;
+ }
/* Listening has failed. In an IPv6 environment, as for bind(), if listen()
fails with the error EADDRINUSE and we are doing IPv4 wildcard listening
are going to ignore. We remove the address from the chain, and back up the
counts. */
- SKIP_SOCKET:
+ SKIP_SOCKET:
sk--; /* Back up the count */
listen_socket_count--; /* Reduce the total */
if (ipa == addresses) addresses = ipa->next; else
/* If we are not listening, we want to write a pid file only if -oP was
explicitly given. */
-else if (override_pid_file_path == NULL) write_pid = FALSE;
+else if (!override_pid_file_path)
+ write_pid = FALSE;
/* Write the pid to a known file for assistance in identification, if required.
We do this before giving up root privilege, because on some systems it is
The variable daemon_write_pid is used to control this. */
-if (running_in_test_harness || write_pid)
+if (f.running_in_test_harness || write_pid)
{
FILE *f;
- if (override_pid_file_path != NULL)
- pid_file_path = override_pid_file_path;
-
- if (pid_file_path[0] == 0)
- pid_file_path = string_sprintf("%s/exim-daemon.pid", spool_directory);
-
- f = modefopen(pid_file_path, "wb", 0644);
- if (f != NULL)
+ set_pid_file_path();
+ if ((f = modefopen(pid_file_path, "wb", 0644)))
{
(void)fprintf(f, "%d\n", (int)getpid());
(void)fclose(f);
DEBUG(D_any) debug_printf("pid written to %s\n", pid_file_path);
}
else
- {
DEBUG(D_any)
debug_printf("%s\n", string_open_failed(errno, "pid file %s",
pid_file_path));
- }
}
/* Set up the handler for SIGHUP, which causes a restart of the daemon. */
originator_uid = exim_uid;
originator_gid = exim_gid;
-originator_login = ((pw = getpwuid(exim_uid)) != NULL)?
- string_copy_malloc(US pw->pw_name) : US"exim";
+originator_login = (pw = getpwuid(exim_uid))
+ ? string_copy_perm(US pw->pw_name, FALSE) : US"exim";
/* Get somewhere to keep the list of queue-runner pids if we are keeping track
of them (and also if we are doing queue runs). */
if (queue_interval > 0 && local_queue_run_max > 0)
{
- int i;
- queue_pid_slots = store_get(local_queue_run_max * sizeof(pid_t));
- for (i = 0; i < local_queue_run_max; i++) queue_pid_slots[i] = 0;
+ queue_pid_slots = store_get(local_queue_run_max * sizeof(pid_t), FALSE);
+ for (int i = 0; i < local_queue_run_max; i++) queue_pid_slots[i] = 0;
}
-/* Set up the handler for termination of child processes. */
+/* Set up the handler for termination of child processes, and the one
+telling us to die. */
sigchld_seen = FALSE;
os_non_restarting_signal(SIGCHLD, main_sigchld_handler);
+sigterm_seen = FALSE;
+os_non_restarting_signal(SIGTERM, main_sigterm_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. */
/* Log the start up of a daemon - at least one of listening or queue running
must be set up. */
-if (inetd_wait_mode)
+if (f.inetd_wait_mode)
{
uschar *p = big_buffer;
sigalrm_seen = 1;
}
-else if (daemon_listen)
+else if (f.daemon_listen)
{
- int i, j;
int smtp_ports = 0;
int smtps_ports = 0;
ip_address_item * ipa;
- uschar * p = big_buffer;
+ uschar * p;
uschar * qinfo = queue_interval > 0
? string_sprintf("-q%s", readconf_printtime(queue_interval))
: US"no queue runs";
deprecated protocol that starts TLS without using STARTTLS), and others
listening for standard SMTP. Keep their listings separate. */
- for (j = 0; j < 2; j++)
+ for (int j = 0, i; j < 2; j++)
{
for (i = 0, ipa = addresses; i < 10 && ipa; i++, ipa = ipa->next)
- {
- /* First time round, look for SMTP ports; second time round, look for
- SMTPS ports. For the first one of each, insert leading text. */
-
- if (host_is_tls_on_connect_port(ipa->port) == (j > 0))
- {
- if (j == 0)
- {
- if (smtp_ports++ == 0)
- {
- memcpy(p, "SMTP on", 8);
- p += 7;
- }
- }
- else
- {
- if (smtps_ports++ == 0)
- {
- (void)sprintf(CS p, "%sSMTPS on",
- smtp_ports == 0 ? "" : " and for ");
- while (*p) p++;
- }
- }
-
- /* Now the information about the port (and sometimes interface) */
-
- if (ipa->address[0] == ':' && ipa->address[1] == 0)
- {
- if (ipa->next != NULL && ipa->next->address[0] == 0 &&
- ipa->next->port == ipa->port)
- {
- (void)sprintf(CS p, " port %d (IPv6 and IPv4)", ipa->port);
- ipa = ipa->next;
- }
- else if (ipa->v6_include_v4)
- (void)sprintf(CS p, " port %d (IPv6 with IPv4)", ipa->port);
- else
- (void)sprintf(CS p, " port %d (IPv6)", ipa->port);
- }
- else if (ipa->address[0] == 0)
- (void)sprintf(CS p, " port %d (IPv4)", ipa->port);
- else
- (void)sprintf(CS p, " [%s]:%d", ipa->address, ipa->port);
- while (*p != 0) p++;
- }
- }
+ {
+ /* First time round, look for SMTP ports; second time round, look for
+ SMTPS ports. Build IP+port strings. */
- if (ipa)
+ if (host_is_tls_on_connect_port(ipa->port) == (j > 0))
+ {
+ if (j == 0)
+ smtp_ports++;
+ else
+ smtps_ports++;
+
+ /* Now the information about the port (and sometimes interface) */
+
+ if (ipa->address[0] == ':' && ipa->address[1] == 0)
+ { /* v6 wildcard */
+ if (ipa->next && ipa->next->address[0] == 0 &&
+ ipa->next->port == ipa->port)
+ {
+ ipa->log = string_sprintf(" port %d (IPv6 and IPv4)", ipa->port);
+ (ipa = ipa->next)->log = NULL;
+ }
+ else if (ipa->v6_include_v4)
+ ipa->log = string_sprintf(" port %d (IPv6 with IPv4)", ipa->port);
+ else
+ ipa->log = string_sprintf(" port %d (IPv6)", ipa->port);
+ }
+ else if (ipa->address[0] == 0) /* v4 wildcard */
+ ipa->log = string_sprintf(" port %d (IPv4)", ipa->port);
+ else /* check for previously-seen IP */
+ {
+ ip_address_item * i2;
+ for (i2 = addresses; i2 != ipa; i2 = i2->next)
+ if ( host_is_tls_on_connect_port(i2->port) == (j > 0)
+ && Ustrcmp(ipa->address, i2->address) == 0
+ )
+ { /* found; append port to list */
+ for (p = i2->log; *p; ) p++; /* end of existing string */
+ if (*--p == '}') *p = '\0'; /* drop EOL */
+ while (isdigit(*--p)) ; /* char before port */
+
+ i2->log = *p == ':' /* no list yet? */
+ ? string_sprintf("%.*s{%s,%d}",
+ (int)(p - i2->log + 1), i2->log, p+1, ipa->port)
+ : string_sprintf("%s,%d}", i2->log, ipa->port);
+ ipa->log = NULL;
+ break;
+ }
+ if (i2 == ipa) /* first-time IP */
+ ipa->log = string_sprintf(" [%s]:%d", ipa->address, ipa->port);
+ }
+ }
+ }
+ }
+
+ p = big_buffer;
+ for (int j = 0, i; j < 2; j++)
+ {
+ /* First time round, look for SMTP ports; second time round, look for
+ SMTPS ports. For the first one of each, insert leading text. */
+
+ if (j == 0)
{
- memcpy(p, " ...", 5);
- p += 4;
+ if (smtp_ports > 0)
+ p += sprintf(CS p, "SMTP on");
}
+ else
+ if (smtps_ports > 0)
+ p += sprintf(CS p, "%sSMTPS on",
+ smtp_ports == 0 ? "" : " and for ");
+
+ /* Now the information about the port (and sometimes interface) */
+
+ for (i = 0, ipa = addresses; i < 10 && ipa; i++, ipa = ipa->next)
+ if (host_is_tls_on_connect_port(ipa->port) == (j > 0))
+ if (ipa->log)
+ p += sprintf(CS p, "%s", ipa->log);
+
+ if (ipa)
+ p += sprintf(CS p, " ...");
}
log_write(0, LOG_MAIN,
(eg: compile regex) */
dns_pattern_init();
+smtp_deliver_init(); /* Used for callouts */
+
+#ifndef DISABLE_DKIM
+ {
+# ifdef MEASURE_TIMING
+ struct timeval t0;
+ gettimeofday(&t0, NULL);
+# endif
+ dkim_exim_init();
+# ifdef MEASURE_TIMING
+ report_time_since(&t0, US"dkim_exim_init (delta)");
+# endif
+ }
+#endif
#ifdef WITH_CONTENT_SCAN
malware_init();
#endif
+#ifdef SUPPORT_SPF
+spf_init();
+#endif
/* Close the log so it can be renamed and moved. In the few cases below where
this long-running process writes to the log (always exceptional conditions), it
smtp_input = TRUE;
+#ifdef MEASURE_TIMING
+report_time_since(×tamp_startup, US"daemon loop start"); /* testcase 0022 */
+#endif
+
/* Enter the never-ending loop... */
for (;;)
EXIM_SOCKLEN_T len;
pid_t pid;
+ if (sigterm_seen)
+ daemon_die(); /* Does not return */
+
/* This code is placed first in the loop, so that it gets obeyed at the
start, before the first wait, for the queue-runner case, so that the first
one can be started immediately.
}
sigalrm_seen = FALSE;
- alarm(resignal_interval);
+ ALARM(resignal_interval);
}
else
{
if ((pid = fork()) == 0)
{
- int sk;
-
DEBUG(D_any) debug_printf("Starting queue-runner: pid %d\n",
(int)getpid());
leave the above message, because it ties up with the "child ended"
debugging messages. */
- if (debug_daemon) debug_selector = 0;
+ if (f.debug_daemon) debug_selector = 0;
/* Close any open listening sockets in the child */
- for (sk = 0; sk < listen_socket_count; sk++)
+ for (int sk = 0; sk < listen_socket_count; sk++)
(void)close(listen_sockets[sk]);
/* Reset SIGHUP and SIGCHLD in the child in both cases. */
signal(SIGHUP, SIG_DFL);
signal(SIGCHLD, SIG_DFL);
+ signal(SIGTERM, SIG_DFL);
/* Re-exec if privilege has been given up, unless deliver_drop_
privilege is set. Reset SIGALRM before exec(). */
signal(SIGALRM, SIG_DFL);
*p++ = '-';
*p++ = 'q';
- if (queue_2stage) *p++ = 'q';
- if (queue_run_first_delivery) *p++ = 'i';
- if (queue_run_force) *p++ = 'f';
- if (deliver_force_thaw) *p++ = 'f';
- if (queue_run_local) *p++ = 'l';
+ if (f.queue_2stage) *p++ = 'q';
+ if (f.queue_run_first_delivery) *p++ = 'i';
+ if (f.queue_run_force) *p++ = 'f';
+ if (f.deliver_force_thaw) *p++ = 'f';
+ if (f.queue_run_local) *p++ = 'l';
*p = 0;
extra[0] = queue_name
? string_sprintf("%sG%s", opt, queue_name) : opt;
if (deliver_selectstring)
{
- extra[extracount++] = deliver_selectstring_regex ? US"-Rr" : US"-R";
+ extra[extracount++] = f.deliver_selectstring_regex ? US"-Rr" : US"-R";
extra[extracount++] = deliver_selectstring;
}
if (deliver_selectstring_sender)
{
- extra[extracount++] = deliver_selectstring_sender_regex
+ extra[extracount++] = f.deliver_selectstring_sender_regex
? US"-Sr" : US"-S";
extra[extracount++] = deliver_selectstring_sender;
}
/* No need to re-exec; SIGALRM remains set to the default handler */
queue_run(NULL, NULL, FALSE);
- _exit(EXIT_SUCCESS);
+ exim_underbar_exit(EXIT_SUCCESS);
}
if (pid < 0)
}
else
{
- int i;
- for (i = 0; i < local_queue_run_max; ++i)
+ for (int i = 0; i < local_queue_run_max; ++i)
if (queue_pid_slots[i] <= 0)
{
queue_pid_slots[i] = pid;
break;
}
DEBUG(D_any) debug_printf("%d queue-runner process%s running\n",
- queue_run_count, (queue_run_count == 1)? "" : "es");
+ queue_run_count, queue_run_count == 1 ? "" : "es");
}
}
/* Reset the alarm clock */
sigalrm_seen = FALSE;
- alarm(queue_interval);
+ ALARM(queue_interval);
}
} /* sigalrm_seen */
new OS. In fact, the later addition of listening on specific interfaces only
requires this way of working anyway. */
- if (daemon_listen)
+ if (f.daemon_listen)
{
- int sk, lcount, select_errno;
+ int lcount, select_errno;
int max_socket = 0;
BOOL select_failed = FALSE;
fd_set select_listen;
FD_ZERO(&select_listen);
- for (sk = 0; sk < listen_socket_count; sk++)
+ for (int sk = 0; sk < listen_socket_count; sk++)
{
FD_SET(listen_sockets[sk], &select_listen);
if (listen_sockets[sk] > max_socket) max_socket = listen_sockets[sk];
errno = EINTR;
}
else
- {
lcount = select(max_socket + 1, (SELECT_ARG2_TYPE *)&select_listen,
NULL, NULL, NULL);
- }
if (lcount < 0)
{
handle_ending_processes();
errno = select_errno;
+#ifndef DISABLE_TLS
+ /* Create or rotate any required keys */
+ tls_daemon_init();
+#endif
+
/* Loop for all the sockets that are currently ready to go. If select
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)
{
int accept_socket = -1;
+
if (!select_failed)
- {
- for (sk = 0; sk < listen_socket_count; sk++)
- {
+ for (int sk = 0; sk < listen_socket_count; sk++)
if (FD_ISSET(listen_sockets[sk], &select_listen))
{
len = sizeof(accepted);
FD_CLR(listen_sockets[sk], &select_listen);
break;
}
- }
- }
/* If select or accept has failed and this was not caused by an
interruption, log the incident and try again. With asymmetric TCP/IP
if (sighup_seen)
{
- int sk;
log_write(0, LOG_MAIN, "pid %d: SIGHUP received: re-exec daemon",
getpid());
- for (sk = 0; sk < listen_socket_count; sk++)
+ for (int sk = 0; sk < listen_socket_count; sk++)
(void)close(listen_sockets[sk]);
- alarm(0);
+ ALARM_CLR(0);
signal(SIGHUP, SIG_IGN);
sighup_argv[0] = exim_path;
exim_nullstd();