build: use pkg-config for i18n
[exim.git] / src / src / log.c
index a01e9c59f20bda6698b73c30655c11354c520a9a..a7bc2aab7460c154302071d644f3ed856a54b37a 100644 (file)
@@ -2,7 +2,7 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) The Exim Maintainers 2020 - 2022 */
+/* Copyright (c) The Exim Maintainers 2020 - 2024 */
 /* 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 */
 /* 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 */
@@ -308,8 +308,8 @@ Send fd over socketpair.
 Return: true iff good.
 */
 
 Return: true iff good.
 */
 
-static BOOL
-log_send_fd(const int sock, const int fd)
+BOOL
+send_fd_over_socket(const int sock, const int fd)
 {
 struct msghdr msg;
 union {
 {
 struct msghdr msg;
 union {
@@ -343,8 +343,8 @@ return n == 1;
 Return fd passed over socketpair, or -1 on error.
 */
 
 Return fd passed over socketpair, or -1 on error.
 */
 
-static int
-log_recv_fd(const int sock)
+int
+recv_fd_from_sock(const int sock)
 {
 struct msghdr msg;
 union {
 {
 struct msghdr msg;
 union {
@@ -415,7 +415,7 @@ else if (euid == root_uid)
          || getgid() != exim_gid || getegid() != exim_gid
 
          || (fd = log_open_already_exim(name)) < 0
          || getgid() != exim_gid || getegid() != exim_gid
 
          || (fd = log_open_already_exim(name)) < 0
-         || !log_send_fd(sock[1], fd)
+         || !send_fd_over_socket(sock[1], fd)
         ) _exit(EXIT_FAILURE);
       (void)close(sock[1]);
       _exit(EXIT_SUCCESS);
         ) _exit(EXIT_FAILURE);
       (void)close(sock[1]);
       _exit(EXIT_SUCCESS);
@@ -424,7 +424,7 @@ else if (euid == root_uid)
     (void)close(sock[1]);
     if (pid > 0)
       {
     (void)close(sock[1]);
     if (pid > 0)
       {
-      fd = log_recv_fd(sock[0]);
+      fd = recv_fd_from_sock(sock[0]);
       while (waitpid(pid, NULL, 0) == -1 && errno == EINTR);
       }
     (void)close(sock[0]);
       while (waitpid(pid, NULL, 0) == -1 && errno == EINTR);
       }
     (void)close(sock[0]);
@@ -601,6 +601,22 @@ if (type == lt_debug) unlink(CS debuglog_name);
 
 
 
 
 
 
+/* Append to a gstring, in no-extend (or rebuffer) mode
+and without taint-checking.  Thanks to the no-extend, the
+gstring struct never needs reallocation; we ignore the
+infore that the initial space allocation for the string
+was exceeded, and just leave it potentially truncated. */
+
+static void
+string_fmt_append_noextend(gstring * g, char * fmt, ...)
+{
+va_list ap;
+va_start(ap, fmt);
+/* can return NULL if it truncates; just ignore */
+(void) string_vformat(g, SVFMT_TAINT_NOCHK, fmt, ap);
+va_end(ap);
+}
+
 /*************************************************
 *     Add configuration file info to log line    *
 *************************************************/
 /*************************************************
 *     Add configuration file info to log line    *
 *************************************************/
@@ -609,24 +625,40 @@ if (type == lt_debug) unlink(CS debuglog_name);
 once for real).
 
 Arguments:
 once for real).
 
 Arguments:
-  ptr         pointer to the end of the line we are building
-  flags       log flags
+  g            extensible-string referring to static buffer,
+               usable after return
+  flags                log flags
 
 
-Returns:      updated pointer
+Returns:      nothing
 */
 
 */
 
-static gstring *
+static void
 log_config_info(gstring * g, int flags)
 {
 log_config_info(gstring * g, int flags)
 {
-g = string_cat(g, US"Exim configuration error");
+string_fmt_append_noextend(g, "Exim configuration error");
 
 if (flags & (LOG_CONFIG_FOR & ~LOG_CONFIG))
 
 if (flags & (LOG_CONFIG_FOR & ~LOG_CONFIG))
-  return string_cat(g, US" for ");
+  string_fmt_append_noextend(g, " for ");
+else
+  {
+  if (flags & (LOG_CONFIG_IN & ~LOG_CONFIG))
+    if (config_lineno > 0)
+      {
+      if (acl_where != ACL_WHERE_UNKNOWN)
+       string_fmt_append_noextend(g, " in ACL verb at");
+      else
+       string_fmt_append_noextend(g, " in");
 
 
-if (flags & (LOG_CONFIG_IN & ~LOG_CONFIG))
-  g = string_fmt_append(g, " in line %d of %s", config_lineno, config_filename);
+      string_fmt_append_noextend(g, " line %d of %s",
+                                 config_lineno, config_filename);
+      }
+    else if (router_name)
+      string_fmt_append_noextend(g, " in %s router", router_name);
+    else if (transport_name)
+      string_fmt_append_noextend(g, " in %s transport", transport_name);
 
 
-return string_catn(g, US":\n  ", 4);
+  string_fmt_append_noextend(g, ":\n  ");
+  }
 }
 
 
 }
 
 
@@ -684,11 +716,11 @@ Returns:
   length actually written, persisting an errno from write()
 */
 ssize_t
   length actually written, persisting an errno from write()
 */
 ssize_t
-write_to_fd_buf(int fd, const uschar *buf, size_t length)
+write_to_fd_buf(int fd, const uschar * buf, size_t length)
 {
 ssize_t wrote;
 size_t total_written = 0;
 {
 ssize_t wrote;
 size_t total_written = 0;
-const uschar *p = buf;
+const uschar * p = buf;
 size_t left = length;
 
 while (1)
 size_t left = length;
 
 while (1)
@@ -711,6 +743,12 @@ while (1)
 return total_written;
 }
 
 return total_written;
 }
 
+static inline ssize_t
+write_gstring_to_fd_buf(int fd, const gstring * g)
+{
+return write_to_fd_buf(fd, g->s, gstring_length(g));
+}
+
 
 
 static void
 
 
 static void
@@ -806,8 +844,8 @@ log_write(unsigned int selector, int flags, const char *format, ...)
 {
 int paniclogfd;
 ssize_t written_len;
 {
 int paniclogfd;
 ssize_t written_len;
-gstring gs = { .size = LOG_BUFFER_SIZE-1, .ptr = 0, .s = log_buffer };
-gstring * g;
+gstring gs = { .size = LOG_BUFFER_SIZE-2, .ptr = 0, .s = log_buffer };
+gstring * g = &gs;
 va_list ap;
 
 /* If panic_recurseflag is set, we have failed to open the panic log. This is
 va_list ap;
 
 /* If panic_recurseflag is set, we have failed to open the panic log. This is
@@ -817,7 +855,7 @@ original log line that caused the problem. Afterwards, expire. */
 
 if (panic_recurseflag)
   {
 
 if (panic_recurseflag)
   {
-  uschar *extra = panic_save_buffer ? panic_save_buffer : US"";
+  uschar * extra = panic_save_buffer ? panic_save_buffer : US"";
   if (debug_file) debug_printf("%s%s", extra, log_buffer);
   if (log_stderr && log_stderr != debug_file)
     fprintf(log_stderr, "%s%s", extra, log_buffer);
   if (debug_file) debug_printf("%s%s", extra, log_buffer);
   if (log_stderr && log_stderr != debug_file)
     fprintf(log_stderr, "%s%s", extra, log_buffer);
@@ -918,47 +956,43 @@ in one go so that it doesn't get split when multi-processing. */
 
 DEBUG(D_any|D_v)
   {
 
 DEBUG(D_any|D_v)
   {
-  int i;
-
-  g = string_catn(&gs, US"LOG:", 4);
+  string_fmt_append_noextend(g, "LOG:");
 
   /* Show the selector that was passed into the call. */
 
 
   /* Show the selector that was passed into the call. */
 
-  for (i = 0; i < log_options_count; i++)
+  for (int i = 0; i < log_options_count; i++)
     {
     unsigned int bitnum = log_options[i].bit;
     if (bitnum < BITWORDSIZE && selector == BIT(bitnum))
     {
     unsigned int bitnum = log_options[i].bit;
     if (bitnum < BITWORDSIZE && selector == BIT(bitnum))
-      g = string_fmt_append(g, " %s", log_options[i].name);
+      string_fmt_append_noextend(g, " %s", log_options[i].name);
     }
 
     }
 
-  g = string_fmt_append(g, "%s%s%s%s\n  ",
+  string_fmt_append_noextend(g, "%s%s%s%s\n  ",
     flags & LOG_MAIN ?    " MAIN"   : "",
     flags & LOG_PANIC ?   " PANIC"  : "",
     (flags & LOG_PANIC_DIE) == LOG_PANIC_DIE ? " DIE" : "",
     flags & LOG_REJECT ?  " REJECT" : "");
 
     flags & LOG_MAIN ?    " MAIN"   : "",
     flags & LOG_PANIC ?   " PANIC"  : "",
     (flags & LOG_PANIC_DIE) == LOG_PANIC_DIE ? " DIE" : "",
     flags & LOG_REJECT ?  " REJECT" : "");
 
-  if (flags & LOG_CONFIG) g = log_config_info(g, flags);
+  if (flags & LOG_CONFIG) log_config_info(g, flags);
 
   /* We want to be able to log tainted info, but log_buffer is directly
   malloc'd.  So use deliberately taint-nonchecking routines to build into
   it, trusting that we will never expand the results. */
 
   va_start(ap, format);
 
   /* We want to be able to log tainted info, but log_buffer is directly
   malloc'd.  So use deliberately taint-nonchecking routines to build into
   it, trusting that we will never expand the results. */
 
   va_start(ap, format);
-  i = g->ptr;
   if (!string_vformat(g, SVFMT_TAINT_NOCHK, format, ap))
     {
   if (!string_vformat(g, SVFMT_TAINT_NOCHK, format, ap))
     {
-    g->ptr = i;
-    g = string_cat(g, US"**** log string overflowed log buffer ****");
+    uschar * s = US"**** log string overflowed log buffer ****";
+    gstring_trim(g, Ustrlen(s));
+    string_fmt_append_noextend(g, "%s", s);
     }
   va_end(ap);
 
     }
   va_end(ap);
 
-  g->size = LOG_BUFFER_SIZE;
-  g = string_catn(g, US"\n", 1);
-  debug_printf("%s", string_from_gstring(g));
+  debug_printf("%Y\n", g);
+
+  /* Having used the buffer for debug output, reset it for the real use. */
 
 
-  gs.size = LOG_BUFFER_SIZE-1; /* Having used the buffer for debug output, */
-  gs.ptr = 0;                  /* reset it for the real use. */
-  gs.s = log_buffer;
+  gstring_reset(g);
   }
 /* If no log file is specified, we are in a mess. */
 
   }
 /* If no log file is specified, we are in a mess. */
 
@@ -982,33 +1016,32 @@ if (!write_rejectlog) flags &= ~LOG_REJECT;
 /* Create the main message in the log buffer. Do not include the message id
 when called by a utility. */
 
 /* Create the main message in the log buffer. Do not include the message id
 when called by a utility. */
 
-g = string_fmt_append(&gs, "%s ", tod_stamp(tod_log));
+string_fmt_append_noextend(&gs, "%s ", tod_stamp(tod_log));
 
 if (LOGGING(pid))
   {
 
 if (LOGGING(pid))
   {
-  if (!syslog_pid) pid_position[0] = g->ptr;           /* remember begin … */
-  g = string_fmt_append(g, "[%d] ", (int)getpid());
-  if (!syslog_pid) pid_position[1] = g->ptr;           /*  … and end+1 of the PID */
+  if (!syslog_pid) pid_position[0] = gstring_length(g);        /* remember begin … */
+  string_fmt_append_noextend(g, "[%ld] ", (long)getpid());
+  if (!syslog_pid) pid_position[1] = gstring_length(g);        /*  … and end+1 of the PID */
   }
 
   }
 
-if (f.really_exim && message_id[0] != 0)
-  g = string_fmt_append(g, "%s ", message_id);
+if (f.really_exim && message_id[0])
+  string_fmt_append_noextend(g, "%s ", message_id);
 
 if (flags & LOG_CONFIG)
 
 if (flags & LOG_CONFIG)
-  g = log_config_info(g, flags);
+  log_config_info(g, flags);
 
 va_start(ap, format);
   {
 
 va_start(ap, format);
   {
-  int i = g->ptr;
-
   /* We want to be able to log tainted info, but log_buffer is directly
   malloc'd.  So use deliberately taint-nonchecking routines to build into
   it, trusting that we will never expand the results. */
 
   if (!string_vformat(g, SVFMT_TAINT_NOCHK, format, ap))
     {
   /* We want to be able to log tainted info, but log_buffer is directly
   malloc'd.  So use deliberately taint-nonchecking routines to build into
   it, trusting that we will never expand the results. */
 
   if (!string_vformat(g, SVFMT_TAINT_NOCHK, format, ap))
     {
-    g->ptr = i;
-    g = string_cat(g, US"**** log string overflowed log buffer ****\n");
+    uschar * s = US"**** log string overflowed log buffer ****";
+    gstring_trim(g, Ustrlen(s));
+    string_fmt_append_noextend(g, "%s", s);
     }
   }
 va_end(ap);
     }
   }
 va_end(ap);
@@ -1016,29 +1049,29 @@ va_end(ap);
 /* Add the raw, unrewritten, sender to the message if required. This is done
 this way because it kind of fits with LOG_RECIPIENTS. */
 
 /* Add the raw, unrewritten, sender to the message if required. This is done
 this way because it kind of fits with LOG_RECIPIENTS. */
 
-if (   flags & LOG_SENDER
-   && g->ptr < LOG_BUFFER_SIZE - 10 - Ustrlen(raw_sender))
-  g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, " from <%s>", raw_sender);
+if (flags & LOG_SENDER)
+  string_fmt_append_noextend(g, " from <%s>", raw_sender);
 
 /* Add list of recipients to the message if required; the raw list,
 before rewriting, was saved in raw_recipients. There may be none, if an ACL
 discarded them all. */
 
 
 /* Add list of recipients to the message if required; the raw list,
 before rewriting, was saved in raw_recipients. There may be none, if an ACL
 discarded them all. */
 
-if (  flags & LOG_RECIPIENTS
-   && g->ptr < LOG_BUFFER_SIZE - 6
-   && raw_recipients_count > 0)
+if (flags & LOG_RECIPIENTS && raw_recipients_count > 0)
   {
   {
-  int i;
-  g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, " for", NULL);
-  for (i = 0; i < raw_recipients_count; i++)
-    {
-    uschar * s = raw_recipients[i];
-    if (LOG_BUFFER_SIZE - g->ptr < Ustrlen(s) + 3) break;
-    g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, " %s", s);
+  string_fmt_append_noextend(g, " for");
+  for (int i = 0; i < raw_recipients_count; i++)
+    if (!string_fmt_append_f(g, SVFMT_TAINT_NOCHK, " %s", raw_recipients[i]))
+      {
+    uschar * s = US"<trunc>";
+    gstring_trim(g, Ustrlen(s));
+    string_fmt_append_noextend(g, "%s", s);
+    break;
     }
   }
 
     }
   }
 
-g = string_catn(g, US"\n", 1);
+/* actual size, now we are placing the newline (and space for NUL) */
+gs.size = LOG_BUFFER_SIZE;
+string_fmt_append_noextend(g, "\n");
 string_from_gstring(g);
 
 /* Handle loggable errors when running a utility, or when address testing.
 string_from_gstring(g);
 
 /* Handle loggable errors when running a utility, or when address testing.
@@ -1112,10 +1145,10 @@ if (  flags & LOG_MAIN
 
     /* Failing to write to the log is disastrous */
 
 
     /* Failing to write to the log is disastrous */
 
-    written_len = write_to_fd_buf(mainlogfd, g->s, g->ptr);
-    if (written_len != g->ptr)
+    if (  (written_len = write_gstring_to_fd_buf(mainlogfd, g))
+       != gstring_length(g))
       {
       {
-      log_write_failed(US"main log", g->ptr, written_len);
+      log_write_failed(US"main log", gstring_length(g), written_len);
       /* That function does not return */
       }
     }
       /* That function does not return */
       }
     }
@@ -1130,53 +1163,39 @@ if (flags & LOG_REJECT)
   {
   if (header_list && LOGGING(rejected_header))
     {
   {
   if (header_list && LOGGING(rejected_header))
     {
-    gstring * g2;
     int i;
 
     if (recipients_count > 0)
       {
       /* List the sender */
 
     int i;
 
     if (recipients_count > 0)
       {
       /* List the sender */
 
-      g2 = string_fmt_append_f(g, SVFMT_TAINT_NOCHK,
+      string_fmt_append_noextend(g,
                        "Envelope-from: <%s>\n", sender_address);
                        "Envelope-from: <%s>\n", sender_address);
-      if (g2) g = g2;
 
       /* List up to 5 recipients */
 
 
       /* List up to 5 recipients */
 
-      g2 = string_fmt_append_f(g, SVFMT_TAINT_NOCHK,
+      string_fmt_append_noextend(g,
                        "Envelope-to: <%s>\n", recipients_list[0].address);
                        "Envelope-to: <%s>\n", recipients_list[0].address);
-      if (g2) g = g2;
 
       for (i = 1; i < recipients_count && i < 5; i++)
 
       for (i = 1; i < recipients_count && i < 5; i++)
-        {
-        g2 = string_fmt_append_f(g, SVFMT_TAINT_NOCHK,
+        string_fmt_append_noextend(g,
                        "    <%s>\n", recipients_list[i].address);
                        "    <%s>\n", recipients_list[i].address);
-       if (g2) g = g2;
-        }
 
       if (i < recipients_count)
 
       if (i < recipients_count)
-        {
-        g2 = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, "    ...\n", NULL);
-       if (g2) g = g2;
-        }
+        string_fmt_append_noextend(g, "    ...\n", NULL);
       }
 
       }
 
-    /* A header with a NULL text is an unfilled in Received: header */
+    /* A header with a NULL text is an unfilled-in Received: header */
 
     for (header_line * h = header_list; h; h = h->next) if (h->text)
 
     for (header_line * h = header_list; h; h = h->next) if (h->text)
-      {
-      g2 = string_fmt_append_f(g, SVFMT_TAINT_NOCHK,
-                       "%c %s", h->type, h->text);
-      if (g2)
-       g = g2;
-      else             /* Buffer is full; truncate */
-        {
-        g->ptr -= 100;        /* For message and separator */
-        if (g->s[g->ptr-1] == '\n') g->ptr--;
-        g = string_cat(g, US"\n*** truncated ***\n");
+      if (!string_fmt_append_f(g, SVFMT_TAINT_NOCHK,
+                       "%c %s", h->type, h->text))
+        {                              /* Buffer is full; truncate */
+       gstring_trim(g, 100);           /* space for message and separator */
+       gstring_trim_trailing(g, '\n');
+        string_fmt_append_noextend(g, "\n*** truncated ***\n");
         break;
         }
         break;
         }
-      }
     }
 
   /* Write to syslog or to a log file */
     }
 
   /* Write to syslog or to a log file */
@@ -1227,8 +1246,8 @@ if (flags & LOG_REJECT)
       if (fstat(rejectlogfd, &statbuf) >= 0) rejectlog_inode = statbuf.st_ino;
       }
 
       if (fstat(rejectlogfd, &statbuf) >= 0) rejectlog_inode = statbuf.st_ino;
       }
 
-    written_len = write_to_fd_buf(rejectlogfd, g->s, g->ptr);
-    if (written_len != g->ptr)
+    if (  (written_len = write_gstring_to_fd_buf(rejectlogfd, g))
+       != gstring_length(g))
       {
       log_write_failed(US"reject log", g->ptr, written_len);
       /* That function does not return */
       {
       log_write_failed(US"reject log", g->ptr, written_len);
       /* That function does not return */
@@ -1262,8 +1281,8 @@ if (flags & LOG_PANIC)
     if (panic_save_buffer)
       (void) write(paniclogfd, panic_save_buffer, Ustrlen(panic_save_buffer));
 
     if (panic_save_buffer)
       (void) write(paniclogfd, panic_save_buffer, Ustrlen(panic_save_buffer));
 
-    written_len = write_to_fd_buf(paniclogfd, g->s, g->ptr);
-    if (written_len != g->ptr)
+    if (  (written_len = write_gstring_to_fd_buf(paniclogfd, g))
+       != gstring_length(g))
       {
       int save_errno = errno;
       write_syslog(LOG_CRIT, log_buffer);
       {
       int save_errno = errno;
       write_syslog(LOG_CRIT, log_buffer);
@@ -1460,7 +1479,7 @@ if (Ustrcmp(which, "debug") == 0)
     return;
     }
   fprintf(stderr, "exim: %s\n", errmsg);
     return;
     }
   fprintf(stderr, "exim: %s\n", errmsg);
-  exit(EXIT_FAILURE);
+  exim_exit(EXIT_FAILURE);
   }
 else log_write(0, LOG_CONFIG|LOG_PANIC_DIE, "%s", errmsg);
 }
   }
 else log_write(0, LOG_CONFIG|LOG_PANIC_DIE, "%s", errmsg);
 }
@@ -1557,3 +1576,5 @@ if (kill) unlink_log(lt_debug);
 
 
 /* End of log.c */
 
 
 /* End of log.c */
+/* vi: sw ai sw=2
+*/