cppcheck silencing
[exim.git] / src / src / exim.c
index 1e5643713c7e08bf45db529663c236bfd043768d..201bf6e8e5cb92d17a601cf45bab882c0c5592c3 100644 (file)
@@ -2,7 +2,7 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) The Exim Maintainers 2020 - 2023 */
+/* Copyright (c) The Exim Maintainers 2020 - 2024 */
 /* 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 */
@@ -33,6 +33,7 @@ Also a few functions that don't naturally fit elsewhere. */
 #endif
 
 extern void init_lookup_list(void);
+extern void init_misc_mod_list(void);
 
 
 
@@ -237,7 +238,7 @@ va_end(ap);
 static void
 term_handler(int sig)
 {
-exit(1);
+exim_exit(EXIT_FAILURE);
 }
 
 
@@ -256,6 +257,12 @@ int nptrs = backtrace(buf, STACKDUMP_MAX);
 
 log_write(0, LOG_MAIN|LOG_PANIC, "backtrace");
 log_write(0, LOG_MAIN|LOG_PANIC, "---");
+
+/* This function is officially not callable from a signal handler, as it
+calls malloc() for the returned data. However, it seems to work - and we
+know we're going on to crash anyway - so just hold our noses and do it.
+A alternative might be backtrace_symbols_fd(). */
+
 if ((ss = backtrace_symbols(buf, nptrs)))
   {
   for (int i = 0; i < nptrs; i++)
@@ -274,22 +281,28 @@ static void
 #ifdef SA_SIGINFO
 segv_handler(int sig, siginfo_t * info, void * uctx)
 {
-log_write(0, LOG_MAIN|LOG_PANIC, "SIGSEGV (fault address: %p)", info->si_addr);
-# if defined(SEGV_MAPERR) && defined(SEGV_ACCERR) && defined(SEGV_BNDERR) && defined(SEGV_PKUERR)
-switch (info->si_code)
+if (!panic_coredump)
   {
-  case SEGV_MAPERR: log_write(0, LOG_MAIN|LOG_PANIC, "SEGV_MAPERR"); break;
-  case SEGV_ACCERR: log_write(0, LOG_MAIN|LOG_PANIC, "SEGV_ACCERR"); break;
-  case SEGV_BNDERR: log_write(0, LOG_MAIN|LOG_PANIC, "SEGV_BNDERR"); break;
-  case SEGV_PKUERR: log_write(0, LOG_MAIN|LOG_PANIC, "SEGV_PKUERR"); break;
+  log_write(0, LOG_MAIN|LOG_PANIC, "SIGSEGV (fault address: %p)", info->si_addr);
+  # if defined(SEGV_MAPERR) && defined(SEGV_ACCERR) && defined(SEGV_BNDERR) && defined(SEGV_PKUERR)
+  switch (info->si_code)
+    {
+    case SEGV_MAPERR: log_write(0, LOG_MAIN|LOG_PANIC, "SEGV_MAPERR"); break;
+    case SEGV_ACCERR: log_write(0, LOG_MAIN|LOG_PANIC, "SEGV_ACCERR"); break;
+    case SEGV_BNDERR: log_write(0, LOG_MAIN|LOG_PANIC, "SEGV_BNDERR"); break;
+    case SEGV_PKUERR: log_write(0, LOG_MAIN|LOG_PANIC, "SEGV_PKUERR"); break;
+    }
+  # endif
   }
-# endif
-if (US info->si_addr < US 4096)
+if (panic_coredump)
+  log_write(0, LOG_MAIN|LOG_PANIC, "SIGSEGV (deliberate trap)");
+else if (US info->si_addr < US 4096)
   log_write(0, LOG_MAIN|LOG_PANIC, "SIGSEGV (null pointer indirection)");
 else
   log_write(0, LOG_MAIN|LOG_PANIC, "SIGSEGV (maybe attempt to write to immutable memory)");
 if (process_info_len > 0)
-  log_write(0, LOG_MAIN|LOG_PANIC, "SIGSEGV (%.*s)", process_info_len, process_info);
+  log_write(0, LOG_MAIN|LOG_PANIC, "SIGSEGV (%s: %.*s)",
+    process_purpose, process_info_len, process_info);
 stackdump();
 signal(SIGSEGV, SIG_DFL);
 kill(getpid(), sig);
@@ -339,7 +352,7 @@ to disrupt whatever is going on outside the signal handler. */
 
 if (fd < 0) return;
 
-(void)write(fd, process_info, process_info_len);
+if (write(fd, process_info, process_info_len) != 0) ;
 (void)close(fd);
 }
 
@@ -871,6 +884,14 @@ exim_len_fail_toolong(Ustrlen(item), maxlen, description);
 return item;
 }
 
