GnuTLS: tweak debug output
[exim.git] / src / src / exim.c
index 2b6297bf5d73524fbc6ac0945e33797134c50b2e..6bbf9b11efe08cd2a949989bcdcd43b83c78ada7 100644 (file)
@@ -23,6 +23,10 @@ Also a few functions that don't naturally fit elsewhere. */
 # endif
 #endif
 
+#ifndef _TIME_H
+# include <time.h>
+#endif
+
 extern void init_lookup_list(void);
 
 
@@ -106,7 +110,7 @@ if (use_malloc)
   pcre_free = function_store_free;
   }
 if (caseless) options |= PCRE_CASELESS;
-yield = pcre_compile(CCS pattern, options, (const char **)&error, &offset, NULL);
+yield = pcre_compile(CCS pattern, options, CCSS &error, &offset, NULL);
 pcre_malloc = function_store_get;
 pcre_free = function_dummy_free;
 if (yield == NULL)
@@ -202,7 +206,7 @@ va_end(ap);
 static void
 term_handler(int sig)
 {
-  exit(1);
+exit(1);
 }
 
 
@@ -292,7 +296,7 @@ will wait for ever, so we panic in this instance. (There was a case of this
 when a bug in a function that calls milliwait() caused it to pass invalid data.
 That's when I added the check. :-)
 
-We assume it to be not worth sleeping for under 100us; this value will
+We assume it to be not worth sleeping for under 50us; this value will
 require revisiting as hardware advances.  This avoids the issue of
 a zero-valued timer setting meaning "never fire".
 
@@ -306,7 +310,7 @@ milliwait(struct itimerval *itval)
 sigset_t sigmask;
 sigset_t old_sigmask;
 
-if (itval->it_value.tv_usec < 100 && itval->it_value.tv_sec == 0)
+if (itval->it_value.tv_usec < 50 && itval->it_value.tv_sec == 0)
   return;
 (void)sigemptyset(&sigmask);                           /* Empty mask */
 (void)sigaddset(&sigmask, SIGALRM);                    /* Add SIGALRM */
@@ -377,6 +381,25 @@ return 0;
 *          Clock tick wait function              *
 *************************************************/
 
+#ifdef _POSIX_MONOTONIC_CLOCK
+/* Amount CLOCK_MONOTONIC is behind realtime, at startup. */
+static struct timespec offset_ts;
+
+static void
+exim_clock_init(void)
+{
+struct timeval tv;
+if (clock_gettime(CLOCK_MONOTONIC, &offset_ts) != 0) return;
+(void)gettimeofday(&tv, NULL);
+offset_ts.tv_sec = tv.tv_sec - offset_ts.tv_sec;
+offset_ts.tv_nsec = tv.tv_usec * 1000 - offset_ts.tv_nsec;
+if (offset_ts.tv_nsec >= 0) return;
+offset_ts.tv_sec--;
+offset_ts.tv_nsec += 1000*1000*1000;
+}
+#endif
+
+
 /* Exim uses a time + a pid to generate a unique identifier in two places: its
 message IDs, and in file names for maildir deliveries. Because some OS now
 re-use pids within the same second, sub-second times are now being used.
@@ -388,7 +411,7 @@ function prepares for the time when things are faster - and it also copes with
 clocks that go backwards.
 
 Arguments:
-  then_tv      A timeval which was used to create uniqueness; its usec field
+  tgt_tv       A timeval which was used to create uniqueness; its usec field
                  has been rounded down to the value of the resolution.
                  We want to be sure the current time is greater than this.
   resolution   The resolution that was used to divide the microseconds
@@ -398,26 +421,45 @@ Returns:       nothing
 */
 
 void
-exim_wait_tick(struct timeval *then_tv, int resolution)
+exim_wait_tick(struct timeval * tgt_tv, int resolution)
 {
 struct timeval now_tv;
 long int now_true_usec;
 
-(void)gettimeofday(&now_tv, NULL);
-now_true_usec = now_tv.tv_usec;
-now_tv.tv_usec = (now_true_usec/resolution) * resolution;
+#ifdef _POSIX_MONOTONIC_CLOCK
+struct timespec now_ts;
+
+if (clock_gettime(CLOCK_MONOTONIC, &now_ts) == 0)
+  {
+  now_ts.tv_sec += offset_ts.tv_sec;
+  if ((now_ts.tv_nsec += offset_ts.tv_nsec) >= 1000*1000*1000)
+    {
+    now_ts.tv_sec++;
+    now_ts.tv_nsec -= 1000*1000*1000;
+    }
+  now_tv.tv_sec = now_ts.tv_sec;
+  now_true_usec = (now_ts.tv_nsec / (resolution * 1000)) * resolution;
+  now_tv.tv_usec = now_true_usec;
+  }
+else
+#endif
+  {
+  (void)gettimeofday(&now_tv, NULL);
+  now_true_usec = now_tv.tv_usec;
+  now_tv.tv_usec = (now_true_usec/resolution) * resolution;
+  }
 
-if (exim_tvcmp(&now_tv, then_tv) <= 0)
+while (exim_tvcmp(&now_tv, tgt_tv) <= 0)
   {
   struct itimerval itval;
   itval.it_interval.tv_sec = 0;
   itval.it_interval.tv_usec = 0;
-  itval.it_value.tv_sec = then_tv->tv_sec - now_tv.tv_sec;
-  itval.it_value.tv_usec = then_tv->tv_usec + resolution - now_true_usec;
+  itval.it_value.tv_sec = tgt_tv->tv_sec - now_tv.tv_sec;
+  itval.it_value.tv_usec = tgt_tv->tv_usec + resolution - now_true_usec;
 
   /* We know that, overall, "now" is less than or equal to "then". Therefore, a
   negative value for the microseconds is possible only in the case when "now"
-  is more than a second less than "then". That means that itval.it_value.tv_sec
+  is more than a second less than "tgt". That means that itval.it_value.tv_sec
   is greater than zero. The following correction is therefore safe. */
 
   if (itval.it_value.tv_usec < 0)
@@ -431,14 +473,21 @@ if (exim_tvcmp(&now_tv, then_tv) <= 0)
     if (!f.running_in_test_harness)
       {
       debug_printf("tick check: " TIME_T_FMT ".%06lu " TIME_T_FMT ".%06lu\n",
-        then_tv->tv_sec, (long) then_tv->tv_usec,
+        tgt_tv->tv_sec, (long) tgt_tv->tv_usec,
                now_tv.tv_sec, (long) now_tv.tv_usec);
-      debug_printf("waiting " TIME_T_FMT ".%06lu\n",
+      debug_printf("waiting " TIME_T_FMT ".%06lu sec\n",
         itval.it_value.tv_sec, (long) itval.it_value.tv_usec);
       }
     }
 
   milliwait(&itval);
+
+  /* Be prapared to go around if the kernel does not implement subtick
+  granularity (GNU Hurd) */
+
+  (void)gettimeofday(&now_tv, NULL);
+  now_true_usec = now_tv.tv_usec;
+  now_tv.tv_usec = (now_true_usec/resolution) * resolution;
   }
 }
 
@@ -901,7 +950,7 @@ fprintf(fp, "Support for:");
 #ifndef DISABLE_OCSP
   fprintf(fp, " OCSP");
 #endif
-#ifdef SUPPORT_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
   fprintf(fp, " PIPE_CONNECT");
 #endif
 #ifndef DISABLE_PRDR
@@ -920,7 +969,7 @@ fprintf(fp, "Support for:");
   fprintf(fp, " DMARC");
 #endif
 #ifdef TCP_FASTOPEN
-  deliver_init();
+  tcp_init();
   if (f.tcp_fastopen_ok) fprintf(fp, " TCP_Fast_Open");
 #endif
 #ifdef EXPERIMENTAL_LMDB
@@ -929,7 +978,7 @@ fprintf(fp, "Support for:");
 #ifdef EXPERIMENTAL_QUEUEFILE
   fprintf(fp, " Experimental_QUEUEFILE");
 #endif
-#ifdef EXPERIMENTAL_SRS
+#if defined(EXPERIMENTAL_SRS) || defined(EXPERIMENTAL_SRS_NATIVE)
   fprintf(fp, " Experimental_SRS");
 #endif
 #ifdef EXPERIMENTAL_ARC
@@ -1065,6 +1114,9 @@ show_db_version(fp);
 #ifdef SUPPORT_I18N
   utf8_version_report(fp);
 #endif
+#ifdef SUPPORT_SPF
+  spf_lib_version_report(fp);
+#endif
 
   for (auth_info * authi = auths_available; *authi->driver_name != '\0'; ++authi)
     if (authi->version_report)
@@ -1213,9 +1265,9 @@ void *dlhandle;
 void *dlhandle_curses = dlopen("libcurses." DYNLIB_FN_EXT, RTLD_GLOBAL|RTLD_LAZY);
 
 dlhandle = dlopen("libreadline." DYNLIB_FN_EXT, RTLD_GLOBAL|RTLD_NOW);
-if (dlhandle_curses != NULL) dlclose(dlhandle_curses);
+if (dlhandle_curses) dlclose(dlhandle_curses);
 
-if (dlhandle != NULL)
+if (dlhandle)
   {
   /* Checked manual pages; at least in GNU Readline 6.1, the prototypes are:
    *   char * readline (const char *prompt);
@@ -1225,9 +1277,7 @@ if (dlhandle != NULL)
   *fn_addhist_ptr = (void(*)(const char*))dlsym(dlhandle, "add_history");
   }
 else
-  {
   DEBUG(D_any) debug_printf("failed to load readline: %s\n", dlerror());
-  }
 
 return dlhandle;
 }
@@ -1666,6 +1716,12 @@ make quite sure. */
 
 setlocale(LC_ALL, "C");
 
+/* Get the offset between CLOCK_MONOTONIC and wallclock */
+
+#ifdef _POSIX_MONOTONIC_CLOCK
+exim_clock_init();
+#endif
+
 /* Set up the default handler for timing using alarm(). */
 
 os_non_restarting_signal(SIGALRM, sigalrm_handler);
@@ -1983,7 +2039,7 @@ for (i = 1; i < argc; i++)
             ignore = TRUE;
           break;
         }
-      if (!ignore) { badarg = TRUE; break; }
+      if (!ignore) badarg = TRUE;
       }
     break;
 
