-. $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
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"
-$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
+-----------------
PP/01 Date: & Message-Id: revert to normally being appended to a message,
only prepend for the Resent-* case. Fixes regression introduced in
PP/09 Implemented reverse_ip expansion operator.
+PP/10 Bugzilla 937: provide a "debug" ACL control.
+
Exim version 4.72
-----------------
-$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
--------------------
${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
------------
-/* $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 *
#ifdef EXPERIMENTAL_BRIGHTMAIL
CONTROL_BMI_RUN,
#endif
+ CONTROL_DEBUG,
#ifndef DISABLE_DKIM
CONTROL_DKIM_VERIFY,
#endif
#ifdef EXPERIMENTAL_BRIGHTMAIL
US"bmi_run",
#endif
+ US"debug",
#ifndef DISABLE_DKIM
US"dkim_disable_verify",
#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),
#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
{
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
}
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;
-/* $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 *
-/*************************************************
-* 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 *
*************************************************/
}
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;
/* 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)
{
-/* $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 *
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 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 *, ...);
-/* $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 *
#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" };
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;
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
-open_log(int *fd, int type)
+open_log(int *fd, int type, uschar *tag)
{
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
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. */
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 (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 ((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 ((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)
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 */
-/* $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 *
#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