DANE: move to mainline
[exim.git] / src / src / exim.c
index 88e119778cf66ec3ff830bcdcc0daaacc0397d3d..f95c107478041761fa5dc3fe4a8b9700dc6fb8f3 100644 (file)
@@ -2,7 +2,7 @@
 *     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. */
 
 
@@ -212,8 +212,7 @@ int fd;
 
 os_restarting_signal(sig, usr1_handler);
 
-fd = Uopen(process_log_path, O_APPEND|O_WRONLY, LOG_MODE);
-if (fd < 0)
+if ((fd = Uopen(process_log_path, O_APPEND|O_WRONLY, LOG_MODE)) < 0)
   {
   /* If we are already running as the Exim user, try to create it in the
   current process (assuming spool_directory exists). Otherwise, if we are
@@ -345,7 +344,7 @@ Arguments:
 Returns:      -1, 0, or +1
 */
 
-int
+static int
 exim_tvcmp(struct timeval *t1, struct timeval *t2)
 {
 if (t1->tv_sec > t2->tv_sec) return +1;
@@ -659,12 +658,13 @@ Returns:     does not return
 */
 
 void
-exim_exit(int rc)
+exim_exit(int rc, const uschar * process)
 {
 search_tidyup();
 DEBUG(D_any)
-  debug_printf(">>>>>>>>>>>>>>>> Exim pid=%d terminating with rc=%d "
-    ">>>>>>>>>>>>>>>>\n", (int)getpid(), rc);
+  debug_printf(">>>>>>>>>>>>>>>> Exim pid=%d %s%s%sterminating with rc=%d "
+    ">>>>>>>>>>>>>>>>\n", (int)getpid(),
+    process ? "(" : "", process, process ? ") " : "", rc);
 exit(rc);
 }
 
@@ -743,26 +743,26 @@ else
 *          Show supported features               *
 *************************************************/
 
-/* This function is called for -bV/--version and for -d to output the optional
-features of the current Exim binary.
-
-Arguments:  a FILE for printing
-Returns:    nothing
-*/
-
 static void
-show_whats_supported(FILE *f)
+show_db_version(FILE * f)
 {
-  auth_info *authi;
-
 #ifdef DB_VERSION_STRING
-fprintf(f, "Berkeley DB: %s\n", DB_VERSION_STRING);
+DEBUG(D_any)
+  {
+  fprintf(f, "Library version: BDB: Compile: %s\n", DB_VERSION_STRING);
+  fprintf(f, "                      Runtime: %s\n",
+    db_version(NULL, NULL, NULL));
+  }
+else
+  fprintf(f, "Berkeley DB: %s\n", DB_VERSION_STRING);
+
 #elif defined(BTREEVERSION) && defined(HASHVERSION)
   #ifdef USE_DB
   fprintf(f, "Probably Berkeley DB version 1.8x (native mode)\n");
   #else
   fprintf(f, "Probably Berkeley DB version 1.8x (compatibility mode)\n");
   #endif
+
 #elif defined(_DBM_RDONLY) || defined(dbm_dirfno)
 fprintf(f, "Probably ndbm\n");
 #elif defined(USE_TDB)
@@ -774,6 +774,22 @@ fprintf(f, "Using tdb\n");
   fprintf(f, "Probably GDBM (compatibility mode)\n");
   #endif
 #endif
+}
+
+
+/* This function is called for -bV/--version and for -d to output the optional
+features of the current Exim binary.
+
+Arguments:  a FILE for printing
+Returns:    nothing
+*/
+
+static void
+show_whats_supported(FILE * f)
+{
+auth_info * authi;
+
+DEBUG(D_any) {} else show_db_version(f);
 
 fprintf(f, "Support for:");
 #ifdef SUPPORT_CRYPTEQ
@@ -801,11 +817,11 @@ fprintf(f, "Support for:");
   fprintf(f, " TCPwrappers");
 #endif
 #ifdef SUPPORT_TLS
-  #ifdef USE_GNUTLS
+ifdef USE_GNUTLS
   fprintf(f, " GnuTLS");
-  #else
+else
   fprintf(f, " OpenSSL");
-  #endif
+endif
 #endif
 #ifdef SUPPORT_TRANSLATE_IP_ADDRESS
   fprintf(f, " translate_ip_address");
@@ -816,6 +832,9 @@ fprintf(f, "Support for:");
 #ifdef WITH_CONTENT_SCAN
   fprintf(f, " Content_Scanning");
 #endif
+#ifdef SUPPORT_DANE
+  fprintf(f, " DANE");
+#endif
 #ifndef DISABLE_DKIM
   fprintf(f, " DKIM");
 #endif
@@ -840,8 +859,12 @@ fprintf(f, "Support for:");
 #ifdef SUPPORT_SOCKS
   fprintf(f, " SOCKS");
 #endif
+#ifdef SUPPORT_SPF
+  fprintf(f, " SPF");
+#endif
 #ifdef TCP_FASTOPEN
-  fprintf(f, " TCP_Fast_Open");
+  deliver_init();
+  if (tcp_fastopen_ok) fprintf(f, " TCP_Fast_Open");
 #endif
 #ifdef EXPERIMENTAL_LMDB
   fprintf(f, " Experimental_LMDB");
@@ -849,18 +872,12 @@ fprintf(f, "Support for:");
 #ifdef EXPERIMENTAL_QUEUEFILE
   fprintf(f, " Experimental_QUEUEFILE");
 #endif
-#ifdef EXPERIMENTAL_SPF
-  fprintf(f, " Experimental_SPF");
-#endif
 #ifdef EXPERIMENTAL_SRS
   fprintf(f, " Experimental_SRS");
 #endif
 #ifdef EXPERIMENTAL_BRIGHTMAIL
   fprintf(f, " Experimental_Brightmail");
 #endif
-#ifdef EXPERIMENTAL_DANE
-  fprintf(f, " Experimental_DANE");
-#endif
 #ifdef EXPERIMENTAL_DCC
   fprintf(f, " Experimental_DCC");
 #endif
@@ -929,86 +946,13 @@ fprintf(f, "Lookups (built-in):");
 #endif
 fprintf(f, "\n");
 
-fprintf(f, "Authenticators:");
-#ifdef AUTH_CRAM_MD5
-  fprintf(f, " cram_md5");
-#endif
-#ifdef AUTH_CYRUS_SASL
-  fprintf(f, " cyrus_sasl");
-#endif
-#ifdef AUTH_DOVECOT
-  fprintf(f, " dovecot");
-#endif
-#ifdef AUTH_GSASL
-  fprintf(f, " gsasl");
-#endif
-#ifdef AUTH_HEIMDAL_GSSAPI
-  fprintf(f, " heimdal_gssapi");
-#endif
-#ifdef AUTH_PLAINTEXT
-  fprintf(f, " plaintext");
-#endif
-#ifdef AUTH_SPA
-  fprintf(f, " spa");
-#endif
-#ifdef AUTH_TLS
-  fprintf(f, " tls");
-#endif
-fprintf(f, "\n");
-
-fprintf(f, "Routers:");
-#ifdef ROUTER_ACCEPT
-  fprintf(f, " accept");
-#endif
-#ifdef ROUTER_DNSLOOKUP
-  fprintf(f, " dnslookup");
-#endif
-#ifdef ROUTER_IPLITERAL
-  fprintf(f, " ipliteral");
-#endif
-#ifdef ROUTER_IPLOOKUP
-  fprintf(f, " iplookup");
-#endif
-#ifdef ROUTER_MANUALROUTE
-  fprintf(f, " manualroute");
-#endif
-#ifdef ROUTER_QUERYPROGRAM
-  fprintf(f, " queryprogram");
-#endif
-#ifdef ROUTER_REDIRECT
-  fprintf(f, " redirect");
-#endif
-fprintf(f, "\n");
+auth_show_supported(f);
+route_show_supported(f);
+transport_show_supported(f);
 
-fprintf(f, "Transports:");
-#ifdef TRANSPORT_APPENDFILE
-  fprintf(f, " appendfile");
-  #ifdef SUPPORT_MAILDIR
-    fprintf(f, "/maildir");
-  #endif
-  #ifdef SUPPORT_MAILSTORE
-    fprintf(f, "/mailstore");
-  #endif
-  #ifdef SUPPORT_MBX
-    fprintf(f, "/mbx");
-  #endif
-#endif
-#ifdef TRANSPORT_AUTOREPLY
-  fprintf(f, " autoreply");
-#endif
-#ifdef TRANSPORT_LMTP
-  fprintf(f, " lmtp");
-#endif
-#ifdef TRANSPORT_PIPE
-  fprintf(f, " pipe");
-#endif
-#ifdef EXPERIMENTAL_QUEUEFILE
-  fprintf(f, " queuefile");
-#endif
-#ifdef TRANSPORT_SMTP
-  fprintf(f, " smtp");
+#ifdef WITH_CONTENT_SCAN
+malware_show_supported(f);
 #endif
-fprintf(f, "\n");
 
 if (fixed_never_users[0] > 0)
   {
@@ -1052,6 +996,8 @@ DEBUG(D_any) do {
                gnu_get_libc_version());
 #endif
 
+show_db_version(f);
+
 #ifdef SUPPORT_TLS
   tls_version_report(f);
 #endif
@@ -1119,8 +1065,8 @@ switch(request)
 "If the string is not recognised, you'll get this help (on stderr).\n"
 "\n"
 "  exim -bI:help    this information\n"
-"  exim -bI:dscp    dscp value keywords known\n"
-"  exim -bI:sieve   list of supported sieve extensions, one per line.\n"
+"  exim -bI:dscp    list of known dscp value keywords\n"
+"  exim -bI:sieve   list of supported sieve extensions\n"
 );
     return;
   case CMDINFO_SIEVE:
@@ -1150,8 +1096,7 @@ uschar *
 local_part_quote(uschar *lpart)
 {
 BOOL needs_quote = FALSE;
-int size, ptr;
-uschar *yield;
+gstring * g;
 uschar *t;
 
 for (t = lpart; !needs_quote && *t != 0; t++)
@@ -1162,26 +1107,24 @@ for (t = lpart; !needs_quote && *t != 0; t++)
 
 if (!needs_quote) return lpart;
 
-size = ptr = 0;
-yield = string_catn(NULL, &size, &ptr, US"\"", 1);
+g = string_catn(NULL, US"\"", 1);
 
 for (;;)
   {
   uschar *nq = US Ustrpbrk(lpart, "\\\"");
   if (nq == NULL)
     {
-    yield = string_cat(yield, &size, &ptr, lpart);
+    g = string_cat(g, lpart);
     break;
     }
-  yield = string_catn(yield, &size, &ptr, lpart, nq - lpart);
-  yield = string_catn(yield, &size, &ptr, US"\\", 1);
-  yield = string_catn(yield, &size, &ptr, nq, 1);
+  g = string_catn(g, lpart, nq - lpart);
+  g = string_catn(g, US"\\", 1);
+  g = string_catn(g, nq, 1);
   lpart = nq + 1;
   }
 
-yield = string_catn(yield, &size, &ptr, US"\"", 1);
-yield[ptr] = 0;
-return yield;
+g = string_catn(g, US"\"", 1);
+return string_from_gstring(g);
 }
 
 
@@ -1254,11 +1197,9 @@ static uschar *
 get_stdinput(char *(*fn_readline)(const char *), void(*fn_addhist)(const char *))
 {
 int i;
-int size = 0;
-int ptr = 0;
-uschar *yield = NULL;
+gstring * g = NULL;
 
-if (fn_readline == NULL) { printf("> "); fflush(stdout); }
+if (!fn_readline) { printf("> "); fflush(stdout); }
 
 for (i = 0;; i++)
   {
@@ -1293,23 +1234,22 @@ for (i = 0;; i++)
     while (p < ss && isspace(*p)) p++;   /* leading space after cont */
     }
 
-  yield = string_catn(yield, &size, &ptr, p, ss - p);
+  g = string_catn(g, p, ss - p);
 
   #ifdef USE_READLINE
-  if (fn_readline != NULL) free(readline_line);
+  if (fn_readline) free(readline_line);
   #endif
 
-  /* yield can only be NULL if ss==p */
-  if (ss == p || yield[ptr-1] != '\\')
-    {
-    if (yield) yield[ptr] = 0;
+  /* g can only be NULL if ss==p */
+  if (ss == p || g->s[g->ptr-1] != '\\')
     break;
-    }
-  yield[--ptr] = 0;
+
+  --g->ptr;
+  (void) string_from_gstring(g);
   }
 
-if (yield == NULL) printf("\n");
-return yield;
+if (!g) printf("\n");
+return string_from_gstring(g);
 }
 
 
@@ -1432,7 +1372,7 @@ whites[i] = NULL;
 
 /* The list of commandline macros should be very short.
 Accept the N*M complexity. */
-for (m = macros; m; m = m->next) if (m->command_line)
+for (m = macros_user; m; m = m->next) if (m->command_line)
   {
   found = FALSE;
   for (w = whites; *w; ++w)
@@ -1443,10 +1383,9 @@ for (m = macros; m; m = m->next) if (m->command_line)
       }
   if (!found)
     return FALSE;
-  if (m->replacement == NULL)
+  if (!m->replacement)
     continue;
-  len = Ustrlen(m->replacement);
-  if (len == 0)
+  if ((len = m->replen) == 0)
     continue;
   n = pcre_exec(regex_whitelisted_macro, NULL, CS m->replacement, len,
    0, PCRE_EOPT, NULL, 0);
@@ -1463,6 +1402,39 @@ return TRUE;
 }
 
 
+/*************************************************
+*          Expansion testing                    *
+*************************************************/
+
+/* Expand and print one item, doing macro-processing.
+
+Arguments:
+  item         line for expansion
+*/
+
+static void
+expansion_test_line(uschar * line)
+{
+int len;
+BOOL dummy_macexp;
+
+Ustrncpy(big_buffer, line, big_buffer_size);
+big_buffer[big_buffer_size-1] = '\0';
+len = Ustrlen(big_buffer);
+
+(void) macros_expand(0, &len, &dummy_macexp);
+
+if (isupper(big_buffer[0]))
+  {
+  if (macro_read_assignment(big_buffer))
+    printf("Defined macro '%s'\n", mlast->name);
+  }
+else
+  if ((line = expand_string(big_buffer))) printf("%s\n", CS line);
+  else printf("Failed: %s\n", expand_string_message);
+}
+
+
 /*************************************************
 *          Entry point and high-level code       *
 *************************************************/
@@ -2450,14 +2422,14 @@ for (i = 1; i < argc; i++)
         while (isspace(*s)) s++;
         }
 