@@ -1991,283 +2047,291 @@ for (i = 1; i < argc; i++)
     so has no need of it. */
 
     case 'B':
-    if (*argrest == 0) i++;       /* Skip over the type */
+    if (!*argrest) i++;       /* Skip over the type */
     break;
 
 
     case 'b':
-    receiving_message = FALSE;    /* Reset TRUE for -bm, -bS, -bs below */
-
-    /* -bd:  Run in daemon mode, awaiting SMTP connections.
-       -bdf: Ditto, but in the foreground.
-    */
-
-    if (*argrest == 'd')
-      {
-      f.daemon_listen = TRUE;
-      if (*(++argrest) == 'f') f.background_daemon = FALSE;
-        else if (*argrest != 0) { badarg = TRUE; break; }
-      }
-
-    /* -be:  Run in expansion test mode
-       -bem: Ditto, but read a message from a file first
-    */
-
-    else if (*argrest == 'e')
-      {
-      expansion_test = checking = TRUE;
-      if (argrest[1] == 'm')
-        {
-        if (++i >= argc) { badarg = TRUE; break; }
-        expansion_test_message = argv[i];
-        argrest++;
-        }
-      if (argrest[1] != 0) { badarg = TRUE; break; }
-      }
-
-    /* -bF:  Run system filter test */
-
-    else if (*argrest == 'F')
-      {
-      filter_test |= checking = FTEST_SYSTEM;
-      if (*(++argrest) != 0) { badarg = TRUE; break; }
-      if (++i < argc) filter_test_sfile = argv[i]; else
-        exim_fail("exim: file name expected after %s\n", argv[i-1]);
-      }
-
-    /* -bf:  Run user filter test
-       -bfd: Set domain for filter testing
-       -bfl: Set local part for filter testing
-       -bfp: Set prefix for filter testing
-       -bfs: Set suffix for filter testing
-    */
-
-    else if (*argrest == 'f')
-      {
-      if (*(++argrest) == 0)
-        {
-        filter_test |= checking = FTEST_USER;
-        if (++i < argc) filter_test_ufile = argv[i]; else
-          exim_fail("exim: file name expected after %s\n", argv[i-1]);
-        }
-      else
-        {
-        if (++i >= argc)
-          exim_fail("exim: string expected after %s\n", arg);
-        if (Ustrcmp(argrest, "d") == 0) ftest_domain = argv[i];
-        else if (Ustrcmp(argrest, "l") == 0) ftest_localpart = argv[i];
-        else if (Ustrcmp(argrest, "p") == 0) ftest_prefix = argv[i];
-        else if (Ustrcmp(argrest, "s") == 0) ftest_suffix = argv[i];
-        else { badarg = TRUE; break; }
-        }
-      }
-
-    /* -bh: Host checking - an IP address must follow. */
-
-    else if (Ustrcmp(argrest, "h") == 0 || Ustrcmp(argrest, "hc") == 0)
-      {
-      if (++i >= argc) { badarg = TRUE; break; }
-      sender_host_address = argv[i];
-      host_checking = checking = f.log_testing_mode = TRUE;
-      f.host_checking_callout = argrest[1] == 'c';
-      message_logs = FALSE;
-      }
-
-    /* -bi: This option is used by sendmail to initialize *the* alias file,
-    though it has the -oA option to specify a different file. Exim has no
-    concept of *the* alias file, but since Sun's YP make script calls
-    sendmail this way, some support must be provided. */
-
-    else if (Ustrcmp(argrest, "i") == 0) bi_option = TRUE;
-
-    /* -bI: provide information, of the type to follow after a colon.
-    This is an Exim flag. */
-
-    else if (argrest[0] == 'I' && Ustrlen(argrest) >= 2 && argrest[1] == ':')
-      {
-      uschar *p = &argrest[2];
-      info_flag = CMDINFO_HELP;
-      if (Ustrlen(p))
-        {
-        if (strcmpic(p, CUS"sieve") == 0)
-          {
-          info_flag = CMDINFO_SIEVE;
-          info_stdout = TRUE;
-          }
-        else if (strcmpic(p, CUS"dscp") == 0)
-          {
-          info_flag = CMDINFO_DSCP;
-          info_stdout = TRUE;
-          }
-        else if (strcmpic(p, CUS"help") == 0)
-          {
-          info_stdout = TRUE;
-          }
-        }
-      }
-
-    /* -bm: Accept and deliver message - the default option. Reinstate
-    receiving_message, which got turned off for all -b options. */
-
-    else if (Ustrcmp(argrest, "m") == 0) receiving_message = TRUE;
-
-    /* -bmalware: test the filename given for malware */
-
-    else if (Ustrcmp(argrest, "malware") == 0)
-      {
-      if (++i >= argc) { badarg = TRUE; break; }
-      checking = TRUE;
-      malware_test_file = argv[i];
-      }
-
-    /* -bnq: For locally originating messages, do not qualify unqualified
-    addresses. In the envelope, this causes errors; in header lines they
-    just get left. */
-
-    else if (Ustrcmp(argrest, "nq") == 0)
-      {
-      f.allow_unqualified_sender = FALSE;
-      f.allow_unqualified_recipient = FALSE;
-      }
-
-    /* -bpxx: List the contents of the mail queue, in various forms. If
-    the option is -bpc, just a queue count is needed. Otherwise, if the
-    first letter after p is r, then order is random. */
-
-    else if (*argrest == 'p')
-      {
-      if (*(++argrest) == 'c')
-        {
-        count_queue = TRUE;
-        if (*(++argrest) != 0) badarg = TRUE;
-        break;
-        }
-
-      if (*argrest == 'r')
-        {
-        list_queue_option = 8;
-        argrest++;
-        }
-      else list_queue_option = 0;
-
-      list_queue = TRUE;
-
-      /* -bp: List the contents of the mail queue, top-level only */
-
-      if (*argrest == 0) {}
-
-      /* -bpu: List the contents of the mail queue, top-level undelivered */
-
-      else if (Ustrcmp(argrest, "u") == 0) list_queue_option += 1;
-
-      /* -bpa: List the contents of the mail queue, including all delivered */
-
-      else if (Ustrcmp(argrest, "a") == 0) list_queue_option += 2;
-
-      /* Unknown after -bp[r] */
-
-      else
-        {
-        badarg = TRUE;
-        break;
-        }
-      }
-
-
-    /* -bP: List the configuration variables given as the address list.
-    Force -v, so configuration errors get displayed. */
-
-    else if (Ustrcmp(argrest, "P") == 0)
       {
-      /* -bP config: we need to setup here, because later,
-       * when list_options is checked, the config is read already */
-      if (argv[i+1] && Ustrcmp(argv[i+1], "config") == 0)
-        {
-        list_config = TRUE;
-        readconf_save_config(version_string);
-        }
-      else
-        {
-        list_options = TRUE;
-        debug_selector |= D_v;
-        debug_file = stderr;
-        }
-      }
-
-    /* -brt: Test retry configuration lookup */
-
-    else if (Ustrcmp(argrest, "rt") == 0)
-      {
-      checking = TRUE;
-      test_retry_arg = i + 1;
-      goto END_ARG;
-      }
+      receiving_message = FALSE;    /* Reset TRUE for -bm, -bS, -bs below */
 