+/* as above, copying as tainted */
+static inline const uschar *
+exim_arg_copy(const uschar * item, int maxlen, const char * description)
+{
+return string_copy_taint(exim_str_fail_toolong(item, maxlen, description),
+                       GET_TAINTED);
+}
+
 /* exim_chown_failure() called from exim_chown()/exim_fchown() on failure
 of chown()/fchown().  See src/functions.h for more explanation */
 int
@@ -1032,6 +1053,169 @@ return g;
 }
 
 
+static gstring *
+lookup_show_supported(gstring * g)
+{
+gstring * b = NULL, * d = NULL;
+
+#ifdef LOOKUP_LSEARCH
+# if LOOKUP_LSEARCH!=2
+  b = string_cat(b, US" lsearch wildlsearch nwildlsearch iplsearch");
+# else
+  d = string_cat(d, US" lsearch wildlsearch nwildlsearch iplsearch");
+# endif
+#endif
+#ifdef LOOKUP_CDB
+# if LOOKUP_CDB!=2
+  b = string_cat(b, US" cdb");
+# else
+  d = string_cat(d, US" cdb");
+# endif
+#endif
+#ifdef LOOKUP_DBM
+# if LOOKUP_DBM!=2
+  b = string_cat(b, US" dbm dbmjz dbmnz");
+# else
+  d = string_cat(d, US" dbm dbmjz dbmnz");
+# endif
+#endif
+#ifdef LOOKUP_DNSDB
+# if LOOKUP_DNSDB!=2
+  b = string_cat(b, US" dnsdb");
+# else
+  d = string_cat(d, US" dnsdb");
+# endif
+#endif
+#ifdef LOOKUP_DSEARCH
+# if LOOKUP_DSEARCH!=2
+  b = string_cat(b, US" dsearch");
+# else
+  d = string_cat(d, US" dsearch");
+# endif
+#endif
+#ifdef LOOKUP_IBASE
+# if LOOKUP_IBASE!=2
+  b = string_cat(b, US" ibase");
+# else
+  d = string_cat(d, US" ibase");
+# endif
+#endif
+#ifdef LOOKUP_JSON
+# if LOOKUP_JSON!=2
+  b = string_cat(b, US" json");
+# else
+  d = string_cat(d, US" json");
+# endif
+#endif
+#ifdef LOOKUP_LDAP
+# if LOOKUP_LDAP!=2
+  b = string_cat(b, US" ldap ldapdn ldapm");
+# else
+  d = string_cat(d, US" ldap ldapdn ldapm");
+# endif
+#endif
+#ifdef LOOKUP_LMDB
+# if LOOKUP_LMDB!=2
+  b = string_cat(b, US" lmdb");
+# else
+  d = string_cat(d, US" lmdb");
+# endif
+#endif
+#ifdef LOOKUP_MYSQL
+# if LOOKUP_MYSQL!=2
+  b = string_cat(b, US" mysql");
+# else
+  d = string_cat(d, US" mysql");
+# endif
+#endif
+#ifdef LOOKUP_NIS
+# if LOOKUP_NIS!=2
+  b = string_cat(b, US" nis nis0");
+# else
+  d = string_cat(d, US" nis nis0");
+# endif
+#endif
+#ifdef LOOKUP_NISPLUS
+# if LOOKUP_NISPLUS!=2
+  b = string_cat(b, US" nisplus");
+# else
+  d = string_cat(d, US" nisplus");
+# endif
+#endif
+#ifdef LOOKUP_ORACLE
+# if LOOKUP_ORACLE!=2
+  b = string_cat(b, US" oracle");
+# else
+  d = string_cat(d, US" oracle");
+# endif
+#endif
+#ifdef LOOKUP_PASSWD
+# if LOOKUP_PASSWD!=2
+  b = string_cat(b, US" passwd");
+# else
+  d = string_cat(d, US" passwd");
+# endif
+#endif
+#ifdef LOOKUP_PGSQL
+# if LOOKUP_PGSQL!=2
+  b = string_cat(b, US" pgsql");
+# else
+  d = string_cat(d, US" pgsql");
+# endif
+#endif
+#ifdef LOOKUP_REDIS
+# if LOOKUP_REDIS!=2
+  b = string_cat(b, US" redis");
+# else
+  d = string_cat(d, US" redis");
+# endif
+#endif
+#ifdef SUPPORT_SPF
+# if SUPPORT_SPF!=2
+  b = string_cat(b, US" spf");
+# else
+  d = string_cat(d, US" spf");
+# endif
+#endif
+#ifdef LOOKUP_SQLITE
+# if LOOKUP_SQLITE!=2
+  b = string_cat(b, US" sqlite");
+# else
+  d = string_cat(d, US" sqlite");
+# endif
+#endif
+#ifdef LOOKUP_TESTDB
+# if LOOKUP_TESTDB!=2
+  b = string_cat(b, US" testdb");
+# else
+  d = string_cat(d, US" testdb");
+# endif
+#endif
+#ifdef LOOKUP_WHOSON
+# if LOOKUP_WHOSON!=2
+  b = string_cat(b, US" whoson");
+# else
+  d = string_cat(d, US" whoson");
+# endif
+#endif
+
+if (b) g = string_fmt_append(g, "Lookups (built-in):%Y\n", b);
+if (d) g = string_fmt_append(g, "Lookups (dynamic): %Y\n", d);
+return g;
+}
+
+
+static void
+lookup_version_report_cb(uschar * name, uschar * ptr, void * ctx)
+{
+const lookup_info * li = (lookup_info *)ptr;
+gstring ** gp = ctx;
+
+if (li->version_report)
+  *gp = li->version_report(*gp);
+}
+
+
 /* This function is called for -bV/--version and for -d to output the optional
 features of the current Exim binary.
 
@@ -1051,6 +1235,12 @@ g = string_cat(g, US"Support for:");
 #ifdef WITH_CONTENT_SCAN
   g = string_cat(g, US" Content_Scanning");
 #endif
+#ifndef DISABLE_EXIM_FILTER
+  g = string_cat(g, US" Exim_filter");
+#endif
+#ifndef DISABLE_SIEVE_FILTER
+  g = string_cat(g, US" Sieve_filter");
+#endif
 #ifdef SUPPORT_CRYPTEQ
   g = string_cat(g, US" crypteq");
 #endif
@@ -1168,66 +1358,7 @@ g = string_cat(g, US"Support for:");
 #endif
 g = string_cat(g, US"\n");
 
-g = string_cat(g, US"Lookups (built-in):");
-#if defined(LOOKUP_LSEARCH) && LOOKUP_LSEARCH!=2
-  g = string_cat(g, US" lsearch wildlsearch nwildlsearch iplsearch");
-#endif
-#if defined(LOOKUP_CDB) && LOOKUP_CDB!=2
-  g = string_cat(g, US" cdb");
-#endif
-#if defined(LOOKUP_DBM) && LOOKUP_DBM!=2
-  g = string_cat(g, US" dbm dbmjz dbmnz");
-#endif
-#if defined(LOOKUP_DNSDB) && LOOKUP_DNSDB!=2
-  g = string_cat(g, US" dnsdb");
-#endif
-#if defined(LOOKUP_DSEARCH) && LOOKUP_DSEARCH!=2
-  g = string_cat(g, US" dsearch");
-#endif
-#if defined(LOOKUP_IBASE) && LOOKUP_IBASE!=2
-  g = string_cat(g, US" ibase");
-#endif
-#if defined(LOOKUP_JSON) && LOOKUP_JSON!=2
-  g = string_cat(g, US" json");
-#endif
-#if defined(LOOKUP_LDAP) && LOOKUP_LDAP!=2
-  g = string_cat(g, US" ldap ldapdn ldapm");
-#endif
-#ifdef LOOKUP_LMDB
-  g = string_cat(g, US" lmdb");
-#endif
-#if defined(LOOKUP_MYSQL) && LOOKUP_MYSQL!=2
-  g = string_cat(g, US" mysql");
-#endif
-#if defined(LOOKUP_NIS) && LOOKUP_NIS!=2
-  g = string_cat(g, US" nis nis0");
-#endif
-#if defined(LOOKUP_NISPLUS) && LOOKUP_NISPLUS!=2
-  g = string_cat(g, US" nisplus");
-#endif
-#if defined(LOOKUP_ORACLE) && LOOKUP_ORACLE!=2
-  g = string_cat(g, US" oracle");
-#endif
-#if defined(LOOKUP_PASSWD) && LOOKUP_PASSWD!=2
-  g = string_cat(g, US" passwd");
-#endif
-#if defined(LOOKUP_PGSQL) && LOOKUP_PGSQL!=2
-  g = string_cat(g, US" pgsql");
-#endif
-#if defined(LOOKUP_REDIS) && LOOKUP_REDIS!=2
-  g = string_cat(g, US" redis");
-#endif
-#if defined(LOOKUP_SQLITE) && LOOKUP_SQLITE!=2
-  g = string_cat(g, US" sqlite");
-#endif
-#if defined(LOOKUP_TESTDB) && LOOKUP_TESTDB!=2
-  g = string_cat(g, US" testdb");
-#endif
-#if defined(LOOKUP_WHOSON) && LOOKUP_WHOSON!=2
-  g = string_cat(g, US" whoson");
-#endif
-g = string_cat(g, US"\n");
-
+g = lookup_show_supported(g);
 g = auth_show_supported(g);
 g = route_show_supported(g);
 g = transport_show_supported(g);
@@ -1262,7 +1393,7 @@ DEBUG(D_any)
 # ifdef __VERSION__
   g = string_fmt_append(g, "Compiler: GCC [%s]\n", __VERSION__);
 # else
-  g = string_fmt_append(g, "Compiler: GCC [%s]\n", "? unknown version ?";
+  g = string_fmt_append(g, "Compiler: GCC [%s]\n", "? unknown version ?");
 # endif
 #else
   g = string_cat(g, US"Compiler: <unknown>\n");
@@ -1276,7 +1407,7 @@ DEBUG(D_any)
                gnu_get_libc_version());
 #endif
 
-g = show_db_version(g);
+  g = show_db_version(g);
 
 #ifndef DISABLE_TLS
   g = tls_version_report(g);
@@ -1284,19 +1415,16 @@ g = show_db_version(g);
 #ifdef SUPPORT_I18N
   g = utf8_version_report(g);
 #endif
-#ifdef SUPPORT_DMARC
-  g = dmarc_version_report(g);
-#endif
-#ifdef SUPPORT_SPF
-  g = spf_lib_version_report(g);
-#endif
 
-show_string(is_stdout, g);
-g = NULL;
+/*XXX do we need a "show misc-mods version-report" ?
+Currently they are output in misc_mod_add() */
+
+  show_string(is_stdout, g);
+  g = NULL;
 
