OpenSSL: track shutdown calls. Bug 2864
[exim.git] / src / src / log.c
index 4cb161ac0fbdf5024a2de1a77589981c0f0d834f..6126b205858dadf0eb45c6aa421a60c52034e2be 100644 (file)
@@ -3,7 +3,7 @@
 *************************************************/
 
 /* Copyright (c) University of Cambridge 1995 - 2018 */
-/* Copyright (c) The Exim Maintainers 2020 */
+/* Copyright (c) The Exim Maintainers 2020 - 2021 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* Functions for writing log files. The code for maintaining datestamped
@@ -455,7 +455,7 @@ return fd;
 it does not exist. This may be called recursively on failure, in order to open
 the panic log.
 
-The directory is in the static variable file_path. This is static so that it
+The directory is in the static variable file_path. This is static so that
 the work of sorting out the path is done just once per Exim process.
 
 Exim is normally configured to avoid running as root wherever possible, the log
@@ -497,7 +497,6 @@ switch (type)
     it gets statted to see if it has been cycled. With a datestamp, the datestamp
     will be compared. The static slot for saving it is the same size as buffer,
     and the text has been checked above to fit, so this use of strcpy() is OK. */
-
     Ustrcpy(mainlog_name, buffer);
     if (string_datestamp_offset > 0)
       mainlog_datestamp = mainlog_name + string_datestamp_offset;
@@ -505,7 +504,6 @@ switch (type)
 
   case lt_reject:
     /* Ditto for the reject log */
-
     Ustrcpy(rejectlog_name, buffer);
     if (string_datestamp_offset > 0)
       rejectlog_datestamp = rejectlog_name + string_datestamp_offset;
@@ -514,10 +512,13 @@ switch (type)
   case lt_debug:
     /* and deal with the debug log (which keeps the datestamp, but does not
     update it) */
