Avoid calling gettimeofday(), select() per char for cmdline message submission. ...
authorJeremy Harris <jgh146exb@wizmail.org>
Fri, 15 Oct 2021 23:12:16 +0000 (00:12 +0100)
committerJeremy Harris <jgh146exb@wizmail.org>
Fri, 15 Oct 2021 23:12:16 +0000 (00:12 +0100)
Broken-by: 3c55eef240
doc/doc-txt/ChangeLog
src/src/exim.c
src/src/filtertest.c
src/src/functions.h
src/src/globals.c
src/src/globals.h
src/src/receive.c
src/src/smtp_in.c
src/src/tls-gnu.c
src/src/tls-openssl.c
src/src/transports/autoreply.c

index 4e0d602dd278cda8831cbd50128a069155568737..1a29ae50bbaaeb91503f903dbb25eaae10e1d925 100644 (file)
@@ -21,6 +21,10 @@ JH/03 Constification work in the filters module required a major version
 JH/04 Fix ClamAV TCP use under FreeBSD. Previously the OS-specific shim for
       sendfile() didi not account for the way the ClamAV driver code called it.
 
+JH/05 Bug 2819: speed up command-line messages being read in.  Previously a
+      time check was being done for every character; replace that with one
+      per buffer.
+
 
 Exim version 4.95
 -----------------
index 6e97981b06935698cabcfcb61c67031d97065393..f718366f68803e9de79afed986d60f8fa2ca05f7 100644 (file)
@@ -5433,7 +5433,7 @@ if (smtp_input)
   {
   if (!f.is_inetd) set_process_info("accepting a local %sSMTP message from <%s>",
     smtp_batched_input? "batched " : "",
-    (sender_address!= NULL)? sender_address : originator_login);
+    sender_address ? sender_address : originator_login);
   }
 else
   {
@@ -5483,7 +5483,8 @@ if (smtp_input)
     }
   }
 
-/* Otherwise, set up the input size limit here. */
+/* Otherwise, set up the input size limit here and set no stdin stdio buffer
+(we handle buferring so as to have visibility of fill level). */
 
 else
   {
@@ -5495,6 +5496,8 @@ else
     else
       log_write(0, LOG_MAIN|LOG_PANIC_DIE, "invalid value for "
         "message_size_limit: %s", expand_string_message);
+
+  setvbuf(stdin, NULL, _IONBF, 0);
   }
 
 /* Loop for several messages when reading SMTP input. If we fork any child
index a2a60a8dd8a5be996aa58bfbb025b634fdee5ca0..3b4752ea5568b655460c0aaf79d2f64a10144740 100644 (file)
@@ -45,11 +45,11 @@ body_len = 0;
 body_linecount = 0;
 header_size = message_size;
 
-if (!dot_ended && !feof(stdin))
+if (!dot_ended && !stdin_feof())
   {
   if (!f.dot_ends)
     {
-    while ((ch = getc(stdin)) != EOF)
+    while ((ch = stdin_getc(GETC_BUFFER_UNLIMITED)) != EOF)
       {
       if (ch == 0) body_zerocount++;
       if (ch == '\n') body_linecount++;
@@ -62,7 +62,7 @@ if (!dot_ended && !feof(stdin))
   else
     {
     int ch_state = 1;
-    while ((ch = getc(stdin)) != EOF)
+    while ((ch = stdin_getc(GETC_BUFFER_UNLIMITED)) != EOF)
       {
       if (ch == 0) body_zerocount++;
       switch (ch_state)
@@ -99,6 +99,7 @@ if (!dot_ended && !feof(stdin))
     }
   if (s == message_body_end || s[-1] != '\n') body_linecount++;
   }
+debug_printf("%s %d\n", __FUNCTION__, __LINE__);
 
 message_body[body_len] = 0;
 message_body_size = message_size - header_size;
@@ -250,7 +251,7 @@ if (filter_type == FILTER_FORWARD)
 /* For a filter, set up the message_body variables and the message size if this
 is the first time this function has been called. */
 