-for (auth_info * authi = auths_available; *authi->driver_name != '\0'; ++authi)
-  if (authi->version_report)
-    g = (*authi->version_report)(g);
+  for (auth_info * ai = auths_available; ai; ai = (auth_info *)ai->drinfo.next)
+    if (ai->version_report)
+      g = (*ai->version_report)(g);
 
   /* PCRE_PRERELEASE is either defined and empty or a bare sequence of
   characters; unless it's an ancient version of PCRE in which case it
@@ -1306,27 +1434,26 @@ for (auth_info * authi = auths_available; *authi->driver_name != '\0'; ++authi)
 #endif
 #define QUOTE(X) #X
 #define EXPAND_AND_QUOTE(X) QUOTE(X)
-  {
-  uschar buf[24];
-  pcre2_config(PCRE2_CONFIG_VERSION, buf);
-  g = string_fmt_append(g, "Library version: PCRE2: Compile: %d.%d%s\n"
-              "                        Runtime: %s\n",
-          PCRE2_MAJOR, PCRE2_MINOR,
-          EXPAND_AND_QUOTE(PCRE2_PRERELEASE) "",
-          buf);
-  }
+    {
+    uschar buf[24];
+    pcre2_config(PCRE2_CONFIG_VERSION, buf);
+    g = string_fmt_append(g, "Library version: PCRE2: Compile: %d.%d%s\n"
+               "                        Runtime: %s\n",
+           PCRE2_MAJOR, PCRE2_MINOR,
+           EXPAND_AND_QUOTE(PCRE2_PRERELEASE) "",
+           buf);
+    }
 #undef QUOTE
 #undef EXPAND_AND_QUOTE
 
-show_string(is_stdout, g);
-g = NULL;
+  show_string(is_stdout, g);
+  g = NULL;
 
-init_lookup_list();
-for (int i = 0; i < lookup_list_count; i++)
-  if (lookup_list[i]->version_report)
-    g = lookup_list[i]->version_report(g);
-show_string(is_stdout, g);
-g = NULL;
+  init_lookup_list();
+  tree_walk(lookups_tree, lookup_version_report_cb, &g);
+  show_string(is_stdout, g);
+  g = NULL;
+  init_misc_mod_list();
 
 #ifdef WHITELIST_D_MACROS
   g = string_fmt_append(g, "WHITELIST_D_MACROS: \"%s\"\n", WHITELIST_D_MACROS);
@@ -1368,8 +1495,14 @@ switch(request)
 );
     return;
   case CMDINFO_SIEVE:
-    for (const uschar ** pp = exim_sieve_extension_list; *pp; ++pp)
-      fprintf(stream, "%s\n", *pp);
+    {
+    const misc_module_info * mi;
+    typedef void (*fn_t)(FILE *);
+    if ((mi = misc_mod_find(US"sieve_filter", NULL)))
+      (((fn_t *) mi->functions)[SIEVE_EXTENSIONS]) (stream);
+    else
+      fprintf(stream, "Sieve filtering not available\n");
+    }
     return;
   case CMDINFO_DSCP:
     dscp_list_to_stream(stream);
@@ -1705,6 +1838,10 @@ len = Ustrlen(big_buffer);
 
 (void) macros_expand(0, &len, &dummy_macexp);
 
+#ifdef LOOKUP_MODULE_DIR
+//mod_load_check(big_buffer);
+#endif
+
 if (isupper(big_buffer[0]))
   {
   if (macro_read_assignment(big_buffer))
@@ -2257,28 +2394,46 @@ on the second character (the one after '-'), to save some effort. */
     /* sendmail uses -Ac and -Am to control which .cf file is used;
     we ignore them. */
     case 'A':