-      for (m = macros; m; m = m->next)
+      for (m = macros_user; m; m = m->next)
         if (Ustrcmp(m->name, name) == 0)
           {
           fprintf(stderr, "exim: duplicated -D in command line\n");
           exit(EXIT_FAILURE);
           }
 
-      m = macro_create(name, s, TRUE, FALSE);
+      m = macro_create(name, s, TRUE);
 
       if (clmacro_count >= MAX_CLMACROS)
         {
@@ -2739,7 +2711,7 @@ for (i = 1; i < argc; i++)
     /* -MCD: set the smtp_use_dsn flag; this indicates that the host
        that exim is connected to supports the esmtp extension DSN */
 
-       case 'D': smtp_peer_options |= PEER_OFFERED_DSN; break;
+       case 'D': smtp_peer_options |= OPTION_DSN; break;
 
     /* -MCG: set the queue name, to a non-default value */
 
@@ -2749,12 +2721,12 @@ for (i = 1; i < argc; i++)
 
     /* -MCK: the peer offered CHUNKING.  Must precede -MC */
 
-       case 'K': smtp_peer_options |= PEER_OFFERED_CHUNKING; break;
+       case 'K': smtp_peer_options |= OPTION_CHUNKING; break;
 
     /* -MCP: set the smtp_use_pipelining flag; this is useful only when
     it preceded -MC (see above) */
 
-       case 'P': smtp_peer_options |= PEER_OFFERED_PIPE; break;
+       case 'P': smtp_peer_options |= OPTION_PIPE; break;
 
     /* -MCQ: pass on the pid of the queue-running process that started
     this chain of deliveries and the fd of its synchronizing pipe; this
@@ -2769,7 +2741,7 @@ for (i = 1; i < argc; i++)
     /* -MCS: set the smtp_use_size flag; this is useful only when it
     precedes -MC (see above) */
 
-       case 'S': smtp_peer_options |= PEER_OFFERED_SIZE; break;
+       case 'S': smtp_peer_options |= OPTION_SIZE; break;
 
 #ifdef SUPPORT_TLS
     /* -MCt: similar to -MCT below but the connection is still open
@@ -2789,7 +2761,7 @@ for (i = 1; i < argc; i++)
     precedes -MC (see above). The flag indicates that the host to which
     Exim is connected has offered TLS support. */
 
-       case 'T': smtp_peer_options |= PEER_OFFERED_TLS; break;
+       case 'T': smtp_peer_options |= OPTION_TLS; break;
 #endif
 
        default:  badarg = TRUE; break;
@@ -3202,10 +3174,10 @@ for (i = 1; i < argc; i++)
     which sets the host protocol and host name */
 
     if (*argrest == 0)
-      {
-      if (i+1 < argc) argrest = argv[++i]; else
+      if (i+1 < argc)
+       argrest = argv[++i];
+      else
         { badarg = TRUE; break; }
-      }
 
     if (*argrest != 0)
       {
@@ -3219,9 +3191,7 @@ for (i = 1; i < argc; i++)
 
       hn = Ustrchr(argrest, ':');
       if (hn == NULL)
-        {
         received_protocol = argrest;
-        }
       else
         {
        int old_pool = store_pool;
@@ -3801,12 +3771,9 @@ NOTE: immediatly after opening the configuration file we change the working
 directory to "/"! Later we change to $spool_directory. We do it there, because
 during readconf_main() some expansion takes place already. */
 
-/* Store the initial cwd before we change directories */
-if ((initial_cwd = os_getcwd(NULL, 0)) == NULL)
-  {
-  perror("exim: can't get the current working directory");
-  exit(EXIT_FAILURE);
-  }
+/* Store the initial cwd before we change directories.  Can be NULL if the
+dir has already been unlinked. */
+initial_cwd = os_getcwd(NULL, 0);
 
 /* checking:
     -be[m] expansion test        -
@@ -3826,9 +3793,6 @@ defined) */
 
 readconf_main(checking || list_options);
 
-if (builtin_macros_create_trigger) DEBUG(D_any)
-  debug_printf("Builtin macros created (expensive) due to config line '%.*s'\n",
-    Ustrlen(builtin_macros_create_trigger)-1, builtin_macros_create_trigger);
 
 /* Now in directory "/" */
 
@@ -3884,7 +3848,7 @@ else
   }
 
 /* At this point, we know if the user is privileged and some command-line
-options become possibly imperssible, depending upon the configuration file. */
+options become possibly impermissible, depending upon the configuration file. */
 
 if (checking && commandline_checks_require_admin && !admin_user) {
   fprintf(stderr, "exim: those command-line flags are set to require admin\n");
@@ -4133,9 +4097,8 @@ if (((debug_selector & D_any) != 0 || LOGGING(arguments))
       quote = US"";
       while (*pp != 0) if (isspace(*pp++)) { quote = US"\""; break; }
       }
-    sprintf(CS p, " %s%.*s%s", quote, (int)(big_buffer_size -
+    p += sprintf(CS p, " %s%.*s%s", quote, (int)(big_buffer_size -
       (p - big_buffer) - 4), printing, quote);
-    while (*p) p++;
     }
 
   if (LOGGING(arguments))
@@ -4156,6 +4119,7 @@ if (Uchdir(spool_directory) != 0)
   int dummy;
   (void)directory_make(spool_directory, US"", SPOOL_DIRECTORY_MODE, FALSE);
   dummy = /* quieten compiler */ Uchdir(spool_directory);
+  dummy = dummy;       /* yet more compiler quietening, sigh */
   }
 
 /* Handle calls with the -bi option. This is a sendmail option to rebuild *the*
@@ -4490,7 +4454,7 @@ if (test_retry_arg >= 0)
   if (test_retry_arg >= argc)
     {
     printf("-brt needs a domain or address argument\n");
-    exim_exit(EXIT_FAILURE);
+    exim_exit(EXIT_FAILURE, US"main");
     }
   s1 = argv[test_retry_arg++];
   s2 = NULL;
@@ -4539,8 +4503,9 @@ if (test_retry_arg >= 0)
       }
     }
 
-  yield = retry_find_config(s1, s2, basic_errno, more_errno);
-  if (yield == NULL) printf("No retry information found\n"); else
+  if (!(yield = retry_find_config(s1, s2, basic_errno, more_errno)))
+    printf("No retry information found\n");
+  else
     {
     retry_rule *r;
     more_errno = yield->more_errno;
@@ -4572,7 +4537,7 @@ if (test_retry_arg >= 0)
       printf("auth_failed  ");
     else printf("*  ");
 
-    for (r = yield->rules; r != NULL; r = r->next)
+    for (r = yield->rules; r; r = r->next)
       {
       printf("%c,%s", r->rule, readconf_printtime(r->timeout)); /* Do not */
       printf(",%s", readconf_printtime(r->p1));                 /* amalgamate */
@@ -4595,7 +4560,7 @@ if (test_retry_arg >= 0)
 
     printf("\n");
     }
-  exim_exit(EXIT_SUCCESS);
+  exim_exit(EXIT_SUCCESS, US"main");
   }
 
 /* Handle a request to list one or more configuration options */
@@ -4603,30 +4568,33 @@ if (test_retry_arg >= 0)
 
 if (list_options)
   {
+  BOOL fail = FALSE;
   set_process_info("listing variables");
-  if (recipients_arg >= argc) readconf_print(US"all", NULL, flag_n);
-    else for (i = recipients_arg; i < argc; i++)
+  if (recipients_arg >= argc)
+    fail = !readconf_print(US"all", NULL, flag_n);
+  else for (i = recipients_arg; i < argc; i++)
+    {
+    if (i < argc - 1 &&
+       (Ustrcmp(argv[i], "router") == 0 ||
+        Ustrcmp(argv[i], "transport") == 0 ||
+        Ustrcmp(argv[i], "authenticator") == 0 ||
+        Ustrcmp(argv[i], "macro") == 0 ||
+        Ustrcmp(argv[i], "environment") == 0))
       {
-      if (i < argc - 1 &&
-          (Ustrcmp(argv[i], "router") == 0 ||
-           Ustrcmp(argv[i], "transport") == 0 ||
-           Ustrcmp(argv[i], "authenticator") == 0 ||
-           Ustrcmp(argv[i], "macro") == 0 ||
-           Ustrcmp(argv[i], "environment") == 0))
-        {
-        readconf_print(argv[i+1], argv[i], flag_n);
-        i++;
-        }
-      else readconf_print(argv[i], NULL, flag_n);
+      fail |= !readconf_print(argv[i+1], argv[i], flag_n);
+      i++;
       }
-  exim_exit(EXIT_SUCCESS);
+    else
+      fail = !readconf_print(argv[i], NULL, flag_n);
+    }
+  exim_exit(fail ? EXIT_FAILURE : EXIT_SUCCESS, US"main");
   }
 
 if (list_config)
   {
   set_process_info("listing config");
-  readconf_print(US"config", NULL, flag_n);
-  exim_exit(EXIT_SUCCESS);
+  exim_exit(readconf_print(US"config", NULL, flag_n)
+               ? EXIT_SUCCESS : EXIT_FAILURE, US"main");
   }
 
 
@@ -4655,7 +4623,7 @@ if (msg_action_arg > 0 && msg_action != MSG_LOAD)
   if (prod_requires_admin && !admin_user)
     {
     fprintf(stderr, "exim: Permission denied\n");
-    exim_exit(EXIT_FAILURE);
+    exim_exit(EXIT_FAILURE, US"main");
     }
   set_process_info("delivering specified messages");
   if (deliver_give_up) forced_delivery = deliver_force_thaw = TRUE;
@@ -4674,11 +4642,11 @@ if (msg_action_arg > 0 && msg_action != MSG_LOAD)
       {
       fprintf(stderr, "failed to fork delivery process for %s: %s\n", argv[i],
         strerror(errno));
-      exim_exit(EXIT_FAILURE);
+      exim_exit(EXIT_FAILURE, US"main");
       }
     else wait(&status);
     }
