diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/src/acl.c | 34 | ||||
-rw-r--r-- | src/src/child.c | 24 | ||||
-rw-r--r-- | src/src/debug.c | 88 | ||||
-rw-r--r-- | src/src/exim.c | 27 | ||||
-rw-r--r-- | src/src/functions.h | 3 | ||||
-rw-r--r-- | src/src/globals.c | 5 | ||||
-rw-r--r-- | src/src/globals.h | 4 | ||||
-rw-r--r-- | src/src/log.c | 15 | ||||
-rw-r--r-- | src/src/macros.h | 9 |
9 files changed, 174 insertions, 35 deletions
diff --git a/src/src/acl.c b/src/src/acl.c index 19c1bbbd9..fa1172331 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -3498,25 +3498,39 @@ for (; cb; cb = cb->next) } else if (Ustrncmp(pp, "kill", 4) == 0) { - for (pp += 4; *pp && *pp != '/';) pp++; + pp += 4; kill = TRUE; } else if (Ustrncmp(pp, "stop", 4) == 0) { - for (pp += 4; *pp && *pp != '/';) pp++; + pp += 4; stop = TRUE; } - else - while (*pp && *pp != '/') pp++; + else if (Ustrncmp(pp, "pretrigger=", 11) == 0) + debug_pretrigger_setup(pp+11); + else if (Ustrncmp(pp, "trigger=", 8) == 0) + { + if (Ustrncmp(pp += 8, "now", 3) == 0) + { + pp += 3; + debug_trigger_fire(); + } + else if (Ustrncmp(pp, "paniclog", 8) == 0) + { + pp += 8; + dtrigger_selector |= BIT(DTi_panictrigger); + } + } + while (*pp && *pp != '/') pp++; p = pp; } - if (kill) - debug_logging_stop(TRUE); - else if (stop) - debug_logging_stop(FALSE); - else - debug_logging_activate(debug_tag, debug_opts); + if (kill) + debug_logging_stop(TRUE); + else if (stop) + debug_logging_stop(FALSE); + else if (debug_tag || debug_opts) + debug_logging_activate(debug_tag, debug_opts); break; } diff --git a/src/src/child.c b/src/src/child.c index 4a262d623..07115bee4 100644 --- a/src/src/child.c +++ b/src/src/child.c @@ -76,7 +76,7 @@ int n = 0; int extra = pcount ? *pcount : 0; uschar **argv; -argv = store_get((extra + acount + MAX_CLMACROS + 21) * sizeof(char *), FALSE); +argv = store_get((extra + acount + MAX_CLMACROS + 24) * sizeof(char *), FALSE); /* In all case, the list starts out with the path, any macros, and a changed config file. */ @@ -88,10 +88,7 @@ if (clmacro_count > 0) n += clmacro_count; } if (f.config_changed) - { - argv[n++] = US"-C"; - argv[n++] = config_main_filename; - } + { argv[n++] = US"-C"; argv[n++] = config_main_filename; } /* These values are added only for non-minimal cases. If debug_selector is precisely D_v, we have to assume this was started by a non-admin user, and @@ -120,22 +117,23 @@ if (!minimal) } } } + if (debug_pretrigger_buf) + { argv[n++] = US"-dp"; argv[n++] = string_sprintf("0x%x", debug_pretrigger_bsize); } + if (dtrigger_selector != 0) + argv[n++] = string_sprintf("-dt=0x%x", dtrigger_selector); DEBUG(D_any) { argv[n++] = US"-MCd"; argv[n++] = US process_purpose; } - if (!f.testsuite_delays) argv[n++] = US"-odd"; - if (f.dont_deliver) argv[n++] = US"-N"; - if (f.queue_smtp) argv[n++] = US"-odqs"; - if (f.synchronous_delivery) argv[n++] = US"-odi"; + if (!f.testsuite_delays) argv[n++] = US"-odd"; + if (f.dont_deliver) argv[n++] = US"-N"; + if (f.queue_smtp) argv[n++] = US"-odqs"; + if (f.synchronous_delivery) argv[n++] = US"-odi"; if (connection_max_messages >= 0) argv[n++] = string_sprintf("-oB%d", connection_max_messages); if (*queue_name) - { - argv[n++] = US"-MCG"; - argv[n++] = queue_name; - } + { argv[n++] = US"-MCG"; argv[n++] = queue_name; } } /* Now add in any others that are in the call. Remember which they were, diff --git a/src/src/debug.c b/src/src/debug.c index 92cad6d48..7b9be057b 100644 --- a/src/src/debug.c +++ b/src/src/debug.c @@ -13,6 +13,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 */ @@ -318,8 +320,40 @@ if (debug_ptr[-1] == '\n') } } - fprintf(debug_file, "%s", CS debug_buffer); - fflush(debug_file); + if (debug_pretrigger_buf) + { + int needed = Ustrlen(debug_buffer), 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); + + 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; } @@ -410,4 +444,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 */ diff --git a/src/src/exim.c b/src/src/exim.c index 664d5d7f1..b59157286 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -2655,21 +2655,36 @@ on the second character (the one after '-'), to save some effort. */ #endif break; - /* -d: Set debug level (see also -v below) or set the drop_cr option. - The latter is now a no-op, retained for compatibility only. If -dd is used, - debugging subprocesses of the daemon is disabled. */ - case 'd': + + /* -dropcr: Set this option. Now a no-op, retained for compatibility only. */ + if (Ustrcmp(argrest, "ropcr") == 0) { /* drop_cr = TRUE; */ } - /* Use an intermediate variable so that we don't set debugging while - decoding the debugging bits. */ + /* -dp: Set up a debug pretrigger buffer with given size. */ + + else if (Ustrcmp(argrest, "p") == 0) + if (++i >= argc) + badarg = TRUE; + else + debug_pretrigger_setup(argv[i]); + + /* -dt: Set a debug trigger selector */ + + else if (Ustrncmp(argrest, "t=", 2) == 0) + dtrigger_selector = (unsigned int) Ustrtol(argrest + 2, NULL, 0); + + /* -d: Set debug level (see also -v below). + If -dd is used, debugging subprocesses of the daemon is disabled. */ else { + /* Use an intermediate variable so that we don't set debugging while + decoding the debugging bits. */ + unsigned int selector = D_default; debug_selector = 0; debug_file = NULL; diff --git a/src/src/functions.h b/src/src/functions.h index d27c23baa..4b4ff5e14 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -193,7 +193,10 @@ extern void debug_printf_indent(const char *, ...) PRINTF_FUNCTION(1,2); extern void debug_print_string(uschar *); extern void debug_print_tree(const char *, tree_node *); extern void debug_vprintf(int, const char *, va_list); +extern void debug_pretrigger_setup(const uschar *); +extern void debug_pretrigger_discard(void); extern void debug_print_socket(int); +extern void debug_trigger_fire(void); extern void decode_bits(unsigned int *, size_t, int *, uschar *, bit_table *, int, uschar *, int); diff --git a/src/src/globals.c b/src/src/globals.c index 7f7ceac00..7e50867b2 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -815,8 +815,10 @@ bit_table debug_options[] = { /* must be in alphabetical order and use BIT_TABLE(D, verify), }; int debug_options_count = nelem(debug_options); - +unsigned debug_pretrigger_bsize= 0; +uschar * debug_pretrigger_buf = NULL; unsigned int debug_selector = 0; + int delay_warning[DELAY_WARNING_SIZE] = { DELAY_WARNING_SIZE, 1, 24*60*60 }; uschar *delay_warning_condition= US"${if or {" @@ -897,6 +899,7 @@ uschar *dnslist_value = NULL; tree_node *domainlist_anchor = NULL; int domainlist_count = 0; uschar *dsn_from = US DEFAULT_DSN_FROM; +unsigned int dtrigger_selector = 0; int errno_quota = ERRNO_QUOTA; uschar *errors_copy = NULL; diff --git a/src/src/globals.h b/src/src/globals.h index 10dd3effc..de4670a81 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -487,6 +487,8 @@ extern FILE *debug_file; /* Where to write debugging info */ extern int debug_notall[]; /* Debug options excluded from +all */ extern bit_table debug_options[]; /* Table of debug options */ extern int debug_options_count; /* Size of table */ +extern unsigned debug_pretrigger_bsize; +extern uschar *debug_pretrigger_buf; /* circular buffer for precapture */ extern BOOL debug_store; /* Do extra checks on store_reset */ extern int delay_warning[]; /* Times between warnings */ extern uschar *delay_warning_condition; /* Condition string for warnings */ @@ -576,6 +578,8 @@ extern int domainlist_count; /* Number defined */ /* This option is now a no-opt, retained for compatibility */ extern BOOL drop_cr; /* For broken local MUAs */ +extern unsigned int dtrigger_selector; /* when to start debug */ + extern uschar *dsn_from; /* From: string for DSNs */ extern BOOL envelope_to_remove; /* Remove envelope_to_headers */ diff --git a/src/src/log.c b/src/src/log.c index fe9bd0c9f..8bdb244bd 100644 --- a/src/src/log.c +++ b/src/src/log.c @@ -905,6 +905,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. */ @@ -1470,7 +1475,11 @@ 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) @@ -1482,7 +1491,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); @@ -1512,11 +1521,13 @@ else 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; +debug_fd = -1; if (kill) unlink_log(lt_debug); } diff --git a/src/src/macros.h b/src/src/macros.h index 80e0ecbe1..5e26a7bce 100644 --- a/src/src/macros.h +++ b/src/src/macros.h @@ -106,7 +106,7 @@ don't make the file descriptors two-way. */ /* Debugging control */ #define DEBUG(x) if (debug_selector & (x)) -#define HDEBUG(x) if (host_checking || (debug_selector & (x))) +#define HDEBUG(x) if (host_checking || debug_selector & (x)) /* The default From: text for DSNs */ @@ -422,6 +422,13 @@ enum { D_timestamp | \ D_resolver)) +/* Bits for debug triggers */ + +enum { + DTi_panictrigger, + DTi_pretrigger, +}; + /* Options bits for logging. Those that have values < BITWORDSIZE can be used in calls to log_write(). The others are put into later words in log_selector and are only ever tested independently, so they do not need bit mask |