-    if (!*argrest) { badarg = TRUE; break; }
-    else
-      {
-      BOOL ignore = FALSE;
-      switch (*argrest)
-        {
-        case 'c':
-        case 'm':
-          if (*(argrest + 1) == '\0')
-            ignore = TRUE;
-          break;
-        }
-      if (!ignore) badarg = TRUE;
-      }
-    break;
+      if (!*argrest) { badarg = TRUE; break; }
+      else
+       {
+       BOOL ignore = FALSE;
+       switch (*argrest)
+         {
+         case 'c':
+         case 'm':
+           if (*(argrest + 1) == '\0')
+             ignore = TRUE;
+           break;
+         }
+       if (!ignore) badarg = TRUE;
+       }
+      break;
+
+    /* -atrn <host> <domains> */
+    case 'a':
+      if (Ustrcmp(argrest, "trn") == 0)
+       if (i+2 < argc)
+         {
+         atrn_mode = US"C";    /* Customer mode */
+
+         /* The host could at this point have a port attached */
+         atrn_host = exim_arg_copy(argv[++i], EXIM_DOMAINNAME_MAX, "-atrn");
+         atrn_domains = exim_arg_copy(argv[++i], EXIM_DOMAINNAME_MAX*4,
+                                     "-atrn");
+         i++;
+         }
+       else
+         exim_fail("exim: host and domainlist expected after %s\n", argv[i]);
+      else badarg = TRUE;
+      break;
 
     /* -Btype is a sendmail option for 7bit/8bit setting. Exim is 8-bit clean
     so has no need of it. */
 
     case 'B':
