X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/1e835086d1592bdfbcd8577133965b78470840ac..46ad960434683b3e15eb17edf3b2d748c191fbc4:/src/src/daemon.c diff --git a/src/src/daemon.c b/src/src/daemon.c index b0533c28f..88114d648 100644 --- a/src/src/daemon.c +++ b/src/src/daemon.c @@ -2,8 +2,8 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2022 */ -/* Copyright (c) University of Cambridge 1995 - 2023 */ +/* Copyright (c) The Exim Maintainers 2020 - 2023 */ +/* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ @@ -128,7 +128,7 @@ 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) smtp_printf("421 %s\r\n", FALSE, smtp_msg); +if (smtp_out) smtp_printf("421 %s\r\n", SP_NO_MORE, smtp_msg); } @@ -181,7 +181,7 @@ Returns: nothing */ static void -handle_smtp_call(struct pollfd *fd_polls, int listen_socket_count, +handle_smtp_call(struct pollfd * fd_polls, int listen_socket_count, int accept_socket, struct sockaddr *accepted) { pid_t pid; @@ -233,7 +233,7 @@ if (getsockname(accept_socket, (struct sockaddr *)(&interface_sockaddr), { 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", FALSE); + smtp_printf("421 Local problem: getsockname() failed; please try again later\r\n", SP_NO_MORE); goto ERROR_RETURN; } @@ -254,8 +254,6 @@ if (LOGGING(incoming_interface)) whofrom = string_fmt_append(whofrom, " I=[%s]:%d", interface_address, interface_port); -(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 it might take some time. */ @@ -265,10 +263,10 @@ 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", FALSE); + "please try again later.\r\n", SP_NO_MORE); log_write(L_connection_reject, - LOG_MAIN, "Connection from %s refused: too many connections", - whofrom->s); + LOG_MAIN, "Connection from %Y refused: too many connections", + whofrom); goto ERROR_RETURN; } @@ -284,10 +282,10 @@ if (smtp_load_reserve >= 0) { 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", FALSE); + smtp_printf("421 Too much load; please try again later.\r\n", SP_NO_MORE); log_write(L_connection_reject, - LOG_MAIN, "Connection from %s refused: load average = %.2f", - whofrom->s, (double)load_average/1000.0); + LOG_MAIN, "Connection from %Y refused: load average = %.2f", + whofrom, (double)load_average/1000.0); goto ERROR_RETURN; } } @@ -300,14 +298,15 @@ 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. */ +GET_OPTION("smtp_accept_max_per_host"); if (smtp_accept_max_per_host) { - uschar *expanded = expand_string(smtp_accept_max_per_host); + uschar * expanded = expand_string(smtp_accept_max_per_host); if (!expanded) { if (!f.expand_string_forcedfail) log_write(0, LOG_MAIN|LOG_PANIC, "expansion of smtp_accept_max_per_host " - "failed for %s: %s", whofrom->s, expand_string_message); + "failed for %Y: %s", whofrom, expand_string_message); } /* For speed, interpret a decimal number inline here */ else @@ -317,7 +316,7 @@ if (smtp_accept_max_per_host) max_for_this_host = max_for_this_host * 10 + *s++ - '0'; if (*s) log_write(0, LOG_MAIN|LOG_PANIC, "expansion of smtp_accept_max_per_host " - "for %s contains non-digit: %s", whofrom->s, expanded); + "for %Y contains non-digit: %s", whofrom, expanded); } } @@ -353,40 +352,17 @@ if (max_for_this_host > 0 && smtp_accept_count >= max_for_this_host) "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", FALSE); + "from this IP address; please try again later.\r\n", SP_NO_MORE); log_write(L_connection_reject, - LOG_MAIN, "Connection from %s refused: too many connections " - "from that IP address", whofrom->s); + LOG_MAIN, "Connection from %Y refused: too many connections " + "from that IP address", whofrom); search_tidyup(); 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 (LOGGING(smtp_connection)) - { - uschar *list = hosts_connection_nolog; - memset(sender_host_cache, 0, sizeof(sender_host_cache)); - if (list && verify_check_host(&list) == OK) - save_log_selector &= ~L_smtp_connection; - else - log_write(L_smtp_connection, LOG_MAIN, "SMTP connection from %s " - "(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 +/* OK, the connection count checks have been passed. +Now we can fork the accepting process; do a lookup tidy, just in case any expansion above did a lookup. */ search_tidyup(); @@ -406,6 +382,33 @@ if (pid == 0) #endif smtp_accept_count++; /* So that it includes this process */ + connection_id = getpid(); + + /* Log the connection if requested. + 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. + + jgh 2023/08/08 :- moved this logging in from the parent process, just + pre-fork. There was a claim back from 2004 that smtp_accept_count could have + become out-of-date by the time the child could log it, and I can't see how + that could happen. */ + + if (LOGGING(smtp_connection)) + { + uschar * list = hosts_connection_nolog; + memset(sender_host_cache, 0, sizeof(sender_host_cache)); + if (list && verify_check_host(&list) == OK) + save_log_selector &= ~L_smtp_connection; + else if (LOGGING(connection_id)) + log_write(L_smtp_connection, LOG_MAIN, "SMTP connection from %Y " + "Ci=%lu (TCP/IP connection count = %d)", whofrom, connection_id, smtp_accept_count); + else + log_write(L_smtp_connection, LOG_MAIN, "SMTP connection from %Y " + "(TCP/IP connection count = %d)", whofrom, smtp_accept_count); + } /* If the listen backlog was over the monitoring level, log it. */ @@ -432,6 +435,7 @@ if (pid == 0) likely what it depends on.) */ smtp_active_hostname = primary_hostname; + GET_OPTION("smtp_active_hostname"); if (raw_active_hostname) { uschar * nah = expand_string(raw_active_hostname); @@ -443,7 +447,7 @@ if (pid == 0) "(smtp_active_hostname): %s", raw_active_hostname, expand_string_message); smtp_printf("421 Local configuration error; " - "please try again later.\r\n", FALSE); + "please try again later.\r\n", SP_NO_MORE); mac_smtp_fflush(); search_tidyup(); exim_underbar_exit(EXIT_FAILURE); @@ -564,7 +568,7 @@ if (pid == 0) smtp_log_no_mail(); /* Log no mail if configured */ exim_underbar_exit(EXIT_SUCCESS); } - if (message_id[0] == 0) continue; /* No message was accepted */ + if (!message_id[0]) continue; /* No message was accepted */ } else /* bad smtp_setup_msg() */ { @@ -1163,6 +1167,7 @@ return offsetof(struct sockaddr_un, sun_path) ssize_t daemon_notifier_sockname(struct sockaddr_un * sup) { +GET_OPTION("notifier_socket"); #ifdef EXIM_HAVE_ABSTRACT_UNIX_SOCKETS sup->sun_path[0] = 0; /* Abstract local socket addr - Linux-specific? */ return offsetof(struct sockaddr_un, sun_path) + 1 @@ -1256,10 +1261,9 @@ static const uschar * queuerun_msg_qname; /* The notifier socket has something to read. Pull the message from it, decode and do the action. +*/ -Return TRUE if a sigalrm should be emulated */ - -static BOOL +static void daemon_notification(void) { uschar buf[256], cbuf[256]; @@ -1275,8 +1279,8 @@ struct msghdr msg = { .msg_name = &sa_un, ssize_t sz; buf[sizeof(buf)-1] = 0; -if ((sz = recvmsg(daemon_notifier_fd, &msg, 0)) <= 0) return FALSE; -if (sz >= sizeof(buf)) return FALSE; +if ((sz = recvmsg(daemon_notifier_fd, &msg, 0)) <= 0) return; +if (sz >= sizeof(buf)) return; #ifdef notdef debug_printf("addrlen %d\n", msg.msg_namelen); @@ -1344,9 +1348,12 @@ switch (buf[0]) memcpy(queuerun_msgid, buf+1, MESSAGE_ID_LENGTH+1); for (qrunner * q = qrunners; q; q = q->next) - if (Ustrcmp(q->name, buf+1+MESSAGE_ID_LENGTH+1) == 0) + if (q->name + ? Ustrcmp(q->name, buf+1+MESSAGE_ID_LENGTH+1) == 0 + : !buf[1+MESSAGE_ID_LENGTH+1] + ) { queuerun_msg_qname = q->name; break; } - return TRUE; + return; #endif case NOTIFY_QUEUE_SIZE_REQ: @@ -1368,7 +1375,7 @@ switch (buf[0]) regex_at_daemon(buf); break; } -return FALSE; +return; } @@ -1427,7 +1434,7 @@ for (qrunner * q = qrunners, * next; q; q = next) if (sorted) { qrunner ** p = &sorted; - for (qrunner * qq; qq = *p; p = &(qq->next)) + for (qrunner * qq; qq = *p; p = &qq->next) if ( q->next_tick < qq->next_tick || q->next_tick == qq->next_tick && q->interval < qq->interval ) @@ -1446,6 +1453,13 @@ qrunners = sorted; return qrunners ? qrunners->next_tick - time(NULL) : 0; } +/* See if we can do a queue run. If policy limit permit, kick one off. +If both notification and timer events are present, handle the former +and leave the timer outstanding. + +Return the number of seconds until the next due runner. +*/ + static int daemon_qrun(int local_queue_run_max, struct pollfd * fd_polls, int listen_socket_count) { @@ -1459,13 +1473,16 @@ DEBUG(D_any) debug_printf("%s received\n", enough queue runners on the go. If we are not running as root, a re-exec is required. In the calling process, restart the alamr timer for the next run. */ -if (is_multiple_qrun()) +if (is_multiple_qrun()) /* we are managing periodic runs */ if (local_queue_run_max <= 0 || queue_run_count < local_queue_run_max) { qrunner * q = NULL; #ifndef DISABLE_QUEUE_RAMP - if (*queuerun_msgid) /* See if we can start another runner for this queue */ + /* If this is a triggered run for a specific message, see if we can start + another runner for this queue. */ + + if (*queuerun_msgid) { for (qrunner * qq = qrunners; qq; qq = qq->next) if (qq->name == queuerun_msg_qname) @@ -1476,13 +1493,13 @@ if (is_multiple_qrun()) } else #endif - /* In order of run priority, find the first queue for which we can start - a runner */ + /* Normal periodic run: in order of run priority, find the first queue + for which we can start a runner */ for (q = qrunners; q; q = q->next) if (q->run_count < q->run_max) break; - if (q) + if (q) /* found a queue to run */ { pid_t pid; @@ -1614,19 +1631,23 @@ if (is_multiple_qrun()) } } -sigalrm_seen = FALSE; +/* The queue run has been initiated (unless we were already running enough) */ + #ifndef DISABLE_QUEUE_RAMP -if (*queuerun_msgid) /* it was a fast-ramp kick */ +if (*queuerun_msgid) /* it was a fast-ramp kick; dealt with */ *queuerun_msgid = 0; else /* periodic or one-time queue run */ #endif - { /* Impose a minimum 1s tick, even when a run was outstanding */ + /* Set up next timer callback. Impose a minimum 1s tick, + even when a run was outstanding */ + { int interval = next_qrunner_interval(); if (interval <= 0) interval = 1; + sigalrm_seen = FALSE; if (qrunners) /* there are still periodic qrunners */ { - ALARM(interval); + ALARM(interval); /* set up next qrun tick */ return interval; } } @@ -1636,7 +1657,7 @@ return 0; -const uschar * +static const uschar * describe_queue_runners(void) { gstring * g = NULL; @@ -1646,6 +1667,7 @@ if (!is_multiple_qrun()) return US"no queue runs"; for (qrunner * q = qrunners; q; q = q->next) { g = string_catn(g, US"-q", 2); + if (q->queue_2stage) g = string_catn(g, US"q", 1); if (q->name) g = string_append(g, 3, US"G", q->name, US"/"); g = string_cat(g, readconf_printtime(q->interval)); g = string_catn(g, US" ", 1); @@ -1688,15 +1710,15 @@ int listen_socket_count = 0, poll_fd_count; ip_address_item * addresses = NULL; time_t last_connection_time = (time_t)0; int local_queue_run_max = 0; -BOOL queue_run_max_has_dollar; if (is_multiple_qrun()) - + { /* Nuber of runner-tracking structs needed: If the option queue_run_max has no expandable elements then it is the overall maximum; else we assume it depends on the queue name, and add them up to get the maximum. Evaluate both that and the individual limits. */ + GET_OPTION("queue_run_max"); if (Ustrchr(queue_run_max, '$') != NULL) { for (qrunner * q = qrunners; q; q = q->next) @@ -1713,6 +1735,7 @@ if (is_multiple_qrun()) for (qrunner * q = qrunners; q; q = q->next) q->run_max = local_queue_run_max; } + } process_purpose = US"daemon"; @@ -2608,7 +2631,7 @@ for (;;) The other option is that we have an inetd wait timeout specified to -bw. */ - if (sigalrm_seen) + if (sigalrm_seen || *queuerun_msgid) if (inetd_wait_timeout > 0) daemon_inetd_wtimeout(last_connection_time); /* Might not return */ else @@ -2637,7 +2660,9 @@ for (;;) select() was interrupted so that we reap the child. This might still leave a small window when a SIGCHLD could get lost. However, since we use SIGCHLD only to do the reaping more quickly, it shouldn't result in anything other - than a delay until something else causes a wake-up. */ + than a delay until something else causes a wake-up. + For the normal case, wait for either a pollable fd (eg. new connection) or + or a SIGALRM (for a periodic queue run) */ if (sigchld_seen) { @@ -2702,10 +2727,13 @@ for (;;) break; /* to top of daemon loop */ } #endif + /* Handle the daemon-notifier socket. If it was a fast-ramp + notification then queuerun_msgid will have a nonzerolength string. */ + if (dnotify_poll && dnotify_poll->revents & POLLIN) { dnotify_poll->revents = 0; - sigalrm_seen = daemon_notification(); + daemon_notification(); break; /* to top of daemon loop */ } for (struct pollfd * p = fd_polls; p < fd_polls + listen_socket_count;