Implement "control = debug" ACL control. Fixes: #937
authorPhil Pennock <pdp@exim.org>
Sun, 6 Jun 2010 00:25:46 +0000 (00:25 +0000)
committerPhil Pennock <pdp@exim.org>
Sun, 6 Jun 2010 00:25:46 +0000 (00:25 +0000)
doc/doc-docbook/spec.xfpt
doc/doc-txt/ChangeLog
doc/doc-txt/NewStuff
src/src/acl.c
src/src/exim.c
src/src/functions.h
src/src/log.c
src/src/macros.h

index 003ae6db83ebdbf5dda66ab5bc477de32aa6dc6f..02020dc504d9ce22686380fb11a5e9789e67705e 100644 (file)
@@ -1,4 +1,4 @@
-. $Cambridge: exim/doc/doc-docbook/spec.xfpt,v 1.79 2010/06/05 23:50:18 pdp Exp $
+. $Cambridge: exim/doc/doc-docbook/spec.xfpt,v 1.80 2010/06/06 00:25:46 pdp Exp $
 .
 . /////////////////////////////////////////////////////////////////////////////
 . This is the primary source of the Exim Manual. It is an xfpt document that is
 .
 . /////////////////////////////////////////////////////////////////////////////
 . This is the primary source of the Exim Manual. It is an xfpt document that is
@@ -25772,6 +25772,23 @@ warn  control = caseful_local_part
 Notice that we put back the lower cased version afterwards, assuming that
 is what is wanted for subsequent tests.
 
 Notice that we put back the lower cased version afterwards, assuming that
 is what is wanted for subsequent tests.
 