-    /* -brw: Test rewrite configuration */
+      switch (*argrest++)
+       {
+       /* -bd:  Run in daemon mode, awaiting SMTP connections.
+          -bdf: Ditto, but in the foreground.
+       */
+       case 'd':
+         f.daemon_listen = TRUE;
+         if (*argrest == 'f') f.background_daemon = FALSE;
+         else if (*argrest) badarg = TRUE;
+         break;
+
+       /* -be:  Run in expansion test mode
+          -bem: Ditto, but read a message from a file first
+       */
+       case 'e':
+         expansion_test = checking = TRUE;
+         if (*argrest == 'm')
+           {
+           if (++i >= argc) { badarg = TRUE; break; }
+           expansion_test_message = argv[i];
+           argrest++;
+           }
+         if (*argrest) badarg = TRUE;
+         break;
+
+       /* -bF:  Run system filter test */
+       case 'F':
+         filter_test |= checking = FTEST_SYSTEM;
+         if (*argrest) { badarg = TRUE; break; }
+         if (++i < argc) filter_test_sfile = argv[i]; else
+           exim_fail("exim: file name expected after %s\n", argv[i-1]);
+         break;
+
+       /* -bf:  Run user filter test
+          -bfd: Set domain for filter testing
+          -bfl: Set local part for filter testing
+          -bfp: Set prefix for filter testing
+          -bfs: Set suffix for filter testing
+       */
+       case 'f':
+         if (!*argrest)
+           {
+           filter_test |= checking = FTEST_USER;
+           if (++i < argc) filter_test_ufile = argv[i];
+           else exim_fail("exim: file name expected after %s\n", argv[i-1]);
+           }
+         else
+           {
+           if (++i >= argc)
+             exim_fail("exim: string expected after %s\n", arg);
+           if (Ustrcmp(argrest, "d") == 0) ftest_domain = argv[i];
+           else if (Ustrcmp(argrest, "l") == 0) ftest_localpart = argv[i];
+           else if (Ustrcmp(argrest, "p") == 0) ftest_prefix = argv[i];
+           else if (Ustrcmp(argrest, "s") == 0) ftest_suffix = argv[i];
+           else badarg = TRUE;
+           }
+         break;
+
+       /* -bh: Host checking - an IP address must follow. */
+       case 'h':
+         if (!*argrest || Ustrcmp(argrest, "c") == 0)
+           {
+           if (++i >= argc) { badarg = TRUE; break; }
+           sender_host_address = argv[i];
+           host_checking = checking = f.log_testing_mode = TRUE;
+           f.host_checking_callout = *argrest == 'c';
+           message_logs = FALSE;
+           }
+         else badarg = TRUE;
+         break;
+
+       /* -bi: This option is used by sendmail to initialize *the* alias file,
+       though it has the -oA option to specify a different file. Exim has no
+       concept of *the* alias file, but since Sun's YP make script calls
+       sendmail this way, some support must be provided. */
+       case 'i':
+         if (!*++argrest) bi_option = TRUE;
+         else badarg = TRUE;
+         break;
+
+       /* -bI: provide information, of the type to follow after a colon.
+       This is an Exim flag. */
+       case 'I':
+         if (Ustrlen(argrest) >= 1 && *argrest == ':')
+           {
+           uschar *p = argrest+1;
+           info_flag = CMDINFO_HELP;
+           if (Ustrlen(p))
+             if (strcmpic(p, CUS"sieve") == 0)
+               {
+               info_flag = CMDINFO_SIEVE;
+               info_stdout = TRUE;
+               }
+             else if (strcmpic(p, CUS"dscp") == 0)
+               {
+               info_flag = CMDINFO_DSCP;
+               info_stdout = TRUE;
+               }
+             else if (strcmpic(p, CUS"help") == 0)
+               info_stdout = TRUE;
+           }
+         else badarg = TRUE;
+         break;
+
+       /* -bm: Accept and deliver message - the default option. Reinstate
+       receiving_message, which got turned off for all -b options.
+          -bmalware: test the filename given for malware */
+       case 'm':
+         if (!*argrest) receiving_message = TRUE;
+         else if (Ustrcmp(argrest, "alware") == 0)
+           {
+           if (++i >= argc) { badarg = TRUE; break; }
+           checking = TRUE;
+           malware_test_file = argv[i];
+           }
+         else badarg = TRUE;
+         break;
+
+       /* -bnq: For locally originating messages, do not qualify unqualified
+       addresses. In the envelope, this causes errors; in header lines they
+       just get left. */
+       case 'n':
+         if (Ustrcmp(argrest, "q") == 0)
+           {
+           f.allow_unqualified_sender = FALSE;
+           f.allow_unqualified_recipient = FALSE;
+           }
+         else badarg = TRUE;
+         break;
+
+       /* -bpxx: List the contents of the mail queue, in various forms. If
+       the option is -bpc, just a queue count is needed. Otherwise, if the
+       first letter after p is r, then order is random. */
+       case 'p':
+         if (*argrest == 'c')
+           {
+           count_queue = TRUE;
+           if (*++argrest) badarg = TRUE;
+           break;
+           }
 
-    else if (Ustrcmp(argrest, "rw") == 0)
-      {
-      checking = TRUE;
-      test_rewrite_arg = i + 1;
-      goto END_ARG;
-      }
+         if (*argrest == 'r')
+           {
+           list_queue_option = 8;
+           argrest++;
+           }
+         else list_queue_option = 0;
 
-    /* -bS: Read SMTP commands on standard input, but produce no replies -
-    all errors are reported by sending messages. */
+         list_queue = TRUE;
 
-    else if (Ustrcmp(argrest, "S") == 0)
-      smtp_input = smtp_batched_input = receiving_message = TRUE;
+         /* -bp: List the contents of the mail queue, top-level only */
 
-    /* -bs: Read SMTP commands on standard input and produce SMTP replies
-    on standard output. */
+         if (!*argrest) {}
 
-    else if (Ustrcmp(argrest, "s") == 0) smtp_input = receiving_message = TRUE;
+         /* -bpu: List the contents of the mail queue, top-level undelivered */
 
-    /* -bt: address testing mode */
+         else if (Ustrcmp(argrest, "u") == 0) list_queue_option += 1;
 
-    else if (Ustrcmp(argrest, "t") == 0)
-      f.address_test_mode = checking = f.log_testing_mode = TRUE;
+         /* -bpa: List the contents of the mail queue, including all delivered */
 
-    /* -bv: verify addresses */
+         else if (Ustrcmp(argrest, "a") == 0) list_queue_option += 2;
 
-    else if (Ustrcmp(argrest, "v") == 0)
-      verify_address_mode = checking = f.log_testing_mode = TRUE;
+         /* Unknown after -bp[r] */
 
-    /* -bvs: verify sender addresses */
+         else badarg = TRUE;
+         break;
 
-    else if (Ustrcmp(argrest, "vs") == 0)
-      {
-      verify_address_mode = checking = f.log_testing_mode = TRUE;
-      verify_as_sender = TRUE;
-      }
 
-    /* -bV: Print version string and support details */
+       /* -bP: List the configuration variables given as the address list.
+       Force -v, so configuration errors get displayed. */
+       case 'P':
 
-    else if (Ustrcmp(argrest, "V") == 0)
-      {
-      printf("Exim version %s #%s built %s\n", version_string,
-        version_cnumber, version_date);
-      printf("%s\n", CS version_copyright);
-      version_printed = TRUE;
-      show_whats_supported(stdout);
-      f.log_testing_mode = TRUE;
-      }
+         /* -bP config: we need to setup here, because later,
+          * when list_options is checked, the config is read already */
+         if (*argrest)
+           badarg = TRUE;
+         else if (argv[i+1] && Ustrcmp(argv[i+1], "config") == 0)
+           {
+           list_config = TRUE;
+           readconf_save_config(version_string);
+           }
+         else
+           {
+           list_options = TRUE;
+           debug_selector |= D_v;
+           debug_file = stderr;
+           }
+         break;
+
+       /* -brt: Test retry configuration lookup */
+       case 'r':
+         if (Ustrcmp(argrest, "t") == 0)
+           {
+           checking = TRUE;
+           test_retry_arg = i + 1;
+           goto END_ARG;
+           }
 
-    /* -bw: inetd wait mode, accept a listening socket as stdin */
+         /* -brw: Test rewrite configuration */
 
-    else if (*argrest == 'w')
-      {
-      f.inetd_wait_mode = TRUE;
-      f.background_daemon = FALSE;
-      f.daemon_listen = TRUE;
-      if (*(++argrest) != '\0')
-        if ((inetd_wait_timeout = readconf_readtime(argrest, 0, FALSE)) <= 0)
-          exim_fail("exim: bad time value %s: abandoned\n", argv[i]);
+         else if (Ustrcmp(argrest, "w") == 0)
+           {
+           checking = TRUE;
+           test_rewrite_arg = i + 1;
+           goto END_ARG;
+           }
+         else badarg = TRUE;
+         break;
+
+       /* -bS: Read SMTP commands on standard input, but produce no replies -
+       all errors are reported by sending messages. */
+       case 'S':
+         if (!*argrest)
+           smtp_input = smtp_batched_input = receiving_message = TRUE;
+         else badarg = TRUE;
+         break;
+
+       /* -bs: Read SMTP commands on standard input and produce SMTP replies
+       on standard output. */
+       case 's':
+         if (!*argrest) smtp_input = receiving_message = TRUE;
+         else badarg = TRUE;
+         break;
+
+       /* -bt: address testing mode */
+       case 't':
+         if (!*argrest)
+           f.address_test_mode = checking = f.log_testing_mode = TRUE;
+         else badarg = TRUE;
+         break;
+
+       /* -bv: verify addresses */
+       case 'v':
+         if (!*argrest)
+           verify_address_mode = checking = f.log_testing_mode = TRUE;
+
+       /* -bvs: verify sender addresses */
+
+         else if (Ustrcmp(argrest, "s") == 0)
+           {
+           verify_address_mode = checking = f.log_testing_mode = TRUE;
+           verify_as_sender = TRUE;
+           }
+         else badarg = TRUE;
+         break;
+
+       /* -bV: Print version string and support details */
+       case 'V':
+         if (!*argrest)
+           {
+           printf("Exim version %s #%s built %s\n", version_string,
+             version_cnumber, version_date);
+           printf("%s\n", CS version_copyright);
+           version_printed = TRUE;
+           show_whats_supported(stdout);
+           f.log_testing_mode = TRUE;
+           }
+         else badarg = TRUE;
+         break;
+
+       /* -bw: inetd wait mode, accept a listening socket as stdin */
+       case 'w':
+         f.inetd_wait_mode = TRUE;
+         f.background_daemon = FALSE;
+         f.daemon_listen = TRUE;
+         if (*argrest)
+           if ((inetd_wait_timeout = readconf_readtime(argrest, 0, FALSE)) <= 0)
+             exim_fail("exim: bad time value %s: abandoned\n", argv[i]);
+         break;
+
+       default:
+         badarg = TRUE;
+         break;
+       }
+      break;
       }
 
