Avoid wait-for-tick on single-message connections
[exim.git] / src / src / exim.c
index abb3ba7c01a3c5adf11aa4179c8a2ecb10543944..e15d5e4760d252557d471c5e4e53b645e1f45b19 100644 (file)
@@ -211,6 +211,19 @@ exit(1);
 }
 
 
 }
 
 
+/***********************************************
+*            Handler for SIGSEGV               *
+***********************************************/
+
+static void
+segv_handler(int sig)
+{
+log_write(0, LOG_MAIN|LOG_PANIC, "SIGSEGV (maybe attempt to write to immutable memory)");
+signal(SIGSEGV, SIG_DFL);
+kill(getpid(), sig);
+}
+
+
 /*************************************************
 *             Handler for SIGUSR1                *
 *************************************************/
 /*************************************************
 *             Handler for SIGUSR1                *
 *************************************************/
@@ -233,18 +246,8 @@ int fd;
 
 os_restarting_signal(sig, usr1_handler);
 
 
 os_restarting_signal(sig, usr1_handler);
 
-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
-  root, do the creation in an exim:exim subprocess. */
-
-  int euid = geteuid();
-  if (euid == exim_uid)
-    fd = Uopen(process_log_path, O_CREAT|O_APPEND|O_WRONLY, LOG_MODE);
-  else if (euid == root_uid)
-    fd = log_create_as_exim(process_log_path);
-  }
+if (!process_log_path) return;
+fd = log_open_as_exim(process_log_path);
 
 /* If we are neither exim nor root, or if we failed to create the log file,
 give up. There is not much useful we can do with errors, since we don't want
 
 /* If we are neither exim nor root, or if we failed to create the log file,
 give up. There is not much useful we can do with errors, since we don't want
@@ -441,9 +444,10 @@ function prepares for the time when things are faster - and it also copes with
 clocks that go backwards.
 
 Arguments:
 clocks that go backwards.
 
 Arguments:
-  tgt_tv       A timeval which was used to create uniqueness; its usec field
+  prev_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.
                  has been rounded down to the value of the resolution.
                  We want to be sure the current time is greater than this.
+                On return, updated to current (rounded down).
   resolution   The resolution that was used to divide the microseconds
                  (1 for maildir, larger for message ids)
 
   resolution   The resolution that was used to divide the microseconds
                  (1 for maildir, larger for message ids)
 
@@ -451,7 +455,7 @@ Returns:       nothing
 */
 
 void
 */
 
 void