-
     Ustrcpy(debuglog_name, buffer);
     if (tag)
       {
+      if (is_tainted(tag))
+       die(US"exim: tainted tag for debug log filename",
+             US"Logging failure; please try later");
+
       /* this won't change the offset of the datestamp */
       ok2 = string_format(buffer, sizeof(buffer), "%s%s",
         debuglog_name, tag);
@@ -530,7 +531,6 @@ switch (type)
     /* Remove any datestamp if this is the panic log. This is rare, so there's no
   need to optimize getting the datestamp length. We remove one non-alphanumeric
   char afterwards if at the start, otherwise one before. */
-
     if (string_datestamp_offset >= 0)
       {
       uschar * from = buffer + string_datestamp_offset;
@@ -558,9 +558,7 @@ if (!ok)
 
 /* We now have the file name. After a successful open, return. */
 
-*fd = log_open_as_exim(buffer);
-
-if (*fd >= 0)
+if ((*fd = log_open_as_exim(buffer)) >= 0)
   return;
 
 euid = geteuid();
@@ -713,62 +711,31 @@ return total_written;
 }
 
 
-/* Pull the file out of the configured or the compiled-in list.
-Called for an empty log_file_path element, for debug logging activation
-when file_path has not previously been set, and from the appenfile transport setup. */
 
-void
-set_file_path(BOOL *multiple)
+static void
+set_file_path(void)
 {
-uschar *s;
 int sep = ':';              /* Fixed separator - outside use */
-const uschar *ss = *log_file_path ? log_file_path : US LOG_FILE_PATH;
-
-if (*ss)
-  for (logging_mode = 0;
-       s = string_nextinlist(&ss, &sep, log_buffer, LOG_BUFFER_SIZE); )
-    {
-    if (Ustrcmp(s, "syslog") == 0)
-      logging_mode |= LOG_MODE_SYSLOG;
-    else if (!(logging_mode & LOG_MODE_FILE))  /* no file yet */
-      {
-      /* If a non-empty path is given, use it */
-
-      if (*s)
-       file_path = string_copy(s);
-
-      /* If handling the config option, and the element is empty, we want to use
-      the first non-empty, non-syslog item in LOG_FILE_PATH, if there is one,
-      since the value of log_file_path may have been set at runtime. If there is
-      no such item, use the ultimate default in the spool directory. */
-
-      else if (*log_file_path && LOG_FILE_PATH[0])
-       {
-       ss = US LOG_FILE_PATH;
-       continue;
-       }
-
-      logging_mode |= LOG_MODE_FILE;
-      }
-    else
-      if (multiple) *multiple = TRUE;
-    }
-  else
-    logging_mode = LOG_MODE_FILE;
-
-/* Set up the ultimate default if necessary. */
-
-if (logging_mode & LOG_MODE_FILE  &&  !*file_path)
-  file_path = string_sprintf("%s/log/%%slog", spool_directory);
+uschar *t;
+const uschar *tt = US LOG_FILE_PATH;
+while ((t = string_nextinlist(&tt, &sep, log_buffer, LOG_BUFFER_SIZE)))
+  {
+  if (Ustrcmp(t, "syslog") == 0 || t[0] == 0) continue;
+  file_path = string_copy(t);
+  break;
+  }
 }
 
 
+/* Close mainlog, unless we do not see a chance to open the file mainlog later
+again.  This will happen if we log from a transport process (which has dropped
+privs); something we traditionally avoid, but the introduction of taint-tracking
+and resulting detection of errors is makinng harder. */
+
 void
 mainlog_close(void)
 {
-/* avoid closing it if it is closed already or if we do not see a chance
-to open the file mainlog later again */
-if (mainlogfd < 0 /* already closed */
+if (mainlogfd < 0
    || !(geteuid() == 0 || geteuid() == exim_uid))
   return;
 (void)close(mainlogfd);
@@ -885,7 +852,38 @@ if (!path_inspected)
   /* If nothing has been set, don't waste effort... the default values for the
   statics are file_path="" and logging_mode = LOG_MODE_FILE. */
 
-  if (*log_file_path) set_file_path(&multiple);
+  if (*log_file_path)
+    {
+    int sep = ':';              /* Fixed separator - outside use */
+    uschar *s;
+    const uschar *ss = log_file_path;
+
+    logging_mode = 0;
+    while ((s = string_nextinlist(&ss, &sep, log_buffer, LOG_BUFFER_SIZE)))
+      {
+      if (Ustrcmp(s, "syslog") == 0)
+        logging_mode |= LOG_MODE_SYSLOG;
+      else if (logging_mode & LOG_MODE_FILE)
+       multiple = TRUE;
+      else
+        {
+        logging_mode |= LOG_MODE_FILE;
+
+        /* If a non-empty path is given, use it */
+
+        if (*s)
+          file_path = string_copy(s);
+
+        /* If the path is empty, we want to use the first non-empty, non-
+        syslog item in LOG_FILE_PATH, if there is one, since the value of
+        log_file_path may have been set at runtime. If there is no such item,
+        use the ultimate default in the spool directory. */
+
+        else
+         set_file_path();  /* Empty item in log_file_path */
+        }    /* First non-syslog item in log_file_path */
+      }      /* Scan of log_file_path */
+    }
 
   /* If no modes have been selected, it is a major disaster */
 
@@ -893,8 +891,11 @@ if (!path_inspected)
     die(US"Neither syslog nor file logging set in log_file_path",
         US"Unexpected logging failure");
 
-  /* Revert to the old store pool, and record that we've sorted out the path. */
+  /* Set up the ultimate default if necessary. Then revert to the old store
+  pool, and record that we've sorted out the path. */
 
+  if (logging_mode & LOG_MODE_FILE  &&  !file_path[0])
+    file_path = string_sprintf("%s/log/%%slog", spool_directory);
   store_pool = old_pool;
   path_inspected = TRUE;
 
@@ -906,6 +907,11 @@ if (!path_inspected)
       "More than one path given in log_file_path: using %s", file_path);
   }
 
+/* Optionally trigger debug */
+
+if (flags & LOG_PANIC && dtrigger_selector & BIT(DTi_panictrigger))
+  debug_trigger_fire();
+
 /* If debugging, show all log entries, but don't show headers. Do it all
 in one go so that it doesn't get split when multi-processing. */
 
@@ -1248,7 +1254,6 @@ if (flags & LOG_PANIC)
 
   if (logging_mode & LOG_MODE_FILE)
     {
-    if (!*file_path) set_file_path(NULL);
     panic_recurseflag = TRUE;
     open_log(&paniclogfd, lt_panic, NULL);  /* Won't return on failure */
     panic_recurseflag = FALSE;
@@ -1471,13 +1476,15 @@ misconfiguration.
 
 The first use of this is in ACL logic, "control = debug/tag=foo/opts=+expand"
 which can be combined with conditions, etc, to activate extra logging only
-for certain sources. The second use is inetd wait mode debug preservation. */
+for certain sources. The second use is inetd wait mode debug preservation.
+
+It might be nice, in ACL-initiated pretrigger mode, to not create the file
+immediately but only upon a trigger - but we'd need another cmdline option
+to pass the name through child_exxec_exim(). */
 
 void
 debug_logging_activate(uschar *tag_name, uschar *opts)
 {
-int fd = -1;
-
 if (debug_file)
   {
   debug_printf("DEBUGGING ACTIVATED FROM WITHIN CONFIG.\n"
@@ -1485,7 +1492,7 @@ if (debug_file)
   return;
   }
 
-if (tag_name != NULL && (Ustrchr(tag_name, '/') != NULL))
+if (tag_name && (Ustrchr(tag_name, '/') != NULL))
   {
   log_write(0, LOG_MAIN|LOG_PANIC, "debug tag may not contain a '/' in: %s",
       tag_name);
@@ -1501,36 +1508,29 @@ if (opts)
 resulting in certain setup not having been done.  Hack this for now so we
 do not segfault; note that nondefault log locations will not work */
 
-if (!*file_path) set_file_path(NULL);
+if (!*file_path) set_file_path();
 
-open_log(&fd, lt_debug, tag_name);
+open_log(&debug_fd, lt_debug, tag_name);
 
-if (fd != -1)
-  debug_file = fdopen(fd, "w");
+if (debug_fd != -1)
+  debug_file = fdopen(debug_fd, "w");
 else
   log_write(0, LOG_MAIN|LOG_PANIC, "unable to open debug log");
 }
 
 
 void
-debug_logging_stop(void)
+debug_logging_stop(BOOL kill)
 {
+debug_pretrigger_discard();
 if (!debug_file || !debuglog_name[0]) return;
 
 debug_selector = 0;
 fclose(debug_file);
 debug_file = NULL;
-unlink_log(lt_debug);
+debug_fd = -1;
+if (kill) unlink_log(lt_debug);
 }
 
-/* Called from the appendfile transport setup. */
-void
-open_logs(void)
-{
-set_file_path(NULL);
-if (!(logging_mode & LOG_MODE_FILE)) return;
-open_log(&mainlogfd, lt_main, 0);
-open_log(&rejectlogfd, lt_reject, 0);
-}
 
 /* End of log.c */