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;
+}
+
+
/*************************************************
int save_log_selector = *log_selector;
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. */
"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
{
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());
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 */
}
/*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 */
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;
}
/* Don't ever molest the parent's SSL connection, but do clean up
the data structures if necessary. */
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
tls_close(NULL, TLS_NO_SHUTDOWN);
#endif
signal(SIGHUP, SIG_DFL);
signal(SIGCHLD, SIG_DFL);
+ signal(SIGTERM, SIG_DFL);
if (geteuid() != root_uid && !deliver_drop_privilege)
{
(void) deliver_message(message_id, FALSE, FALSE);
search_tidyup();
- _exit(EXIT_SUCCESS);
+ exim_underbar_exit(EXIT_SUCCESS);
}
if (dpid > 0)
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 */
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 (smtp_slots)
{
+ int i;
for (i = 0; i < smtp_accept_max; i++)
if (smtp_slots[i].pid == pid)
{
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 *
*************************************************/
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,
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];
- gstring ** gp;
-
- if (Ustrpbrk(s, ".:") == NULL)
- gp = &new_smtp_port;
- else
- gp = &new_local_interfaces;
+ gstring ** gp = Ustrpbrk(s, ".:") ? &new_local_interfaces : &new_smtp_port;
if (!*gp)
{
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;
ipa->port = default_smtp_port[0];
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];
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 (smtp_accept_max > 0)
{
- smtp_slots = store_get(smtp_accept_max * sizeof(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;
}
}
{
FILE *f;
- if (override_pid_file_path)
- pid_file_path = override_pid_file_path;
-
- if (pid_file_path[0] == 0)
- pid_file_path = string_sprintf("%s/exim-daemon.pid", spool_directory);
-
+ set_pid_file_path();
if ((f = modefopen(pid_file_path, "wb", 0644)))
{
(void)fprintf(f, "%d\n", (int)getpid());
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)
{
- queue_pid_slots = store_get(local_queue_run_max * sizeof(pid_t));
+ 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. */
(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.
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(). */
/* 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)
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. */