-exim_wait_tick(struct timeval * tgt_tv, int resolution)
+exim_wait_tick(struct timeval * prev_tv, int resolution)
 {
 struct timeval now_tv;
 long int now_true_usec;
 {
 struct timeval now_tv;
 long int now_true_usec;
@@ -460,13 +464,13 @@ exim_gettime(&now_tv);
 now_true_usec = now_tv.tv_usec;
 now_tv.tv_usec = (now_true_usec/resolution) * resolution;
 
 now_true_usec = now_tv.tv_usec;
 now_tv.tv_usec = (now_true_usec/resolution) * resolution;
 
-while (exim_tvcmp(&now_tv, tgt_tv) <= 0)
+while (exim_tvcmp(&now_tv, prev_tv) <= 0)
   {
   struct itimerval itval;
   itval.it_interval.tv_sec = 0;
   itval.it_interval.tv_usec = 0;
   {
   struct itimerval itval;
   itval.it_interval.tv_sec = 0;
   itval.it_interval.tv_usec = 0;
-  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;
+  itval.it_value.tv_sec = prev_tv->tv_sec - now_tv.tv_sec;
+  itval.it_value.tv_usec = prev_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"
 
   /* 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"
@@ -484,7 +488,7 @@ while (exim_tvcmp(&now_tv, tgt_tv) <= 0)
     if (!f.running_in_test_harness)
       {
       debug_printf("tick check: " TIME_T_FMT ".%06lu " TIME_T_FMT ".%06lu\n",
     if (!f.running_in_test_harness)
       {
       debug_printf("tick check: " TIME_T_FMT ".%06lu " TIME_T_FMT ".%06lu\n",
-        tgt_tv->tv_sec, (long) tgt_tv->tv_usec,
+        prev_tv->tv_sec, (long) prev_tv->tv_usec,
                now_tv.tv_sec, (long) now_tv.tv_usec);
       debug_printf("waiting " TIME_T_FMT ".%06lu sec\n",
         itval.it_value.tv_sec, (long) itval.it_value.tv_usec);
                now_tv.tv_sec, (long) now_tv.tv_usec);
       debug_printf("waiting " TIME_T_FMT ".%06lu sec\n",
         itval.it_value.tv_sec, (long) itval.it_value.tv_usec);
@@ -500,6 +504,7 @@ while (exim_tvcmp(&now_tv, tgt_tv) <= 0)
   now_true_usec = now_tv.tv_usec;
   now_tv.tv_usec = (now_true_usec/resolution) * resolution;
   }
   now_true_usec = now_tv.tv_usec;
   now_tv.tv_usec = (now_true_usec/resolution) * resolution;
   }
+*prev_tv = now_tv;
 }
 
 
 }
 
 
@@ -1641,7 +1646,6 @@ BOOL list_queue = FALSE;
 BOOL list_options = FALSE;
 BOOL list_config = FALSE;
 BOOL local_queue_only;
 BOOL list_options = FALSE;
 BOOL list_config = FALSE;
 BOOL local_queue_only;
-BOOL more = TRUE;
 BOOL one_msg_action = FALSE;
 BOOL opt_D_used = FALSE;
 BOOL queue_only_set = FALSE;
 BOOL one_msg_action = FALSE;
 BOOL opt_D_used = FALSE;
 BOOL queue_only_set = FALSE;
@@ -1815,7 +1819,8 @@ descriptive text. */
 
 process_info = store_get(PROCESS_INFO_SIZE, TRUE);     /* tainted */
 set_process_info("initializing");
 
 process_info = store_get(PROCESS_INFO_SIZE, TRUE);     /* tainted */
 set_process_info("initializing");
-os_restarting_signal(SIGUSR1, usr1_handler);
+os_restarting_signal(SIGUSR1, usr1_handler);           /* exiwhat */
+signal(SIGSEGV, segv_handler);                         /* log faults */
 
 /* If running in a dockerized environment, the TERM signal is only
 delegated to the PID 1 if we request it by setting an signal handler */
 
 /* If running in a dockerized environment, the TERM signal is only
 delegated to the PID 1 if we request it by setting an signal handler */
@@ -3256,6 +3261,10 @@ on the second character (the one after '-'), to save some effort. */
         -oPX:       delete pid file of daemon */
 
       case 'P':
         -oPX:       delete pid file of daemon */
 
       case 'P':
+       if (!f.running_in_test_harness && real_uid != root_uid && real_uid != exim_uid)
+         exim_fail("exim: only uid=%d or uid=%d can use -oP and -oPX "
+                    "(uid=%d euid=%d | %d)\n",
+                    root_uid, exim_uid, getuid(), geteuid(), real_uid);
        if (!*argrest) override_pid_file_path = argv[++i];
        else if (Ustrcmp(argrest, "X") == 0) delete_pid_file();
        else badarg = TRUE;
        if (!*argrest) override_pid_file_path = argv[++i];
        else if (Ustrcmp(argrest, "X") == 0) delete_pid_file();
        else badarg = TRUE;
@@ -3694,7 +3703,7 @@ else
   {
   struct rlimit rlp;
 
   {
   struct rlimit rlp;
 
-  #ifdef RLIMIT_NOFILE
+#ifdef RLIMIT_NOFILE
   if (getrlimit(RLIMIT_NOFILE, &rlp) < 0)
     {
     log_write(0, LOG_MAIN|LOG_PANIC, "getrlimit(RLIMIT_NOFILE) failed: %s",
   if (getrlimit(RLIMIT_NOFILE, &rlp) < 0)
     {
     log_write(0, LOG_MAIN|LOG_PANIC, "getrlimit(RLIMIT_NOFILE) failed: %s",
@@ -3717,9 +3726,9 @@ else
           strerror(errno));
       }
     }
           strerror(errno));
       }
     }
-  #endif
+#endif
 
 
-  #ifdef RLIMIT_NPROC
+#ifdef RLIMIT_NPROC
   if (getrlimit(RLIMIT_NPROC, &rlp) < 0)
     {
     log_write(0, LOG_MAIN|LOG_PANIC, "getrlimit(RLIMIT_NPROC) failed: %s",
   if (getrlimit(RLIMIT_NPROC, &rlp) < 0)
     {
     log_write(0, LOG_MAIN|LOG_PANIC, "getrlimit(RLIMIT_NPROC) failed: %s",
@@ -3727,20 +3736,20 @@ else
     rlp.rlim_cur = rlp.rlim_max = 0;
     }
 
     rlp.rlim_cur = rlp.rlim_max = 0;
     }
 
-  #ifdef RLIM_INFINITY
+ifdef RLIM_INFINITY
   if (rlp.rlim_cur != RLIM_INFINITY && rlp.rlim_cur < 1000)
     {
     rlp.rlim_cur = rlp.rlim_max = RLIM_INFINITY;
   if (rlp.rlim_cur != RLIM_INFINITY && rlp.rlim_cur < 1000)
     {
     rlp.rlim_cur = rlp.rlim_max = RLIM_INFINITY;
-  #else
+else
   if (rlp.rlim_cur < 1000)
     {
     rlp.rlim_cur = rlp.rlim_max = 1000;
   if (rlp.rlim_cur < 1000)
     {
     rlp.rlim_cur = rlp.rlim_max = 1000;
-  #endif
+endif
     if (setrlimit(RLIMIT_NPROC, &rlp) < 0)
       log_write(0, LOG_MAIN|LOG_PANIC, "setrlimit(RLIMIT_NPROC) failed: %s",
         strerror(errno));
     }
     if (setrlimit(RLIMIT_NPROC, &rlp) < 0)
       log_write(0, LOG_MAIN|LOG_PANIC, "setrlimit(RLIMIT_NPROC) failed: %s",
         strerror(errno));
     }
-  #endif
+#endif
   }
 
 /* Exim is normally entered as root (but some special configurations are
   }
 
 /* Exim is normally entered as root (but some special configurations are
@@ -3863,6 +3872,7 @@ is equivalent to the ability to modify a setuid binary!
 This needs to happen before we read the main configuration. */
 init_lookup_list();
 
 This needs to happen before we read the main configuration. */
 init_lookup_list();
 
+/*XXX this excrescence could move to the testsuite standard config setup file */
 #ifdef SUPPORT_I18N
 if (f.running_in_test_harness) smtputf8_advertise_hosts = NULL;
 #endif
 #ifdef SUPPORT_I18N
 if (f.running_in_test_harness) smtputf8_advertise_hosts = NULL;
 #endif
@@ -3877,7 +3887,6 @@ during readconf_main() some expansion takes place already. */
 
 /* Store the initial cwd before we change directories.  Can be NULL if the
 dir has already been unlinked. */
 
 /* Store the initial cwd before we change directories.  Can be NULL if the
 dir has already been unlinked. */
-errno = 0;
 initial_cwd = os_getcwd(NULL, 0);
 if (!initial_cwd && errno)
   exim_fail("exim: getting initial cwd failed: %s\n", strerror(errno));
 initial_cwd = os_getcwd(NULL, 0);
 if (!initial_cwd && errno)
   exim_fail("exim: getting initial cwd failed: %s\n", strerror(errno));
@@ -3902,19 +3911,21 @@ issues (currently about tls_advertise_hosts and keep_environment not being
 defined) */
 
   {
 defined) */
 
   {
+  int old_pool = store_pool;
 #ifdef MEASURE_TIMING
   struct timeval t0, diff;
   (void)gettimeofday(&t0, NULL);
 #endif
 
 #ifdef MEASURE_TIMING
   struct timeval t0, diff;
   (void)gettimeofday(&t0, NULL);
 #endif
 
+  store_pool = POOL_CONFIG;
   readconf_main(checking || list_options);
   readconf_main(checking || list_options);
+  store_pool = old_pool;
 
 #ifdef MEASURE_TIMING
   report_time_since(&t0, US"readconf_main (delta)");
 #endif
   }
 
 
 #ifdef MEASURE_TIMING
   report_time_since(&t0, US"readconf_main (delta)");
 #endif
   }
 
-
 /* Now in directory "/" */
 
 if (cleanup_environment() == FALSE)
 /* Now in directory "/" */
 
 if (cleanup_environment() == FALSE)
@@ -4171,11 +4182,9 @@ if (  (debug_selector & D_any  ||  LOGGING(arguments))
     p += 13;
   else
     {
     p += 13;
   else
     {
-    Ustrncpy(p + 4, initial_cwd, big_buffer_size-5);
-    p += 4 + Ustrlen(initial_cwd);
-    /* in case p is near the end and we don't provide enough space for
-     * string_format to be willing to write. */
-    *p = '\0';
+    p += 4;
+    snprintf(CS p, big_buffer_size - (p - big_buffer), "%s", CCS initial_cwd);
+    p += Ustrlen(CCS p);
     }
 
   (void)string_format(p, big_buffer_size - (p - big_buffer), " %d args:", argc);
     }
 
   (void)string_format(p, big_buffer_size - (p - big_buffer), " %d args:", argc);
@@ -4503,7 +4512,13 @@ if (msg_action_arg > 0 && msg_action != MSG_DELIVER && msg_action != MSG_LOAD)
   event_action gets expanded */
 
   if (msg_action == MSG_REMOVE)
   event_action gets expanded */
 
   if (msg_action == MSG_REMOVE)
+    {
+    int old_pool = store_pool;
+    store_pool = POOL_CONFIG;
     readconf_rest();
     readconf_rest();
+    store_pool = old_pool;
+    store_writeprotect(POOL_CONFIG);
+    }
 
   if (!one_msg_action)
     {
 
   if (!one_msg_action)
     {
@@ -4528,12 +4543,16 @@ Now, since the intro of the ${acl } expansion, ACL definitions may be
 needed in transports so we lost the optimisation. */
 
   {
 needed in transports so we lost the optimisation. */
 
   {
+  int old_pool = store_pool;
 #ifdef MEASURE_TIMING
   struct timeval t0, diff;
   (void)gettimeofday(&t0, NULL);
 #endif
 
 #ifdef MEASURE_TIMING
   struct timeval t0, diff;
   (void)gettimeofday(&t0, NULL);
 #endif
 
+  store_pool = POOL_CONFIG;
   readconf_rest();
   readconf_rest();
+  store_pool = old_pool;
+  store_writeprotect(POOL_CONFIG);
 
 #ifdef MEASURE_TIMING
   report_time_since(&t0, US"readconf_rest (delta)");
 
 #ifdef MEASURE_TIMING
   report_time_since(&t0, US"readconf_rest (delta)");
@@ -5460,15 +5479,15 @@ that SIG_IGN works. */
 
 if (!f.synchronous_delivery)
   {
 
 if (!f.synchronous_delivery)
   {
-  #ifdef SA_NOCLDWAIT
+#ifdef SA_NOCLDWAIT
   struct sigaction act;
   act.sa_handler = SIG_IGN;
   sigemptyset(&(act.sa_mask));
   act.sa_flags = SA_NOCLDWAIT;
   sigaction(SIGCHLD, &act, NULL);
   struct sigaction act;
   act.sa_handler = SIG_IGN;
   sigemptyset(&(act.sa_mask));
   act.sa_flags = SA_NOCLDWAIT;
   sigaction(SIGCHLD, &act, NULL);
-  #else
+#else
   signal(SIGCHLD, SIG_IGN);
   signal(SIGCHLD, SIG_IGN);
-  #endif
+#endif
   }
 
 /* Save the current store pool point, for resetting at the start of
   }
 
 /* Save the current store pool point, for resetting at the start of
@@ -5480,7 +5499,7 @@ real_sender_address = sender_address;
 messages to be read (SMTP input), or FALSE otherwise (not SMTP, or SMTP channel
 collapsed). */
 
 messages to be read (SMTP input), or FALSE otherwise (not SMTP, or SMTP channel
 collapsed). */
 
-while (more)
+for (BOOL more = TRUE; more; )
   {
   rmark reset_point = store_mark();
   message_id[0] = 0;
   {
   rmark reset_point = store_mark();
   message_id[0] = 0;
@@ -5522,10 +5541,10 @@ while (more)
       /* Now get the data for the message */
 
       more = receive_msg(extract_recipients);
       /* Now get the data for the message */
 
       more = receive_msg(extract_recipients);
-      if (message_id[0] == 0)
+      if (!message_id[0])
         {
        cancel_cutthrough_connection(TRUE, US"receive dropped");
         {
        cancel_cutthrough_connection(TRUE, US"receive dropped");
-        if (more) goto moreloop;
+        if (more) goto MORELOOP;
         smtp_log_no_mail();               /* Log no mail if configured */
         exim_exit(EXIT_FAILURE);
         }
         smtp_log_no_mail();               /* Log no mail if configured */
         exim_exit(EXIT_FAILURE);
         }
@@ -5602,11 +5621,12 @@ while (more)
           parse_extract_address(s, &errmess, &start, &end, &domain, FALSE);
 
 #ifdef SUPPORT_I18N
           parse_extract_address(s, &errmess, &start, &end, &domain, FALSE);
 
 #ifdef SUPPORT_I18N
-       if (string_is_utf8(recipient))
-         message_smtputf8 = TRUE;
-       else
-         allow_utf8_domains = b;
+        if (recipient)
+          if (string_is_utf8(recipient)) message_smtputf8 = TRUE;
+          else allow_utf8_domains = b;
        }
        }
+#else
+        ;
 #endif
         if (domain == 0 && !f.allow_unqualified_recipient)
           {
 #endif
         if (domain == 0 && !f.allow_unqualified_recipient)
           {
@@ -5690,7 +5710,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. */
 
     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]) exim_exit(EXIT_FAILURE);
     }  /* Non-SMTP message reception */
 
   /* If this is a filter testing run, there are headers in store, but
     }  /* Non-SMTP message reception */
 
   /* If this is a filter testing run, there are headers in store, but
@@ -5883,11 +5903,11 @@ while (more)
   finished subprocesses here, in case there are lots of messages coming in
   from the same source. */
 
   finished subprocesses here, in case there are lots of messages coming in
   from the same source. */
 
-  #ifndef SIG_IGN_WORKS
+#ifndef SIG_IGN_WORKS
   while (waitpid(-1, NULL, WNOHANG) > 0);
   while (waitpid(-1, NULL, WNOHANG) > 0);
-  #endif
+#endif
 
 
-moreloop:
+MORELOOP:
   return_path = sender_address = NULL;
   authenticated_sender = NULL;
   deliver_localpart_orig = NULL;
   return_path = sender_address = NULL;
   authenticated_sender = NULL;
   deliver_localpart_orig = NULL;