SPDX: Mass-update to GPL-2.0-or-later
[exim.git] / src / src / debug.c
index 90c48dde407e2ebf3b8abe5a12585ee11a7fd08d..44ad763e147b25450b260f024965c2df465ed561 100644 (file)
@@ -2,8 +2,10 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
+/* Copyright (c) The Exim Maintainers 2015 - 2022 */
 /* Copyright (c) University of Cambridge 1995 - 2018 */
 /* See the file NOTICE for conditions of use and distribution. */
+/* SPDX-License-Identifier: GPL-2.0-or-later */
 
 
 #include "exim.h"
@@ -12,6 +14,8 @@ static uschar  debug_buffer[2048];
 static uschar *debug_ptr = debug_buffer;
 static int     debug_prefix_length = 0;
 
+static unsigned pretrigger_writeoff;
+static unsigned pretrigger_readoff;
 
 
 const uschar * rc_names[] = {          /* Mostly for debug output */
@@ -31,6 +35,7 @@ const uschar * rc_names[] = {         /* Mostly for debug output */
   [CANCELLED] =                US"CANCELLED",
   [FAIL_SEND] =                US"FAIL_SEND",
   [FAIL_DROP] =                US"FAIL_DROP",
+  [DANE] =             US"DANE",
 };
 
 const uschar * dns_rc_names[] = {
@@ -316,8 +321,41 @@ if (debug_ptr[-1] == '\n')
       }
     }
 
-  fprintf(debug_file, "%s", CS debug_buffer);
-  fflush(debug_file);
+  if (debug_pretrigger_buf)
+    {
+    int needed = Ustrlen(debug_buffer)+1, avail;
+    char c;
+
+    if (needed > debug_pretrigger_bsize)
+      needed = debug_pretrigger_bsize;
+    if ((avail = pretrigger_readoff - pretrigger_writeoff) <= 0)
+      avail += debug_pretrigger_bsize;
+
+    /* We have a pretrigger set up, trigger not yet hit. Copy the line(s) to the
+    pretrig buffer, dropping earlier lines if needed but truncating this line if
+    the pbuf is maxed out.  In the PTB the lines are NOT nul-terminated. */
+
+    while (avail < needed)
+      do
+       {
+       avail++;
+        c = debug_pretrigger_buf[pretrigger_readoff];
+       if (++pretrigger_readoff >= debug_pretrigger_bsize) pretrigger_readoff = 0;
+       }
+      while (c && c != '\n' && pretrigger_readoff != pretrigger_writeoff);
+
+    needed--;
+    for (int i = 0; needed; i++, needed--)
+      {
+      debug_pretrigger_buf[pretrigger_writeoff] = debug_buffer[i];
+      if (++pretrigger_writeoff >= debug_pretrigger_bsize) pretrigger_writeoff = 0;
+      }
+    }
+  else
+    {
+    fprintf(debug_file, "%s", CS debug_buffer);
+    fflush(debug_file);
+    }
   debug_ptr = debug_buffer;
   debug_prefix_length = 0;
   }
@@ -351,7 +389,7 @@ if (fstat(fd, &s) == 0 && (s.st_mode & S_IFMT) == S_IFSOCK)
        g = string_fmt_append(g, " lcl [%s]:%u",
          inet_ntoa(sinp->sin_addr), ntohs(sinp->sin_port));
        alen = sizeof(*sinp);
-       if (getpeername(fd, sinp, &alen) == 0)
+       if (getpeername(fd, (struct sockaddr *)sinp, &alen) == 0)
          g = string_fmt_append(g, " rmt [%s]:%u",
            inet_ntoa(sinp->sin_addr), ntohs(sinp->sin_port));
        break;
@@ -363,7 +401,7 @@ if (fstat(fd, &s) == 0 && (s.st_mode & S_IFMT) == S_IFSOCK)
          inet_ntop(AF_INET6, &sin6p->sin6_addr, CS buf, sizeof(buf)),
          ntohs(sin6p->sin6_port));
        alen = sizeof(*sin6p);
-       if (getpeername(fd, sin6p, &alen) == 0)
+       if (getpeername(fd, (struct sockaddr *)sin6p, &alen) == 0)
          g = string_fmt_append(g, " rmt [%s]:%u",
            inet_ntop(AF_INET6, &sin6p->sin6_addr, CS buf, sizeof(buf)),
            ntohs(sin6p->sin6_port));
@@ -376,7 +414,7 @@ if (fstat(fd, &s) == 0 && (s.st_mode & S_IFMT) == S_IFSOCK)
             sunp->sun_path[0] ? US"" : US"@",
             sunp->sun_path[0] ? sunp->sun_path : sunp->sun_path+1);
         alen = sizeof(*sunp);
-        if (getpeername(fd, sunp, &alen) == 0)
+        if (getpeername(fd, (struct sockaddr *)sunp, &alen) == 0)
           g = string_fmt_append(g, " rmt %s%s",
             sunp->sun_path[0] ? US"" : US"@",
             sunp->sun_path[0] ? sunp->sun_path : sunp->sun_path+1);
@@ -408,4 +446,54 @@ else
 }
 
 
+/**************************************************************/
+/* Pretrigger handling for debug.  The debug_printf implementation
+diverts output to a circular buffer if the buffer is set up.
+The routines here set up the buffer, and unload it to file (and release it).
+What ends up in the buffer is subject to the usual debug_selector. */
+
+void
+debug_pretrigger_setup(const uschar * size_string)
+{
+long size = Ustrtol(size_string, NULL, 0);
+if (size > 0)
+  {
+  unsigned bufsize = MIN(size, 16384);
+
+  dtrigger_selector |= BIT(DTi_pretrigger);
+  if (debug_pretrigger_buf) store_free(debug_pretrigger_buf);
+  debug_pretrigger_buf = store_malloc((size_t)(debug_pretrigger_bsize = bufsize));
+  pretrigger_readoff = pretrigger_writeoff = 0;
+  }
+}
+
+void
+debug_trigger_fire(void)
+{
+int nbytes;
+
+if (!debug_pretrigger_buf) return;
+
+if (debug_file && (nbytes = pretrigger_writeoff - pretrigger_readoff) != 0)
+  if (nbytes > 0)
+    fwrite(debug_pretrigger_buf + pretrigger_readoff, 1, nbytes, debug_file);
+  else
+    {
+    fwrite(debug_pretrigger_buf + pretrigger_readoff, 1,
+      debug_pretrigger_bsize - pretrigger_readoff, debug_file);
+    fwrite(debug_pretrigger_buf, 1, pretrigger_writeoff, debug_file);
+    }
+
+debug_pretrigger_discard();
+}
+
+void
+debug_pretrigger_discard(void)
+{
+if (debug_pretrigger_buf) store_free(debug_pretrigger_buf);
+debug_pretrigger_buf = NULL;
+dtrigger_selector = 0;
+}
+
+
 /* End of debug.c */