-    if (!*argrest) i++;       /* Skip over the type */
-    break;
+      if (!*argrest) i++;       /* Skip over the type */
+      break;
 
 
     case 'b':
@@ -2552,9 +2707,8 @@ on the second character (the one after '-'), to save some effort. */
 
        /* -bw: inetd wait mode, accept a listening socket as stdin */
        case 'w':
-         f.inetd_wait_mode = TRUE;
+         f.inetd_wait_mode = f.daemon_listen = f.daemon_scion = TRUE;
          f.background_daemon = FALSE;
-         f.daemon_listen = f.daemon_scion = TRUE;
          if (*argrest)
            if ((inetd_wait_timeout = readconf_readtime(argrest, 0, FALSE)) <= 0)
              exim_fail("exim: bad time value %s: abandoned\n", argv[i]);
@@ -3860,40 +4014,46 @@ END_ARG:
 if (usage_wanted) exim_usage(called_as);
 
 /* Arguments have been processed. Check for incompatibilities. */
-if (  (  (smtp_input || extract_recipients || recipients_arg < argc)
+if (    (smtp_input || extract_recipients || recipients_arg < argc)
       && (  f.daemon_listen || qrunners || bi_option
         || test_retry_arg >= 0 || test_rewrite_arg >= 0
         || filter_test != FTEST_NONE
         || msg_action_arg > 0 && !one_msg_action
-      )  )
-   || (  msg_action_arg > 0
+        )
+   ||   msg_action_arg > 0
       && (  f.daemon_listen || is_multiple_qrun() || list_options
         || checking && msg_action != MSG_LOAD
         || bi_option || test_retry_arg >= 0 || test_rewrite_arg >= 0
-      )  )
-   || (  (f.daemon_listen || is_multiple_qrun())
+        )
+   ||   (f.daemon_listen || is_multiple_qrun())
       && (  sender_address || list_options || list_queue || checking
         || bi_option
-      )  )
+        )
    || f.daemon_listen && is_onetime_qrun()
    || f.inetd_wait_mode && qrunners