-  exim_exit(EXIT_SUCCESS);
+  exim_exit(EXIT_SUCCESS, US"main");
   }
 
 
@@ -4697,7 +4665,7 @@ if (queue_interval == 0 && !daemon_listen)
   else
     set_process_info("running the queue (single queue run)");
   queue_run(start_queue_run_id, stop_queue_run_id, FALSE);
-  exim_exit(EXIT_SUCCESS);
+  exim_exit(EXIT_SUCCESS, US"main");
   }
 
 
@@ -4720,10 +4688,9 @@ for (i = 0;;)
     /* If user name has not been set by -F, set it from the passwd entry
     unless -f has been used to set the sender address by a trusted user. */
 
-    if (originator_name == NULL)
+    if (!originator_name)
       {
-      if (sender_address == NULL ||
-           (!trusted_caller && filter_test == FTEST_NONE))
+      if (!sender_address || (!trusted_caller && filter_test == FTEST_NONE))
         {
         uschar *name = US pw->pw_gecos;
         uschar *amp = Ustrchr(name, '&');
@@ -4733,11 +4700,11 @@ for (i = 0;;)
         replaced by a copy of the login name, and some even specify that
         the first character should be upper cased, so that's what we do. */
 
-        if (amp != NULL)
+        if (amp)
           {
           int loffset;
           string_format(buffer, sizeof(buffer), "%.*s%n%s%s",
-            amp - name, name, &loffset, originator_login, amp + 1);
+            (int)(amp - name), name, &loffset, originator_login, amp + 1);
           buffer[loffset] = toupper(buffer[loffset]);
           name = buffer;
           }
@@ -4745,7 +4712,7 @@ for (i = 0;;)
         /* If a pattern for matching the gecos field was supplied, apply
         it and then expand the name string. */
 
-        if (gecos_pattern != NULL && gecos_name != NULL)
+        if (gecos_pattern && gecos_name)
           {
           const pcre *re;
           re = regex_must_compile(gecos_pattern, FALSE, TRUE); /* Use malloc */
@@ -4754,7 +4721,7 @@ for (i = 0;;)
             {
             uschar *new_name = expand_string(gecos_name);
             expand_nmax = -1;
-            if (new_name != NULL)
+            if (new_name)
               {
               DEBUG(D_receive) debug_printf("user name \"%s\" extracted from "
                 "gecos field \"%s\"\n", new_name, name);
@@ -4851,10 +4818,10 @@ if (test_rewrite_arg >= 0)
   if (test_rewrite_arg >= argc)
     {
     printf("-brw needs an address argument\n");
-    exim_exit(EXIT_FAILURE);
+    exim_exit(EXIT_FAILURE, US"main");
     }
   rewrite_test(argv[test_rewrite_arg]);
-  exim_exit(EXIT_SUCCESS);
+  exim_exit(EXIT_SUCCESS, US"main");
   }
 
 /* A locally-supplied message is considered to be coming from a local user
@@ -4969,7 +4936,7 @@ if (verify_address_mode || address_test_mode)
     }
 
   route_tidyup();
-  exim_exit(exit_value);
+  exim_exit(exit_value, US"main");
   }
 
 /* Handle expansion checking. Either expand items on the command line, or read
@@ -4999,7 +4966,7 @@ if (expansion_test)
   /* Read a test message from a file. We fudge it up to be on stdin, saving
   stdin itself for later reading of expansion strings. */
 
-  else if (expansion_test_message != NULL)
+  else if (expansion_test_message)
     {
     int save_stdin = dup(0);
     int fd = Uopen(expansion_test_message, O_RDONLY, 0);
@@ -5019,6 +4986,10 @@ if (expansion_test)
     clearerr(stdin);               /* Required by Darwin */
     }
 
+  /* Only admin users may see config-file macros this way */
+
+  if (!admin_user) macros_user = macros = mlast = NULL;
+
   /* Allow $recipients for this testing */
 
   enable_dollar_recipients = TRUE;
@@ -5026,15 +4997,8 @@ if (expansion_test)
   /* Expand command line items */
 
   if (recipients_arg < argc)
-    {
     while (recipients_arg < argc)
-      {
-      uschar *s = argv[recipients_arg++];
-      uschar *ss = expand_string(s);
-      if (ss == NULL) printf ("Failed: %s\n", expand_string_message);
-      else printf("%s\n", CS ss);
-      }
-    }
+      expansion_test_line(argv[recipients_arg++]);
 
   /* Read stdin */
 
@@ -5042,25 +5006,18 @@ if (expansion_test)
     {
     char *(*fn_readline)(const char *) = NULL;
     void (*fn_addhist)(const char *) = NULL;
+    uschar * s;
 
-    #ifdef USE_READLINE
+#ifdef USE_READLINE
     void *dlhandle = set_readline(&fn_readline, &fn_addhist);
-    #endif
+#endif
 
-    for (;;)
-      {
-      uschar *ss;
-      uschar *source = get_stdinput(fn_readline, fn_addhist);
-      if (source == NULL) break;
-      ss = expand_string(source);
-      if (ss == NULL)
-        printf ("Failed: %s\n", expand_string_message);
-      else printf("%s\n", CS ss);
-      }
+    while (s = get_stdinput(fn_readline, fn_addhist))
+      expansion_test_line(s);
 
-    #ifdef USE_READLINE
-    if (dlhandle != NULL) dlclose(dlhandle);
-    #endif
+#ifdef USE_READLINE
+    if (dlhandle) dlclose(dlhandle);
+#endif
     }
 
   /* The data file will be open after -Mset */
@@ -5071,7 +5028,7 @@ if (expansion_test)
     deliver_datafile = -1;
     }
 
-  exim_exit(EXIT_SUCCESS);
+  exim_exit(EXIT_SUCCESS, US"main: expansion test");
   }
 
 
@@ -5165,7 +5122,7 @@ if (host_checking)
       }
     smtp_log_no_mail();
     }