-    else badarg = TRUE;
-    break;
-
 
     /* -C: change configuration file list; ignore if it isn't really
     a change! Enforce a prefix check if required. */
@@ -2787,6 +2851,11 @@ for (i = 1; i < argc; i++)
       msg_action = MSG_DELIVER;
       deliver_give_up = TRUE;
       }
+   else if (Ustrcmp(argrest, "G") == 0)
+      {
+      msg_action = MSG_SETQUEUE;
+      queue_name_dest = argv[++i];
+      }
     else if (Ustrcmp(argrest, "mad") == 0)
       {
       msg_action = MSG_MARK_ALL_DELIVERED;
@@ -3067,11 +3136,15 @@ for (i = 1; i < argc; i++)
 
     else if (Ustrcmp(argrest, "o") == 0) {}
 
-    /* -oP <name>: set pid file path for daemon */
+    /* -oP <name>: set pid file path for daemon
+       -oPX:       delete pid file of daemon */
 
     else if (Ustrcmp(argrest, "P") == 0)
       override_pid_file_path = argv[++i];
 
+    else if (Ustrcmp(argrest, "PX") == 0)
+      delete_pid_file();
+
     /* -or <n>: set timeout for non-SMTP acceptance
        -os <n>: set timeout for SMTP acceptance */
 
@@ -3574,11 +3647,15 @@ an error return. The following code should cope with both types of system.
  in the call to exim_setugid().
 
 However, if this process isn't running as root, setgroups() can't be used
-since you have to be root to run it, even if throwing away groups. Not being
-root here happens only in some unusual configurations. We just ignore the
-error. */
+since you have to be root to run it, even if throwing away groups.
+Except, sigh, for Hurd - where you can.
+Not being root here happens only in some unusual configurations. */
 
-if (setgroups(0, NULL) != 0 && setgroups(1, group_list) != 0 && !unprivileged)
+if (  !unprivileged
+#ifndef OS_SETGROUPS_ZERO_DROPS_ALL
+   && setgroups(0, NULL) != 0
+#endif
+   && setgroups(1, group_list) != 0)
   exim_fail("exim: setgroups() failed: %s\n", strerror(errno));
 
 /* If the configuration file name has been altered by an argument on the
@@ -4064,11 +4141,13 @@ count. Only an admin user can use the test interface to scan for email
 if (!f.admin_user)
   {
   BOOL debugset = (debug_selector & ~D_v) != 0;
-  if (deliver_give_up || f.daemon_listen || malware_test_file ||
-     (count_queue && queue_list_requires_admin) ||
-     (list_queue && queue_list_requires_admin) ||
-     (queue_interval >= 0 && prod_requires_admin) ||
-     (debugset && !f.running_in_test_harness))
+  if (  deliver_give_up || f.daemon_listen || malware_test_file
+     || count_queue && queue_list_requires_admin
+     || list_queue && queue_list_requires_admin
+     || queue_interval >= 0 && prod_requires_admin
+     || queue_name_dest && prod_requires_admin
+     || debugset && !f.running_in_test_harness
+     )
     exim_fail("exim:%s permission denied\n", debugset? " debugging" : "");
   }
 
@@ -4165,13 +4244,9 @@ now for those OS that require the first call to os_getloadavg() to be done as
 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)
-      ))
-  {
+if (  receiving_message
+   && (queue_only_load >= 0 || (f.is_inetd && smtp_load_reserve >= 0)))
   load_average = OS_GETLOADAVG();
-  }
 #endif
 
 /* The queue_only configuration option can be overridden by -odx on the command
@@ -4297,6 +4372,11 @@ if (msg_action_arg > 0 && msg_action != MSG_DELIVER && msg_action != MSG_LOAD)
     for (i = msg_action_arg; i < argc; i++)
       if (!queue_action(argv[i], msg_action, NULL, 0, 0))
         yield = EXIT_FAILURE;
+    switch (msg_action)
+      {
+      case MSG_REMOVE: case MSG_FREEZE: case MSG_THAW: break;
+      default: printf("\n"); break;
+      }
     }
 
   else if (!queue_action(argv[msg_action_arg], msg_action, argv, argc,
@@ -4480,31 +4560,9 @@ if (list_config)
   }
 
 
-/* Initialise subsystems as required */
-#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 MEASURE_TIMING
-  struct timeval t0;
-  gettimeofday(&t0, NULL);
-#endif
-  deliver_init();
-#ifdef MEASURE_TIMING
-  report_time_since(&t0, US"deliver_init (delta)");
-#endif
-  }
+/* Initialise subsystems as required. */
 