-   || (  list_options
+   ||   list_options
       && (  checking || smtp_input || extract_recipients
         || filter_test != FTEST_NONE || bi_option
-      )  )
-   || (  verify_address_mode
+        )
+   ||   verify_address_mode
       && (  f.address_test_mode || smtp_input || extract_recipients
         || filter_test != FTEST_NONE || bi_option
-      )  )
-   || (  f.address_test_mode
+        )
+   ||   f.address_test_mode
       && (  smtp_input || extract_recipients || filter_test != FTEST_NONE
         || bi_option
-      )  )
-   || (  smtp_input
+        )
+   ||   smtp_input
       && (sender_address || filter_test != FTEST_NONE || extract_recipients)
-      )
    || deliver_selectstring && !qrunners
    || msg_action == MSG_LOAD && (!expansion_test || expansion_test_message)
+   ||   atrn_mode
+      && (  f.daemon_listen || expansion_test || filter_test != FTEST_NONE
+        || checking /* || bi_option || info_stdout || receiving_message
+        || malware_test_file || list_queue || list_config || list_options
+        || version_printed || msg_action_arg > 0 || qrunners
+        */
+        )
    )
   exim_fail("exim: incompatible command-line options or arguments\n");
 
@@ -4099,6 +4259,7 @@ is equivalent to the ability to modify a setuid binary!
 
 This needs to happen before we read the main configuration. */
 init_lookup_list();
+init_misc_mod_list();
 
 /*XXX this excrescence could move to the testsuite standard config setup file */
 #ifdef SUPPORT_I18N
@@ -4387,9 +4548,16 @@ if (perl_start_option != 0)
   opt_perl_at_start = (perl_start_option > 0);
 if (opt_perl_at_start && opt_perl_startup != NULL)
   {
-  uschar *errstr;
+  uschar * errstr;
+  const misc_module_info * mi = misc_mod_find(US"perl", &errstr);
+  typedef uschar * (*fn_t)(uschar *);
+
+  if (!mi)
+    exim_fail("exim: error finding perl module: %s\n", errstr);
+
   DEBUG(D_any) debug_printf("Starting Perl interpreter\n");
-  if ((errstr = init_perl(opt_perl_startup)))
+
+  if ((errstr = (((fn_t *) mi->functions)[PERL_STARTUP]) (opt_perl_startup)))
     exim_fail("exim: error in perl_startup code: %s\n", errstr);
   opt_perl_started = TRUE;
   }