-  exim_exit(EXIT_SUCCESS);
+  exim_exit(EXIT_SUCCESS, US"main");
   }
 
 
@@ -5329,7 +5286,7 @@ if (smtp_input)
   if (!smtp_start_session())
     {
     mac_smtp_fflush();
-    exim_exit(EXIT_SUCCESS);
+    exim_exit(EXIT_SUCCESS, US"smtp_start toplevel");
     }
   }
 
@@ -5444,14 +5401,14 @@ while (more)
        cancel_cutthrough_connection(TRUE, US"receive dropped");
         if (more) goto moreloop;
         smtp_log_no_mail();               /* Log no mail if configured */
-        exim_exit(EXIT_FAILURE);
+        exim_exit(EXIT_FAILURE, US"receive toplevel");
         }
       }
     else
       {
       cancel_cutthrough_connection(TRUE, US"message setup dropped");
       smtp_log_no_mail();               /* Log no mail if configured */
-      exim_exit((rc == 0)? EXIT_SUCCESS : EXIT_FAILURE);
+      exim_exit(rc ? EXIT_FAILURE : EXIT_SUCCESS, US"msg setup toplevel");
       }
     }
 
@@ -5502,14 +5459,12 @@ while (more)
           if (error_handling == ERRORS_STDERR)
             {
             fprintf(stderr, "exim: too many recipients\n");
-            exim_exit(EXIT_FAILURE);
+            exim_exit(EXIT_FAILURE, US"main");
             }
           else