-if (message_body == NULL) read_message_body(dot_ended);
+if (!message_body) read_message_body(dot_ended);
 
 /* Now pass the filter file to the function that interprets it. Because
 filter_test is not FILTER_NONE, the interpreter will output comments about what
@@ -269,10 +270,9 @@ if (is_system)
   }
 else
   {
-  yield = (filter_type == FILTER_SIEVE)?
-    sieve_interpret(filebuf, RDO_REWRITE, NULL, NULL, NULL, NULL, &generated, &error)
-    :
-    filter_interpret(filebuf, RDO_REWRITE, &generated, &error);
+  yield = filter_type == FILTER_SIEVE
+    ? sieve_interpret(filebuf, RDO_REWRITE, NULL, NULL, NULL, NULL, &generated, &error)
+    : filter_interpret(filebuf, RDO_REWRITE, &generated, &error);
   }
 
 return yield != FF_ERROR;
index 3f04ec7820076943c995d262ee016baf8437b6ad..401300d94453e92265afbd2a949ec7c639fe3e9c 100644 (file)
@@ -68,6 +68,7 @@ extern void    tls_free_cert(void **);
 extern int     tls_getc(unsigned);
 extern uschar *tls_getbuf(unsigned *);
 extern void    tls_get_cache(unsigned);
+extern BOOL    tls_hasc(void);
 extern BOOL    tls_import_cert(const uschar *, void **);
 extern BOOL    tls_is_name_for_cert(const uschar *, void *);
 # ifdef USE_OPENSSL
@@ -150,6 +151,7 @@ extern uschar *b64encode_taint(const uschar *, int, BOOL);
 extern int     b64decode(const uschar *, uschar **);
 extern int     bdat_getc(unsigned);
 extern uschar *bdat_getbuf(unsigned *);
+extern BOOL    bdat_hasc(void);
 extern int     bdat_ungetc(int);
 extern void    bdat_flush_data(void);
 
@@ -496,6 +498,7 @@ extern BOOL    smtp_get_port(uschar *, address_item *, int *, uschar *);
 extern int     smtp_getc(unsigned);
 extern uschar *smtp_getbuf(unsigned *);
 extern void    smtp_get_cache(unsigned);
+extern BOOL    smtp_hasc(void);
 extern int     smtp_handle_acl_fail(int, int, uschar *, uschar *);
 extern void    smtp_log_no_mail(void);
 extern void    smtp_message_code(uschar **, int *, uschar **, uschar **, BOOL);
@@ -525,6 +528,7 @@ extern int     spool_write_header(uschar *, int, uschar **);
 extern int     stdin_getc(unsigned);
 extern int     stdin_feof(void);
 extern int     stdin_ferror(void);
+extern BOOL    stdin_hasc(void);
 extern int     stdin_ungetc(int);
 
 extern void    store_exit(void);
index 1adb540c2fe121c1f9924b6d7290adfaac100f5f..200b506f7548c7b7d4bcb09cb3140c4c9c566ebd 100644 (file)
@@ -171,16 +171,19 @@ incoming TCP/IP. The defaults use stdin. We never need these for any
 stand-alone tests. */
 
 #if !defined(STAND_ALONE) && !defined(MACRO_PREDEF)
-int (*lwr_receive_getc)(unsigned) = stdin_getc;
+int    (*lwr_receive_getc)(unsigned)   = stdin_getc;
 uschar * (*lwr_receive_getbuf)(unsigned *) = NULL;
-int (*lwr_receive_ungetc)(int) = stdin_ungetc;
-int (*receive_getc)(unsigned)  = stdin_getc;
-uschar * (*receive_getbuf)(unsigned *)  = NULL;
-void (*receive_get_cache)(unsigned)    = NULL;
-int (*receive_ungetc)(int)     = stdin_ungetc;
-int (*receive_feof)(void)      = stdin_feof;
-int (*receive_ferror)(void)    = stdin_ferror;
-BOOL (*receive_smtp_buffered)(void) = NULL;   /* Only used for SMTP */
+int    (*lwr_receive_ungetc)(int)      = stdin_ungetc;
+BOOL   (*lwr_receive_hasc)(void)       = stdin_hasc;
+
+int    (*receive_getc)(unsigned)       = stdin_getc;
+uschar * (*receive_getbuf)(unsigned *)         = NULL;
+void   (*receive_get_cache)(unsigned)  = NULL;
+BOOL   (*receive_hasc)(void)           = stdin_hasc;
+int    (*receive_ungetc)(int)          = stdin_ungetc;
+int    (*receive_feof)(void)           = stdin_feof;
+int    (*receive_ferror)(void)         = stdin_ferror;
+BOOL   (*receive_smtp_buffered)(void)  = NULL;   /* Only used for SMTP */
 #endif
 
 