+.vitem &*control&~=&~debug/*&<&'options'&>
+.cindex "&ACL;" "enabling debug logging"
+.cindex "debugging" "enabling from an ACL"
+This control turns on debug logging, almost as though Exim had been invoked
+with &`-d`&, with the output going to a new logfile, by default called
+&'debuglog'&.  The filename can be adjusted with the &'tag'& option, which
+may access any variables already defined.  The logging may be adjusted with
+the &'opts'& option, which takes the same values as the &`-d`& command-line
+option.  Some examples (which depend on variables that don't exist in all
+contexts):
+.code
+      control = debug
+      control = debug/tag=.$sender_host_address
+      control = debug/opts=+expand+acl
+      control = debug/tag=.$message_exim_id/opts=+expand
+.endd
+
 .vitem &*control&~=&~enforce_sync*& &&&
        &*control&~=&~no_enforce_sync*&
 .cindex "SMTP" "synchronization checking"
 .vitem &*control&~=&~enforce_sync*& &&&
        &*control&~=&~no_enforce_sync*&
 .cindex "SMTP" "synchronization checking"
index 0591e41156a24d7af488adb2194ff182b1292eaa..b89d1b44ca1be2fc033867ba9a9465d92a55637e 100644 (file)
@@ -1,9 +1,10 @@
-$Cambridge: exim/doc/doc-txt/ChangeLog,v 1.622 2010/06/05 23:50:18 pdp Exp $
+$Cambridge: exim/doc/doc-txt/ChangeLog,v 1.623 2010/06/06 00:27:52 pdp Exp $
 
 Change log file for Exim from version 4.21
 -------------------------------------------
 
 Exim version 4.73
 
 Change log file for Exim from version 4.21
 -------------------------------------------
 
 Exim version 4.73
+-----------------
 
 PP/01 Date: & Message-Id: revert to normally being appended to a message,
       only prepend for the Resent-* case.  Fixes regression introduced in
 
 PP/01 Date: & Message-Id: revert to normally being appended to a message,
       only prepend for the Resent-* case.  Fixes regression introduced in
@@ -33,6 +34,8 @@ PP/08 Bugzilla 926: switch ClamAV to use the new zINSTREAM API for content
 
 PP/09 Implemented reverse_ip expansion operator.
 
 
 PP/09 Implemented reverse_ip expansion operator.
 
+PP/10 Bugzilla 937: provide a "debug" ACL control.
+
 
 Exim version 4.72
 -----------------
 
 Exim version 4.72
 -----------------
index 35ed9a399382d053630fdb71ca2f4d54070ea7e5..a8606870c77df86957cd2a8940fb7b1a8f6a5fab 100644 (file)
@@ -1,4 +1,4 @@
-$Cambridge: exim/doc/doc-txt/NewStuff,v 1.169 2010/06/05 23:50:18 pdp Exp $
+$Cambridge: exim/doc/doc-txt/NewStuff,v 1.170 2010/06/06 00:27:52 pdp Exp $
 
 New Features in Exim
 --------------------
 
 New Features in Exim
 --------------------
@@ -51,6 +51,18 @@ Version 4.73
       ${reverse_ip:2001:0db8:c42:9:1:abcd:192.0.2.3}
        -> 3.0.2.0.0.0.0.c.d.c.b.a.1.0.0.0.9.0.0.0.2.4.c.0.8.b.d.0.1.0.0.2
 
       ${reverse_ip:2001:0db8:c42:9:1:abcd:192.0.2.3}
        -> 3.0.2.0.0.0.0.c.d.c.b.a.1.0.0.0.9.0.0.0.2.4.c.0.8.b.d.0.1.0.0.2
 
+ 6. There is a new ACL control called "debug", to enable debug logging.
+    This allows selective logging of certain incoming transactions within
+    production environments, with some care.  It takes two options, "tag"
+    and "opts"; "tag" is included in the filename of the log and "opts"
+    is used as per the -d<options> command-line option.  Examples, which
+    don't all make sense in all contexts:
+
+      control = debug
+      control = debug/tag=.$sender_host_address
+      control = debug/opts=+expand+acl
+      control = debug/tag=.$message_exim_id/opts=+expand
+
 
 Version 4.72
 ------------
 
 Version 4.72
 ------------
index 8825a8f13d81338fd62a259946ef34e77aa252d4..4ad2b01b9a1cc7f04110b7b45592df706d2f376e 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/acl.c,v 1.87 2009/11/16 19:50:36 nm4 Exp $ */
+/* $Cambridge: exim/src/src/acl.c,v 1.88 2010/06/06 00:27:52 pdp Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -171,6 +171,7 @@ enum {
   #ifdef EXPERIMENTAL_BRIGHTMAIL
   CONTROL_BMI_RUN,
   #endif
   #ifdef EXPERIMENTAL_BRIGHTMAIL
   CONTROL_BMI_RUN,
   #endif
+  CONTROL_DEBUG,
   #ifndef DISABLE_DKIM
   CONTROL_DKIM_VERIFY,
   #endif
   #ifndef DISABLE_DKIM
   CONTROL_DKIM_VERIFY,
   #endif
@@ -204,6 +205,7 @@ static uschar *controls[] = {
   #ifdef EXPERIMENTAL_BRIGHTMAIL
   US"bmi_run",
   #endif
   #ifdef EXPERIMENTAL_BRIGHTMAIL
   US"bmi_run",
   #endif
+  US"debug",
   #ifndef DISABLE_DKIM
   US"dkim_disable_verify",
   #endif
   #ifndef DISABLE_DKIM
   US"dkim_disable_verify",
   #endif
@@ -517,6 +519,8 @@ static unsigned int control_forbids[] = {
   0,                                               /* bmi_run */
   #endif
 
   0,                                               /* bmi_run */
   #endif
 
+  0,                                               /* debug */
+
   #ifndef DISABLE_DKIM
   (1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_NOTSMTP)|      /* dkim_disable_verify */
     (1<<ACL_WHERE_NOTSMTP_START),
   #ifndef DISABLE_DKIM
   (1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_NOTSMTP)|      /* dkim_disable_verify */
     (1<<ACL_WHERE_NOTSMTP_START),