@@ -4457,7 +4625,7 @@ privilege by now. Before the chdir, we try to ensure that the directory exists.
 if (Uchdir(spool_directory) != 0)
   {
   (void) directory_make(spool_directory, US"", SPOOL_DIRECTORY_MODE, FALSE);
-  (void) Uchdir(spool_directory);
+  if (Uchdir(spool_directory) < 0) ;
   }
 
 /* Handle calls with the -bi option. This is a sendmail option to rebuild *the*
@@ -4599,11 +4767,11 @@ if (smtp_input)
 
       if (real_uid == root_uid || real_uid == exim_uid || interface_port < 1024)
         {
+        if (mua_wrapper) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Input from "
+          "inetd is not supported when mua_wrapper is set");
         f.is_inetd = TRUE;
         sender_host_address = host_ntoa(-1, (struct sockaddr *)(&inetd_sock),
           NULL, &sender_host_port);
-        if (mua_wrapper) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Input from "
-          "inetd is not supported when mua_wrapper is set");
         }
       else
         exim_fail(
@@ -4618,7 +4786,7 @@ root. There will be further calls later for each message received. */
 
 #ifdef LOAD_AVG_NEEDS_ROOT
 if (  receiving_message
-   && (queue_only_load >= 0 || (f.is_inetd && smtp_load_reserve >= 0)))
+   && (queue_only_load >= 0 ||  f.is_inetd && smtp_load_reserve >= 0))
   load_average = OS_GETLOADAVG();
 #endif
 
@@ -4692,12 +4860,12 @@ if (malware_test_file)
   if ((result = malware_in_file(malware_test_file)) == FAIL)
     {
     printf("No malware found.\n");
-    exit(EXIT_SUCCESS);
+    exim_exit(EXIT_SUCCESS);
     }
   if (result != OK)
     {
     printf("Malware lookup returned non-okay/fail: %d\n", result);
-    exit(EXIT_FAILURE);
+    exim_exit(EXIT_FAILURE);
     }
   if (malware_name)
     printf("Malware found: %s\n", malware_name);
@@ -4706,7 +4874,7 @@ if (malware_test_file)
 #else
   printf("Malware scanning not enabled at compile time.\n");
 #endif
-  exit(EXIT_FAILURE);
+  exim_exit(EXIT_FAILURE);
   }
 
 /* Handle a request to list the delivery queue */
@@ -4715,7 +4883,7 @@ if (list_queue)
   {
   set_process_info("listing the queue");
   queue_list(list_queue_option, argv + recipients_arg, argc - recipients_arg);
-  exit(EXIT_SUCCESS);
+  exim_exit(EXIT_SUCCESS);
   }
 
 /* Handle a request to count the delivery queue */
@@ -4724,7 +4892,7 @@ if (count_queue)
   {
   set_process_info("counting the queue");
   fprintf(stdout, "%u\n", queue_count());
-  exit(EXIT_SUCCESS);
+  exim_exit(EXIT_SUCCESS);
   }
 
 /* Handle actions on specific messages, except for the force delivery and
@@ -4763,7 +4931,7 @@ if (msg_action_arg > 0 && msg_action != MSG_DELIVER && msg_action != MSG_LOAD)
 
   else if (!queue_action(argv[msg_action_arg], msg_action, argv, argc,
     recipients_arg)) yield = EXIT_FAILURE;
-  exit(yield);
+  exim_exit(yield);
   }
 
 /* We used to set up here to skip reading the ACL section, on
@@ -5166,7 +5334,7 @@ if (f.daemon_listen || f.inetd_wait_mode || is_multiple_qrun())
     (void)gettimeofday(&t0, NULL);
 # endif
     if (!tls_dropprivs_validate_require_cipher(FALSE))
-      exit(1);
+      exim_exit(EXIT_FAILURE);
 # ifdef MEASURE_TIMING
     report_time_since(&t0, US"validate_ciphers (delta)");
 # endif
@@ -5174,6 +5342,7 @@ if (f.daemon_listen || f.inetd_wait_mode || is_multiple_qrun())
 #endif
 
   daemon_go();
+  /*NOTREACHED*/
   }
 
 /* If the sender ident has not been set (by a trusted caller) set it to
@@ -5375,8 +5544,12 @@ if (expansion_test)
   /* Expand command line items */
 
   if (recipients_arg < argc)