index bd88f74829ee33e0b4b741a6145731d950978641..ed264f0c149d79c1c62d33211d655678f858936a 100644 (file)
@@ -161,9 +161,12 @@ incoming TCP/IP. */
 
 extern int (*lwr_receive_getc)(unsigned);
 extern uschar * (*lwr_receive_getbuf)(unsigned *);
+extern BOOL (*lwr_receive_hasc)(void);
 extern int (*lwr_receive_ungetc)(int);
+
 extern int (*receive_getc)(unsigned);
 extern uschar * (*receive_getbuf)(unsigned *);
+extern BOOL (*receive_hasc)(void);
 extern void (*receive_get_cache)(unsigned);
 extern int (*receive_ungetc)(int);
 extern int (*receive_feof)(void);
index 3a9a91a81ef8620c6731a9851d346af57feef082..d2e556e32a18e54870c59cc34bcb81b96f543232 100644 (file)
@@ -44,42 +44,71 @@ receive_getc initially. They just call the standard functions, passing stdin as
 the file. (When SMTP input is occurring, different functions are used by
 changing the pointer variables.) */
 
-int
-stdin_getc(unsigned lim)
-{
-int c = getc(stdin);
+uschar stdin_buf[4096];
+uschar * stdin_inptr = stdin_buf;
+uschar * stdin_inend = stdin_buf;
 
-if (had_data_timeout)
-  {
-  fprintf(stderr, "exim: timed out while reading - message abandoned\n");
-  log_write(L_lost_incoming_connection,
-            LOG_MAIN, "timed out while reading local message");
-  receive_bomb_out(US"data-timeout", NULL);   /* Does not return */
-  }
-if (had_data_sigint)
+static BOOL
+stdin_refill(void)
+{
+size_t rc = fread(stdin_buf, 1, sizeof(stdin_buf), stdin);
+if (rc <= 0)
   {
-  if (filter_test == FTEST_NONE)
+  if (had_data_timeout)
     {
-    fprintf(stderr, "\nexim: %s received - message abandoned\n",
-      had_data_sigint == SIGTERM ? "SIGTERM" : "SIGINT");
-    log_write(0, LOG_MAIN, "%s received while reading local message",
-      had_data_sigint == SIGTERM ? "SIGTERM" : "SIGINT");
+    fprintf(stderr, "exim: timed out while reading - message abandoned\n");
+    log_write(L_lost_incoming_connection,
+             LOG_MAIN, "timed out while reading local message");
+    receive_bomb_out(US"data-timeout", NULL);   /* Does not return */
     }
-  receive_bomb_out(US"signal-exit", NULL);    /* Does not return */
+  if (had_data_sigint)
+    {
+    if (filter_test == FTEST_NONE)
+      {
+      fprintf(stderr, "\nexim: %s received - message abandoned\n",
+       had_data_sigint == SIGTERM ? "SIGTERM" : "SIGINT");
+      log_write(0, LOG_MAIN, "%s received while reading local message",
+       had_data_sigint == SIGTERM ? "SIGTERM" : "SIGINT");
+      }
+    receive_bomb_out(US"signal-exit", NULL);    /* Does not return */
+    }
+  return FALSE;
   }
-return c;
+stdin_inend = stdin_buf + rc;
+stdin_inptr = stdin_buf;
+return TRUE;
+}
+
+int
+stdin_getc(unsigned lim)
+{
+if (stdin_inptr >= stdin_inend)
+  if (!stdin_refill())
+      return EOF;
+return *stdin_inptr++;
+}
+
+
+BOOL
+stdin_hasc(void)
+{
+return stdin_inptr < stdin_inend;
 }
 
 int
 stdin_ungetc(int c)
 {
-return ungetc(c, stdin);
+if (stdin_inptr <= stdin_buf)
+  log_write(0, LOG_MAIN|LOG_PANIC_DIE, "buffer underflow in stdin_ungetc");
+
+*--stdin_inptr = c;
+return c;
 }
 
 int
 stdin_feof(void)
 {
-return feof(stdin);
+return stdin_hasc() ? FALSE : feof(stdin);
 }
 
 int
@@ -588,7 +617,7 @@ the file copy. */
 static void
 log_close_chk(void)
 {
-if (!receive_timeout)
+if (!receive_timeout && !receive_hasc())
   {
   struct timeval t;
   timesince(&t, &received_time);
@@ -654,11 +683,6 @@ if (!f.dot_ends)
   {
   int last_ch = '\n';
 
-/*XXX we do a gettimeofday before checking for every received char,
-which is hardly clever.  The function-indirection doesn't help, but
-an additional function to check for nonempty read buffer would help.
-See stdin_getc() / smtp_getc() / tls_getc() / bdat_getc(). */
-
   for ( ;
        log_close_chk(), (ch = (receive_getc)(GETC_BUFFER_UNLIMITED)) != EOF;
        last_ch = ch)
index 41232d7cd1b15d9887a7bf875a92c5c9bd32f8bc..c1aa5867cde8b4281179e5ab249835b656b40c13 100644 (file)
@@ -563,6 +563,12 @@ if (smtp_inptr >= smtp_inend)
 return *smtp_inptr++;
 }
 
+BOOL
+smtp_hasc(void)
+{
+return smtp_inptr < smtp_inend;
+}
+
 uschar *
 smtp_getbuf(unsigned * len)
 {
@@ -745,6 +751,14 @@ next_cmd:
   }
 }
 
+BOOL
+bdat_hasc(void)
+{
+if (chunking_data_left > 0)
+  return lwr_receive_hasc();
+return TRUE;
+}
+
 uschar *
 bdat_getbuf(unsigned * len)
 {
@@ -784,10 +798,11 @@ bdat_push_receive_functions(void)
 /* push the current receive_* function on the "stack", and
 replace them by bdat_getc(), which in turn will use the lwr_receive_*
 functions to do the dirty work. */
-if (lwr_receive_getc == NULL)
+if (!lwr_receive_getc)
   {
   lwr_receive_getc = receive_getc;
   lwr_receive_getbuf = receive_getbuf;
+  lwr_receive_hasc = receive_hasc;
   lwr_receive_ungetc = receive_ungetc;
   }
 else
@@ -797,23 +812,26 @@ else
 
 receive_getc = bdat_getc;
 receive_getbuf = bdat_getbuf;
+receive_hasc = bdat_hasc;
 receive_ungetc = bdat_ungetc;
 }
 
 static inline void
 bdat_pop_receive_functions(void)
 {
-if (lwr_receive_getc == NULL)
+if (!lwr_receive_getc)
   {
   DEBUG(D_receive) debug_printf("chunking double-pop receive functions\n");
   return;
   }
 receive_getc = lwr_receive_getc;
 receive_getbuf = lwr_receive_getbuf;
+receive_hasc = lwr_receive_hasc;
 receive_ungetc = lwr_receive_ungetc;
 
 lwr_receive_getc = NULL;
 lwr_receive_getbuf = NULL;
+lwr_receive_hasc = NULL;
 lwr_receive_ungetc = NULL;
 }
 
@@ -2576,12 +2594,14 @@ smtp_inbuffer[IN_BUFFER_SIZE-1] = '\0';
 receive_getc = smtp_getc;
 receive_getbuf = smtp_getbuf;
 receive_get_cache = smtp_get_cache;
+receive_hasc = smtp_hasc;
 receive_ungetc = smtp_ungetc;
 receive_feof = smtp_feof;
 receive_ferror = smtp_ferror;
 receive_smtp_buffered = smtp_buffered;
 lwr_receive_getc = NULL;
 lwr_receive_getbuf = NULL;
+lwr_receive_hasc = NULL;
 lwr_receive_ungetc = NULL;
 smtp_inptr = smtp_inend = smtp_inbuffer;
 smtp_had_eof = smtp_had_error = 0;
index 4626dc94556263d489b691cc4e162dff1fa100be..c6625c0600b9d02f85a0941940f924a2c473049c 100644 (file)
@@ -3138,6 +3138,7 @@ state->xfer_buffer = store_malloc(ssl_xfer_buffer_size);
 receive_getc = tls_getc;
 receive_getbuf = tls_getbuf;
 receive_get_cache = tls_get_cache;
+receive_hasc = tls_hasc;
 receive_ungetc = tls_ungetc;
 receive_feof = tls_feof;
 receive_ferror = tls_ferror;
@@ -3740,6 +3741,7 @@ if (!ct_ctx)      /* server */
   receive_getc =       smtp_getc;
   receive_getbuf =     smtp_getbuf;
   receive_get_cache =  smtp_get_cache;
+  receive_hasc =       smtp_hasc;
   receive_ungetc =     smtp_ungetc;
   receive_feof =       smtp_feof;
   receive_ferror =     smtp_ferror;
@@ -3854,6 +3856,13 @@ if (state->xfer_buffer_lwm >= state->xfer_buffer_hwm)
 return state->xfer_buffer[state->xfer_buffer_lwm++];
 }
 
+BOOL
+tls_hasc(void)
+{
+exim_gnutls_state_st * state = &state_server;
+return state->xfer_buffer_lwm < state->xfer_buffer_hwm;
+}
+
 uschar *
 tls_getbuf(unsigned * len)
 {
index bc454c8a136d3e71a5b4d9e0ac1a83944b2e7ab3..fddad9edc018b2a72f2729a1a0d58e6332784fb4 100644 (file)
@@ -3350,6 +3350,7 @@ ssl_xfer_eof = ssl_xfer_error = FALSE;
 receive_getc = tls_getc;
 receive_getbuf = tls_getbuf;
 receive_get_cache = tls_get_cache;
+receive_hasc = tls_hasc;
 receive_ungetc = tls_ungetc;
 receive_feof = tls_feof;
 receive_ferror = tls_ferror;
@@ -4126,6 +4127,12 @@ if (ssl_xfer_buffer_lwm >= ssl_xfer_buffer_hwm)
 return ssl_xfer_buffer[ssl_xfer_buffer_lwm++];
 }
 
+BOOL
+tls_hasc(void)
+{
+return ssl_xfer_buffer_lwm < ssl_xfer_buffer_hwm;
+}
+
 uschar *
 tls_getbuf(unsigned * len)
 {
@@ -4415,6 +4422,7 @@ if (!o_ctx)               /* server side */
   receive_getc =       smtp_getc;
   receive_getbuf =     smtp_getbuf;
   receive_get_cache =  smtp_get_cache;
+  receive_hasc =       smtp_hasc;
   receive_ungetc =     smtp_ungetc;
   receive_feof =       smtp_feof;
   receive_ferror =     smtp_ferror;
index 864257b46792e06b429bbb53c99b70d09e08bc94..83b7fb685a24b2f5417bffa2d901dea91750abd0 100644 (file)
@@ -646,6 +646,7 @@ if (text)
 
 if (ff)
   {
+debug_printf("%s %d: ff\n", __FUNCTION__, __LINE__);
   while (Ufgets(big_buffer, big_buffer_size, ff) != NULL)
     {
     if (file_expand)
@@ -669,12 +670,12 @@ limit if we are returning the body. */
 
 if (return_message)
   {
-  uschar *rubric = (tblock->headers_only)?
-    US"------ This is a copy of the message's header lines.\n"
-    : (tblock->body_only)?
-    US"------ This is a copy of the body of the message, without the headers.\n"
-    :
-    US"------ This is a copy of the message, including all the headers.\n";
+debug_printf("%s %d: ret msg\n", __FUNCTION__, __LINE__);
+  uschar *rubric = tblock->headers_only
+    ? US"------ This is a copy of the message's header lines.\n"
+    : tblock->body_only
+    ? US"------ This is a copy of the body of the message, without the headers.\n"
+    US"------ This is a copy of the message, including all the headers.\n";
   transport_ctx tctx = {
     .u = {.fd = fileno(fp)},
     .tblock = tblock,