@@ -598,6 +602,7 @@ static control_def controls_list[] = {
 #ifdef EXPERIMENTAL_BRIGHTMAIL
   { US"bmi_run",                 CONTROL_BMI_RUN, FALSE },
 #endif
 #ifdef EXPERIMENTAL_BRIGHTMAIL
   { US"bmi_run",                 CONTROL_BMI_RUN, FALSE },
 #endif
+  { US"debug",                   CONTROL_DEBUG, TRUE },
 #ifndef DISABLE_DKIM
   { US"dkim_disable_verify",     CONTROL_DKIM_VERIFY, FALSE },
 #endif
 #ifndef DISABLE_DKIM
   { US"dkim_disable_verify",     CONTROL_DKIM_VERIFY, FALSE },
 #endif
@@ -2416,6 +2421,8 @@ acl_check_condition(int verb, acl_condition_block *cb, int where,
 {
 uschar *user_message = NULL;
 uschar *log_message = NULL;
 {
 uschar *user_message = NULL;
 uschar *log_message = NULL;
+uschar *debug_tag = NULL;
+uschar *debug_opts = NULL;
 uschar *p = NULL;
 int rc = OK;
 #ifdef WITH_CONTENT_SCAN
 uschar *p = NULL;
 int rc = OK;
 #ifdef WITH_CONTENT_SCAN
@@ -2703,6 +2710,27 @@ for (; cb != NULL; cb = cb->next)
         }
       break;
 
         }
       break;
 
+      case CONTROL_DEBUG:
+      while (*p == '/')
+        {
+        if (Ustrncmp(p, "/tag=", 5) == 0)
+          {
+          uschar *pp = p + 5;
+          while (*pp != '\0' && *pp != '/') pp++;
+          debug_tag = string_copyn(p+5, pp-p-5);
+          p = pp;
+          }
+        else if (Ustrncmp(p, "/opts=", 6) == 0)
+          {
+          uschar *pp = p + 6;
+          while (*pp != '\0' && *pp != '/') pp++;
+          debug_opts = string_copyn(p+6, pp-p-6);
+          p = pp;
+          }
+        }
+        debug_logging_activate(debug_tag, debug_opts);
+      break;
+
       case CONTROL_SUPPRESS_LOCAL_FIXUPS:
       suppress_local_fixups = TRUE;
       break;
       case CONTROL_SUPPRESS_LOCAL_FIXUPS:
       suppress_local_fixups = TRUE;
       break;
index c54470bd6e56062503db41ab92a3a5131b75f8ce..7d1c3b6a6a02fbfeba68d306fcbe0ed36f4914db 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/exim.c,v 1.66 2010/06/05 11:13:29 pdp Exp $ */
+/* $Cambridge: exim/src/src/exim.c,v 1.67 2010/06/06 00:27:52 pdp Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -681,155 +681,6 @@ else
 
 
 
 
 
 
-/*************************************************
-*         Decode bit settings for log/debug      *
-*************************************************/
-
-/* This function decodes a string containing bit settings in the form of +name
-and/or -name sequences, and sets/unsets bits in a bit string accordingly. It
-also recognizes a numeric setting of the form =<number>, but this is not
-intended for user use. It's an easy way for Exim to pass the debug settings
-when it is re-exec'ed.
-
-The log options are held in two unsigned ints (because there became too many
-for one). The top bit in the table means "put in 2nd selector". This does not
-yet apply to debug options, so the "=" facility sets only the first selector.
-
-The "all" selector, which must be equal to 0xffffffff, is recognized specially.
-It sets all the bits in both selectors. However, there is a facility for then
-unsetting certain bits, because we want to turn off "memory" in the debug case.
-
-A bad value for a debug setting is treated as an unknown option - error message
-to stderr and die. For log settings, which come from the configuration file,
-we write to the log on the way out...
-
-Arguments:
-  selector1      address of the first bit string
-  selector2      address of the second bit string, or NULL
-  notall1        bits to exclude from "all" for selector1
-  notall2        bits to exclude from "all" for selector2
-  string         the configured string
-  options        the table of option names
-  count          size of table
-  which          "log" or "debug"
-
-Returns:         nothing on success - bomb out on failure
-*/
-
-static void
-decode_bits(unsigned int *selector1, unsigned int *selector2, int notall1,
-  int notall2, uschar *string, bit_table *options, int count, uschar *which)
-{
-uschar *errmsg;
-if (string == NULL) return;
-
-if (*string == '=')
-  {
-  char *end;    /* Not uschar */
-  *selector1 = strtoul(CS string+1, &end, 0);
-  if (*end == 0) return;
-  errmsg = string_sprintf("malformed numeric %s_selector setting: %s", which,
-    string);
-  goto ERROR_RETURN;
-  }
-
-/* Handle symbolic setting */
-
-else for(;;)
-  {
-  BOOL adding;
-  uschar *s;
-  int len;
-  bit_table *start, *end;
-
-  while (isspace(*string)) string++;
-  if (*string == 0) return;
-
-  if (*string != '+' && *string != '-')
-    {
-    errmsg = string_sprintf("malformed %s_selector setting: "
-      "+ or - expected but found \"%s\"", which, string);
-    goto ERROR_RETURN;
-    }
-
-  adding = *string++ == '+';
-  s = string;
-  while (isalnum(*string) || *string == '_') string++;
-  len = string - s;
-
-  start = options;
-  end = options + count;
-
-  while (start < end)
-    {
-    bit_table *middle = start + (end - start)/2;
-    int c = Ustrncmp(s, middle->name, len);
-    if (c == 0)
-      {
-      if (middle->name[len] != 0) c = -1; else
-        {
-        unsigned int bit = middle->bit;
-        unsigned int *selector;
-
-        /* The value with all bits set means "force all bits in both selectors"
-        in the case where two are being handled. However, the top bit in the
-        second selector is never set. When setting, some bits can be excluded.
-        */
-
-        if (bit == 0xffffffff)
-          {
-          if (adding)
-            {
-            *selector1 = 0xffffffff ^ notall1;
-            if (selector2 != NULL) *selector2 = 0x7fffffff ^ notall2;
-            }
-          else
-            {
-            *selector1 = 0;
-            if (selector2 != NULL) *selector2 = 0;
-            }
-          }
-
-        /* Otherwise, the 0x80000000 bit means "this value, without the top
-        bit, belongs in the second selector". */
-
-        else
-          {
-          if ((bit & 0x80000000) != 0)
-            {
-            selector = selector2;
-            bit &= 0x7fffffff;
-            }
-          else selector = selector1;
-          if (adding) *selector |= bit; else *selector &= ~bit;
-          }
-        break;  /* Out of loop to match selector name */
-        }
-      }
-    if (c < 0) end = middle; else start = middle + 1;
-    }  /* Loop to match selector name */
-
-  if (start >= end)
-    {
-    errmsg = string_sprintf("unknown %s_selector setting: %c%.*s", which,
-      adding? '+' : '-', len, s);
-    goto ERROR_RETURN;
-    }
-  }    /* Loop for selector names */
-
-/* Handle disasters */
-
-ERROR_RETURN:
-if (Ustrcmp(which, "debug") == 0)
-  {
-  fprintf(stderr, "exim: %s\n", errmsg);
-  exit(EXIT_FAILURE);
-  }
-else log_write(0, LOG_CONFIG|LOG_PANIC_DIE, "%s", errmsg);
-}
-
-
-
 /*************************************************
 *          Show supported features               *
 *************************************************/
 /*************************************************
 *          Show supported features               *
 *************************************************/
@@ -2080,7 +1931,7 @@ for (i = 1; i < argc; i++)
         }
       if (*argrest != 0)
         decode_bits(&selector, NULL, D_memory, 0, argrest, debug_options,
         }
       if (*argrest != 0)
         decode_bits(&selector, NULL, D_memory, 0, argrest, debug_options,
-          debug_options_count, US"debug");
+          debug_options_count, US"debug", 0);
       debug_selector = selector;
       }
     break;
       debug_selector = selector;
       }
     break;