+tcp_init();
 
 /* Handle a request to deliver one or more messages that are already on the
 queue. Values of msg_action other than MSG_DELIVER and MSG_LOAD are dealt with
@@ -4699,6 +4757,23 @@ if (f.daemon_listen || f.inetd_wait_mode || queue_interval > 0)
     log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Daemon cannot be run when "
       "mua_wrapper is set");
     }
+
+# ifndef DISABLE_TLS
+  /* This also checks that the library linkage is working and we can call
+  routines in it, so call even if tls_require_ciphers is unset */
+    {
+# ifdef MEASURE_TIMING
+    struct timeval t0, diff;
+    (void)gettimeofday(&t0, NULL);
+# endif
+    if (!tls_dropprivs_validate_require_cipher(FALSE))
+      exit(1);
+# ifdef MEASURE_TIMING
+    report_time_since(&t0, US"validate_ciphers (delta)");
+# endif
+    }
+#endif
+
   daemon_go();
   }
 
@@ -4814,8 +4889,9 @@ if (verify_address_mode || f.address_test_mode)
     {
     while (recipients_arg < argc)
       {
-      uschar *s = argv[recipients_arg++];
-      while (*s != 0)
+      /* Supplied addresses are tainted since they come from a user */
+      uschar * s = string_copy_taint(argv[recipients_arg++], TRUE);
+      while (*s)
         {
         BOOL finished = FALSE;
         uschar *ss = parse_find_address_end(s, FALSE);
@@ -4823,16 +4899,16 @@ if (verify_address_mode || f.address_test_mode)
         test_address(s, flags, &exit_value);
         s = ss;
         if (!finished)
-          while (*(++s) != 0 && (*s == ',' || isspace(*s)));
+          while (*++s == ',' || isspace(*s)) ;
         }
       }
     }
 
   else for (;;)
     {
-    uschar *s = get_stdinput(NULL, NULL);
-    if (s == NULL) break;
-    test_address(s, flags, &exit_value);
+    uschar * s = get_stdinput(NULL, NULL);
+    if (!s) break;
+    test_address(string_copy_taint(s, TRUE), flags, &exit_value);
     }
 
   route_tidyup();
@@ -4959,9 +5035,9 @@ if (host_checking)
   if (!sender_ident_set)
     {
     sender_ident = NULL;
-    if (f.running_in_test_harness && sender_host_port != 0 &&
-        interface_address != NULL && interface_port != 0)
-      verify_get_ident(1413);
+    if (f.running_in_test_harness && sender_host_port
+       && interface_address && interface_port)
+      verify_get_ident(1223);          /* note hardwired port number */
     }
 
   /* In case the given address is a non-canonical IPv6 address, canonicalize
@@ -5326,13 +5402,13 @@ while (more)
 
     raw_sender = string_copy(sender_address);
 
-    /* Loop for each argument */
+    /* Loop for each argument (supplied by user hence tainted) */
 
     for (int i = 0; i < count; i++)
       {
       int start, end, domain;
-      uschar *errmess;
-      uschar *s = list[i];
+      uschar * errmess;
+      uschar * s = string_copy_taint(list[i], TRUE);
 
       /* Loop for each comma-separated address */