-            {
             return
               moan_to_sender(ERRMESS_TOOMANYRECIP, NULL, NULL, stdin, TRUE)?
                 errors_sender_rc : EXIT_FAILURE;
-            }
 
 #ifdef SUPPORT_I18N
        {
@@ -5538,7 +5493,7 @@ while (more)
             {
             fprintf(stderr, "exim: bad recipient address \"%s\": %s\n",
               string_printing(list[i]), errmess);
-            exim_exit(EXIT_FAILURE);
+            exim_exit(EXIT_FAILURE, US"main");
             }
           else
             {
@@ -5593,7 +5548,7 @@ while (more)
 
     if (!receive_timeout)
       {
-      struct timeval t = { 30*60, 0 }; /* 30 minutes */
+      struct timeval t = { .tv_sec = 30*60, .tv_usec = 0 };    /* 30 minutes */
       fd_set r;
 
       FD_ZERO(&r); FD_SET(0, &r);
@@ -5611,7 +5566,7 @@ while (more)
     for real; when reading the headers of a message for filter testing,
     it is TRUE if the headers were terminated by '.' and FALSE otherwise. */
 
-    if (message_id[0] == 0) exim_exit(EXIT_FAILURE);
+    if (message_id[0] == 0) exim_exit(EXIT_FAILURE, US"main");
     }  /* Non-SMTP message reception */
 
   /* If this is a filter testing run, there are headers in store, but
@@ -5656,7 +5611,7 @@ while (more)
     if (chdir("/"))   /* Get away from wherever the user is running this from */
       {
       DEBUG(D_receive) debug_printf("chdir(\"/\") failed\n");
-      exim_exit(EXIT_FAILURE);
+      exim_exit(EXIT_FAILURE, US"main");
       }
 
     /* Now we run either a system filter test, or a user filter test, or both.
@@ -5665,20 +5620,16 @@ while (more)
     explicitly. */
 
     if ((filter_test & FTEST_SYSTEM) != 0)
-      {
       if (!filter_runtest(filter_sfd, filter_test_sfile, TRUE, more))
-        exim_exit(EXIT_FAILURE);
-      }
+        exim_exit(EXIT_FAILURE, US"main");
 
     memcpy(filter_sn, filter_n, sizeof(filter_sn));
 
     if ((filter_test & FTEST_USER) != 0)
-      {
       if (!filter_runtest(filter_ufd, filter_test_ufile, FALSE, more))
-        exim_exit(EXIT_FAILURE);
-      }
+        exim_exit(EXIT_FAILURE, US"main");
 
-    exim_exit(EXIT_SUCCESS);
+    exim_exit(EXIT_SUCCESS, US"main");
     }
 
   /* Else act on the result of message reception. We should not get here unless
@@ -5804,7 +5755,7 @@ while (more)
          log_write(0, LOG_MAIN|LOG_PANIC,
            "process %d crashed with signal %d while delivering %s",
            (int)pid, status & 0x00ff, message_id);
-       if (mua_wrapper && (status & 0xffff) != 0) exim_exit(EXIT_FAILURE);
+       if (mua_wrapper && (status & 0xffff) != 0) exim_exit(EXIT_FAILURE, US"main");
        }
       }
     }
@@ -5836,8 +5787,9 @@ moreloop:
   store_reset(reset_point);
   }
 
-exim_exit(EXIT_SUCCESS);   /* Never returns */
+exim_exit(EXIT_SUCCESS, US"main");   /* Never returns */
 return 0;                  /* To stop compiler warning */
 }
 
+
 /* End of exim.c */