-    while (recipients_arg < argc)
-      expansion_test_line(exim_str_fail_toolong(argv[recipients_arg++], EXIM_EMAILADDR_MAX, "recipient"));
+    {
+    config_filename = US"-be args";
+    for (config_lineno = 1; recipients_arg < argc; config_lineno++)
+      expansion_test_line(exim_str_fail_toolong(argv[recipients_arg++],
+                                             EXIM_EMAILADDR_MAX, "-be arg"));
+    }
 
   /* Read stdin */
 
@@ -5390,7 +5563,9 @@ if (expansion_test)
     void *dlhandle = set_readline(&fn_readline, &fn_addhist);
 #endif
 
-    while (s = get_stdinput(fn_readline, fn_addhist))
+    config_filename = US"-be stdin";
+    for (config_lineno = 1; s = get_stdinput(fn_readline, fn_addhist);
+       config_lineno++)
       expansion_test_line(s);
 
 #ifdef USE_READLINE
@@ -5474,7 +5649,7 @@ if (host_checking)
     "**** This is not for real!\n\n",
       sender_host_address);
 
-  connection_id = getpid();
+  set_connection_id();
   memset(sender_host_cache, 0, sizeof(sender_host_cache));
   if (verify_check_host(&hosts_connection_nolog) == OK)
     {
@@ -5498,9 +5673,6 @@ if (host_checking)
 
       return_path = sender_address = NULL;
       dnslist_domain = dnslist_matched = NULL;
-#ifndef DISABLE_DKIM
-      dkim_cur_signer = NULL;
-#endif
       acl_var_m = NULL;
       deliver_localpart_orig = NULL;
       deliver_domain_orig = NULL;
@@ -5520,7 +5692,8 @@ otherwise complain unless a version print (-bV) happened or this is a filter
 verification test or info dump.
 In the former case, show the configuration file name. */
 
-if (recipients_arg >= argc && !extract_recipients && !smtp_input)
+if (  recipients_arg >= argc && !extract_recipients
+   && !smtp_input && !atrn_mode)
   {
   if (version_printed)
     {
@@ -5540,6 +5713,10 @@ if (recipients_arg >= argc && !extract_recipients && !smtp_input)
     exim_usage(called_as);
   }
 
+/*XXX somewhere around here.  Maybe earlier, but no later.  ATRN customer */
+if (atrn_mode)
+  atrn_handle_customer();
+
 
 /* If mua_wrapper is set, Exim is being used to turn an MUA that submits on the
 standard input into an MUA that submits to a smarthost over TCP/IP. We know
@@ -5584,7 +5761,7 @@ if (!smtp_input) error_handling = arg_error_handling;
 logging being sent down the socket and make an identd call to get the
 sender_ident. */
 
-else if (f.is_inetd)
+else if (f.is_inetd && !atrn_mode)
   {
   (void)fclose(stderr);
   exim_nullstd();                       /* Re-open to /dev/null */
@@ -5663,11 +5840,12 @@ because a log line has already been written for all its failure exists
 (usually "connection refused: <reason>") and writing another one is
 unnecessary clutter. */
 
-connection_id = getpid();
+set_connection_id();
 if (smtp_input)
   {
   smtp_in = stdin;
   smtp_out = stdout;
+
   memset(sender_host_cache, 0, sizeof(sender_host_cache));
   if (verify_check_host(&hosts_connection_nolog) == OK)
     {
@@ -5755,7 +5933,7 @@ for (BOOL more = TRUE; more; )
   rmark reset_point = store_mark();
   message_id[0] = 0;
 
-  /* Handle the SMTP case; call smtp_setup_mst() to deal with the initial SMTP
+  /* Handle the SMTP case; call smtp_setup_msg() to deal with the initial SMTP
   input and build the recipients list, before calling receive_msg() to read the
   message proper. Whatever sender address is given in the SMTP transaction is
   often ignored for local senders - we use the actual sender, which is normally