@@ -3249,8 +3100,8 @@ readconf_main();
 
 /* Handle the decoding of logging options. */
 
 
 /* Handle the decoding of logging options. */
 
-decode_bits(&log_write_selector, &log_extra_selector, 0, 0, log_selector_string,
-  log_options, log_options_count, US"log");
+decode_bits(&log_write_selector, &log_extra_selector, 0, 0,
+  log_selector_string, log_options, log_options_count, US"log", 0);
 
 DEBUG(D_any)
   {
 
 DEBUG(D_any)
   {
index 00f8bb7baa4bf712cd0425cda6273e0183ab0eb6..90c2b126969b0d2986532c354d43a5eedf13e4b2 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/functions.h,v 1.50 2010/06/05 23:50:18 pdp Exp $ */
+/* $Cambridge: exim/src/src/functions.h,v 1.51 2010/06/06 00:27:52 pdp Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -72,11 +72,14 @@ extern void    daemon_go(void);
 extern int     dcc_process(uschar **);
 #endif
 
 extern int     dcc_process(uschar **);
 #endif
 
+extern void    debug_logging_activate(uschar *, uschar *);
 extern void    debug_print_argv(uschar **);
 extern void    debug_print_ids(uschar *);
 extern void    debug_print_string(uschar *);
 extern void    debug_print_tree(tree_node *);
 extern void    debug_vprintf(char *, va_list);
 extern void    debug_print_argv(uschar **);
 extern void    debug_print_ids(uschar *);
 extern void    debug_print_string(uschar *);
 extern void    debug_print_tree(tree_node *);
 extern void    debug_vprintf(char *, va_list);
+extern void    decode_bits(unsigned int *, unsigned int *,
+                  int, int, uschar *, bit_table *, int, uschar *, int);
 extern address_item *deliver_make_addr(uschar *, BOOL);
 extern int     deliver_message(uschar *, BOOL, BOOL);
 extern void    deliver_msglog(const char *, ...);
 extern address_item *deliver_make_addr(uschar *, BOOL);
 extern int     deliver_message(uschar *, BOOL, BOOL);
 extern void    deliver_msglog(const char *, ...);
index a5cd100750d6bf0d08b238e7e2b25a8155050d0b..1d2c8f5e0a7a16833d21b46fbceb015c94a9d3cf 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/log.c,v 1.14 2009/11/16 19:50:37 nm4 Exp $ */
+/* $Cambridge: exim/src/src/log.c,v 1.15 2010/06/06 00:27:52 pdp Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -19,9 +19,9 @@ log files was originally contributed by Tony Sheen. */
 #define LOG_MODE_FILE   1
 #define LOG_MODE_SYSLOG 2
 
 #define LOG_MODE_FILE   1
 #define LOG_MODE_SYSLOG 2
 
-enum { lt_main, lt_reject, lt_panic, lt_process };
+enum { lt_main, lt_reject, lt_panic, lt_debug, lt_process };
 
 
-static uschar *log_names[] = { US"main", US"reject", US"panic", US"process" };
+static uschar *log_names[] = { US"main", US"reject", US"panic", US"debug", US"process" };
 
 
 
 
 
 
@@ -31,6 +31,7 @@ static uschar *log_names[] = { US"main", US"reject", US"panic", US"process" };
 
 static uschar mainlog_name[LOG_NAME_SIZE];
 static uschar rejectlog_name[LOG_NAME_SIZE];
 
 static uschar mainlog_name[LOG_NAME_SIZE];
 static uschar rejectlog_name[LOG_NAME_SIZE];
+static uschar debuglog_name[LOG_NAME_SIZE];
 
 static uschar *mainlog_datestamp = NULL;
 static uschar *rejectlog_datestamp = NULL;
 
 static uschar *mainlog_datestamp = NULL;
 static uschar *rejectlog_datestamp = NULL;
@@ -226,16 +227,17 @@ avoid races.
 
 Arguments:
   fd         where to return the resulting file descriptor
 
 Arguments:
   fd         where to return the resulting file descriptor
-  type       lt_main, lt_reject, lt_panic, or lt_process
+  type       lt_main, lt_reject, lt_panic, lt_debug or lt_process
+  tag        optional tag to include in the name (only hooked up for debug)
 
 Returns:   nothing
 */
 
 static void
 
 Returns:   nothing
 */
 
 static void
-open_log(int *fd, int type)
+open_log(int *fd, int type, uschar *tag)
 {
 uid_t euid;
 {
 uid_t euid;
-BOOL ok;
+BOOL ok, ok2;
 uschar buffer[LOG_NAME_SIZE];
 
 /* Sort out the file name. This depends on the type of log we are opening. The
 uschar buffer[LOG_NAME_SIZE];
 
 /* Sort out the file name. This depends on the type of log we are opening. The
@@ -282,6 +284,22 @@ else
     rejectlog_datestamp = rejectlog_name + string_datestamp_offset;
     }
 
     rejectlog_datestamp = rejectlog_name + string_datestamp_offset;
     }
 
+  /* and deal with the debug log (which keeps the datestamp, but does not
+  update it) */
+
+  else if (type == lt_debug)
+    {
+    Ustrcpy(debuglog_name, buffer);
+    if (tag)
+      {
+      /* this won't change the offset of the datestamp */
+      ok2 = string_format(buffer, sizeof(buffer), "%s%s",
+          debuglog_name, tag);
+      if (ok2)
+        Ustrcpy(debuglog_name, buffer);
+      }
+    }
+
   /* 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. */
   /* 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. */
@@ -860,7 +878,7 @@ if ((flags & LOG_MAIN) != 0 &&
 
     if (mainlogfd < 0)
       {
 
     if (mainlogfd < 0)
       {
-      open_log(&mainlogfd, lt_main);     /* No return on error */
+      open_log(&mainlogfd, lt_main, NULL);     /* No return on error */
       if (fstat(mainlogfd, &statbuf) >= 0) mainlog_inode = statbuf.st_ino;
       }
 
       if (fstat(mainlogfd, &statbuf) >= 0) mainlog_inode = statbuf.st_ino;
       }
 
@@ -984,7 +1002,7 @@ if ((flags & LOG_REJECT) != 0)
 
     if (rejectlogfd < 0)
       {
 
     if (rejectlogfd < 0)
       {
-      open_log(&rejectlogfd, lt_reject); /* No return on error */
+      open_log(&rejectlogfd, lt_reject, NULL); /* No return on error */
       if (fstat(rejectlogfd, &statbuf) >= 0) rejectlog_inode = statbuf.st_ino;
       }
 
       if (fstat(rejectlogfd, &statbuf) >= 0) rejectlog_inode = statbuf.st_ino;
       }
 
@@ -1005,7 +1023,7 @@ written to a file - never to syslog. */
 if ((flags & LOG_PROCESS) != 0)
   {
   int processlogfd;
 if ((flags & LOG_PROCESS) != 0)
   {
   int processlogfd;
-  open_log(&processlogfd, lt_process);  /* No return on error */
+  open_log(&processlogfd, lt_process, NULL);  /* No return on error */
   if ((rc = write(processlogfd, log_buffer, length)) != length)
     {
     log_write_failed(US"process log", length, rc);
   if ((rc = write(processlogfd, log_buffer, length)) != length)
     {
     log_write_failed(US"process log", length, rc);
@@ -1036,7 +1054,7 @@ if ((flags & LOG_PANIC) != 0)
   if ((logging_mode & LOG_MODE_FILE) != 0)
     {
     panic_recurseflag = TRUE;
   if ((logging_mode & LOG_MODE_FILE) != 0)
     {
     panic_recurseflag = TRUE;
-    open_log(&paniclogfd, lt_panic);  /* Won't return on failure */
+    open_log(&paniclogfd, lt_panic, NULL);  /* Won't return on failure */
     panic_recurseflag = FALSE;
 
     if (panic_save_buffer != NULL)
     panic_recurseflag = FALSE;
 
     if (panic_save_buffer != NULL)
@@ -1079,4 +1097,214 @@ closelog();
 syslog_open = FALSE;
 }
 
 syslog_open = FALSE;
 }
 
+
+
+/*************************************************
+*         Decode bit settings for log/debug      *
+*************************************************/
+
+/* This function decodes a string containing bit settings in the form of +name
+and/or -name sequences, and sets/unsets bits in a bit string accordingly. It
+also recognizes a numeric setting of the form =<number>, but this is not
+intended for user use. It's an easy way for Exim to pass the debug settings
+when it is re-exec'ed.
+
+The log options are held in two unsigned ints (because there became too many
+for one). The top bit in the table means "put in 2nd selector". This does not
+yet apply to debug options, so the "=" facility sets only the first selector.
+
+The "all" selector, which must be equal to 0xffffffff, is recognized specially.
+It sets all the bits in both selectors. However, there is a facility for then
+unsetting certain bits, because we want to turn off "memory" in the debug case.
+
+The action taken for bad values varies depending upon why we're here.
+For log messages, or if the debugging is triggered from config, then we write
+to the log on the way out.  For debug setting triggered from the command-line,
+we treat it as an unknown option: error message to stderr and die.
+
+Arguments:
+  selector1      address of the first bit string
+  selector2      address of the second bit string, or NULL
+  notall1        bits to exclude from "all" for selector1
+  notall2        bits to exclude from "all" for selector2
+  string         the configured string
+  options        the table of option names
+  count          size of table
+  which          "log" or "debug"
+  flags          DEBUG_FROM_CONFIG
+
+Returns:         nothing on success - bomb out on failure
+*/
+
+void
+decode_bits(unsigned int *selector1, unsigned int *selector2, int notall1,
+  int notall2, uschar *string, bit_table *options, int count, uschar *which,
+  int flags)
+{
+uschar *errmsg;
+if (string == NULL) return;
+
+if (*string == '=')
+  {
+  char *end;    /* Not uschar */
+  *selector1 = strtoul(CS string+1, &end, 0);
+  if (*end == 0) return;
+  errmsg = string_sprintf("malformed numeric %s_selector setting: %s", which,
+    string);
+  goto ERROR_RETURN;
+  }
+
+/* Handle symbolic setting */
+
+else for(;;)
+  {
+  BOOL adding;
+  uschar *s;
+  int len;
+  bit_table *start, *end;
+
+  while (isspace(*string)) string++;
+  if (*string == 0) return;
+
+  if (*string != '+' && *string != '-')
+    {
+    errmsg = string_sprintf("malformed %s_selector setting: "
+      "+ or - expected but found \"%s\"", which, string);
+    goto ERROR_RETURN;
+    }
+
+  adding = *string++ == '+';
+  s = string;
+  while (isalnum(*string) || *string == '_') string++;
+  len = string - s;
+
+  start = options;
+  end = options + count;
+
+  while (start < end)
+    {
+    bit_table *middle = start + (end - start)/2;
+    int c = Ustrncmp(s, middle->name, len);
+    if (c == 0)
+      {
+      if (middle->name[len] != 0) c = -1; else
+        {
+        unsigned int bit = middle->bit;
+        unsigned int *selector;
+
+        /* The value with all bits set means "force all bits in both selectors"
+        in the case where two are being handled. However, the top bit in the
+        second selector is never set. When setting, some bits can be excluded.
+        */
+
+        if (bit == 0xffffffff)
+          {
+          if (adding)
+            {
+            *selector1 = 0xffffffff ^ notall1;
+            if (selector2 != NULL) *selector2 = 0x7fffffff ^ notall2;
+            }
+          else
+            {
+            *selector1 = 0;
+            if (selector2 != NULL) *selector2 = 0;
+            }
+          }
+
+        /* Otherwise, the 0x80000000 bit means "this value, without the top
+        bit, belongs in the second selector". */
+
+        else
+          {
+          if ((bit & 0x80000000) != 0)
+            {
+            selector = selector2;
+            bit &= 0x7fffffff;
+            }
+          else selector = selector1;
+          if (adding) *selector |= bit; else *selector &= ~bit;
+          }
+        break;  /* Out of loop to match selector name */
+        }
+      }
+    if (c < 0) end = middle; else start = middle + 1;
+    }  /* Loop to match selector name */
+
+  if (start >= end)
+    {
+    errmsg = string_sprintf("unknown %s_selector setting: %c%.*s", which,
+      adding? '+' : '-', len, s);
+    goto ERROR_RETURN;
+    }
+  }    /* Loop for selector names */
+
+/* Handle disasters */
+
+ERROR_RETURN:
+if (Ustrcmp(which, "debug") == 0)
+  {
+  if (flags & DEBUG_FROM_CONFIG)
+    {
+    log_write(0, LOG_CONFIG|LOG_PANIC, "%s", errmsg);
+    return;
+    }
+  fprintf(stderr, "exim: %s\n", errmsg);
+  exit(EXIT_FAILURE);
+  }
+else log_write(0, LOG_CONFIG|LOG_PANIC_DIE, "%s", errmsg);
+}
+
+
+
+/*************************************************
+*        Activate a debug logfile (late)         *
+*************************************************/
+
+/* Normally, debugging is activated from the command-line; it may be useful
+within the configuration to activate debugging later, based on certain
+conditions.  If debugging is already in progress, we return early, no action
+taken (besides debug-logging that we wanted debug-logging).
+
+Failures in options are not fatal but will result in paniclog entries for the
+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. */
+
+void
+debug_logging_activate(uschar *tag_name, uschar *opts)
+{
+int fd = -1;
+
+if (debug_file)
+  {
+  debug_printf("DEBUGGING ACTIVATED FROM WITHIN CONFIG.\n"
+      "DEBUG: Tag=\"%s\" Opts=\"%s\"\n", tag_name, opts);
+  return;
+  }
+
+if (tag_name != NULL && (Ustrchr(tag_name, '/') != NULL))
+  {
+  log_write(0, LOG_MAIN|LOG_PANIC, "debug tag may not contain a '/' in: %s",
+      tag_name);
+  return;
+  }
+
+debug_selector = D_default;
+if (opts)
+  {
+  decode_bits(&debug_selector, NULL, D_memory, 0, opts,
+      debug_options, debug_options_count, US"debug", DEBUG_FROM_CONFIG);
+  }
+
+open_log(&fd, lt_debug, tag_name);
+
+if (fd != -1)
+  debug_file = fdopen(fd, "w");
+else
+  log_write(0, LOG_MAIN|LOG_PANIC, "unable to open debug log");
+}
+
+
 /* End of log.c */
 /* End of log.c */
index 4db115533821e8ed6b6202563a3a098a635a9ada..ed90b25357fb55ea35dc1249e0eaafcfe1e94e75 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/macros.h,v 1.39 2009/11/16 19:50:37 nm4 Exp $ */
+/* $Cambridge: exim/src/src/macros.h,v 1.40 2010/06/06 00:27:52 pdp Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -707,6 +707,9 @@ local_scan.h */
 #define LOG_CONFIG_FOR  (256+128) /* Add " for" instead of ":\n" */
 #define LOG_CONFIG_IN   (512+128) /* Add " in line x[ of file y]" */
 
 #define LOG_CONFIG_FOR  (256+128) /* Add " for" instead of ":\n" */
 #define LOG_CONFIG_IN   (512+128) /* Add " in line x[ of file y]" */
 
+/* and for debug_bits() logging action control: */
+#define DEBUG_FROM_CONFIG       0x0001
+
 /* SMTP command identifiers for the smtp_connection_had field that records the
 most recent SMTP commands. Must be kept in step with the list of names in
 smtp_in.c that is used for creating the smtp_no_mail logging action. SCH_NONE
 /* SMTP command identifiers for the smtp_connection_had field that records the
 most recent SMTP commands. Must be kept in step with the list of names in
 smtp_in.c that is used for creating the smtp_no_mail logging action. SCH_NONE