From f1e5fef536bdc2c5d84c4bc7852d6e948253ccaf Mon Sep 17 00:00:00 2001 From: Phil Pennock Date: Sun, 13 Feb 2011 00:31:49 -0500 Subject: [PATCH] Implement %M datestamping in log filenames. Patch from Simon Arlott. fixes bug 486 --- doc/doc-docbook/spec.xfpt | 28 +++++++++++++++++---------- doc/doc-txt/ChangeLog | 3 +++ doc/doc-txt/NewStuff | 3 +++ src/exim_monitor/em_globals.c | 2 ++ src/exim_monitor/em_main.c | 4 ++-- src/src/expand.c | 2 +- src/src/globals.c | 2 ++ src/src/globals.h | 2 ++ src/src/log.c | 17 +++++++++-------- src/src/macros.h | 5 +++-- src/src/readconf.c | 6 +++--- src/src/string.c | 22 +++++++++++++++++---- src/src/tod.c | 36 ++++++++++++++++++++++------------- 13 files changed, 89 insertions(+), 43 deletions(-) diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt index b75e36ba4..bc9dd2e14 100644 --- a/doc/doc-docbook/spec.xfpt +++ b/doc/doc-docbook/spec.xfpt @@ -32110,8 +32110,10 @@ log_file_path = $spool_directory/log/%slog If you do not specify anything at build time or run time, that is where the logs are written. -A log file path may also contain &`%D`& if datestamped log file names are in -use &-- see section &<>& below. +.new +A log file path may also contain &`%D`& or &`%M`& if datestamped log file names +are in use &-- see section &<>& below. +.wen Here are some examples of possible settings: .display @@ -32151,18 +32153,20 @@ renamed. +.new .section "Datestamped log files" "SECTdatlogfil" .cindex "log" "datestamped files" Instead of cycling the main and reject log files by renaming them periodically, some sites like to use files whose names contain a datestamp, -for example, &_mainlog-20031225_&. The datestamp is in the form &_yyyymmdd_&. -Exim has support for this way of working. It is enabled by setting the -&%log_file_path%& option to a path that includes &`%D`& at the point where the -datestamp is required. For example: +for example, &_mainlog-20031225_&. The datestamp is in the form &_yyyymmdd_& or +&_yyyymm_&. Exim has support for this way of working. It is enabled by setting +the &%log_file_path%& option to a path that includes &`%D`& or &`%M`& at the +point where the datestamp is required. For example: .code log_file_path = /var/spool/exim/log/%slog-%D log_file_path = /var/log/exim-%s-%D.log log_file_path = /var/spool/exim/log/%D-%slog +log_file_path = /var/log/exim/%s.%M .endd As before, &`%s`& is replaced by &"main"& or &"reject"&; the following are examples of names generated by the above examples: @@ -32170,6 +32174,7 @@ examples of names generated by the above examples: /var/spool/exim/log/mainlog-20021225 /var/log/exim-reject-20021225.log /var/spool/exim/log/20021225-mainlog +/var/log/exim/main.200212 .endd When this form of log file is specified, Exim automatically switches to new files at midnight. It does not make any attempt to compress old logs; you @@ -32178,15 +32183,18 @@ run &'exicyclog'& with this form of logging. The location of the panic log is also determined by &%log_file_path%&, but it is not datestamped, because rotation of the panic log does not make sense. -When generating the name of the panic log, &`%D`& is removed from the string. -In addition, if it immediately follows a slash, a following non-alphanumeric -character is removed; otherwise a preceding non-alphanumeric character is -removed. Thus, the three examples above would give these panic log names: +When generating the name of the panic log, &`%D`& or &`%M`& are removed from +the string. In addition, if it immediately follows a slash, a following +non-alphanumeric character is removed; otherwise a preceding non-alphanumeric +character is removed. Thus, the four examples above would give these panic +log names: .code /var/spool/exim/log/paniclog /var/log/exim-panic.log /var/spool/exim/log/paniclog +/var/log/exim/panic .endd +.wen .section "Logging to syslog" "SECID249" diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog index 439e80aba..63a73c9bb 100644 --- a/doc/doc-txt/ChangeLog +++ b/doc/doc-txt/ChangeLog @@ -48,6 +48,9 @@ PP/07 Bugzilla 1061: restrict error messages sent over SMTP to not reveal SQL string expansion failure details. Patch from Andrey Oktyabrski. +PP/08 Bugzilla 486: implement %M datestamping in log filenames. + Patch from Simon Arlott. + Exim version 4.74 ----------------- diff --git a/doc/doc-txt/NewStuff b/doc/doc-txt/NewStuff index 46fd6c4d8..6159bf443 100644 --- a/doc/doc-txt/NewStuff +++ b/doc/doc-txt/NewStuff @@ -22,6 +22,9 @@ Version 4.75 false. When true, if the external delivery command exits on a signal then Exim will freeze the message in the queue, instead of generating a bounce. + 3. Log filenames may now use %M as an escape, instead of %D (still available). + The %M pattern expands to yyyymm, providing month-level resolution. + Version 4.74 ------------ diff --git a/src/exim_monitor/em_globals.c b/src/exim_monitor/em_globals.c index b333a9461..a9e4980b1 100644 --- a/src/exim_monitor/em_globals.c +++ b/src/exim_monitor/em_globals.c @@ -209,6 +209,8 @@ uschar *smtp_active_hostname = NULL; BOOL split_spool_directory = FALSE; uschar *spool_directory = US SPOOL_DIRECTORY; int string_datestamp_offset=-1; +int string_datestamp_length= 0; +int string_datestamp_type = -1; BOOL timestamps_utc = FALSE; BOOL tls_certificate_verified = FALSE; diff --git a/src/exim_monitor/em_main.c b/src/exim_monitor/em_main.c index 2756ab015..187dba3b0 100644 --- a/src/exim_monitor/em_main.c +++ b/src/exim_monitor/em_main.c @@ -645,8 +645,8 @@ stripchart_init(); only, and we can't tail the log. If not, open the log file and position to the end of it. Before doing so, we have to detect whether the log files are datestamped, and if so, sort out the name. The string in log_file already has -%s replaced by "main"; if datestamping is occurring, %D will be present. In -fact, we don't need to test explicitly - just process the string with +%s replaced by "main"; if datestamping is occurring, %D or %M will be present. +In fact, we don't need to test explicitly - just process the string with string_format. Once opened, save the file's inode so that we can detect when the file is diff --git a/src/src/expand.c b/src/src/expand.c index 287129c2f..1fd4335da 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -1587,7 +1587,7 @@ while (last > first) return tod_stamp(tod_zulu); case vtype_todlf: /* Log file datestamp tod */ - return tod_stamp(tod_log_datestamp); + return tod_stamp(tod_log_datestamp_daily); case vtype_reply: /* Get reply address */ s = find_header(US"reply-to:", exists_only, newsize, TRUE, diff --git a/src/src/globals.c b/src/src/globals.c index 8631c7d8c..3882a3074 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -1164,6 +1164,8 @@ BOOL srs_usetimestamp = TRUE; #endif BOOL strict_acl_vars = FALSE; int string_datestamp_offset= -1; +int string_datestamp_length= 0; +int string_datestamp_type = -1; BOOL strip_excess_angle_brackets = FALSE; BOOL strip_trailing_dot = FALSE; uschar *submission_domain = NULL; diff --git a/src/src/globals.h b/src/src/globals.h index bdc9bcf6d..cc9021e1b 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -744,6 +744,8 @@ extern BOOL srs_usetimestamp; /* SRS use timestamp flag */ #endif extern BOOL strict_acl_vars; /* ACL variables have to be set before being used */ extern int string_datestamp_offset;/* After insertion by string_format */ +extern int string_datestamp_length;/* After insertion by string_format */ +extern int string_datestamp_type; /* After insertion by string_format */ extern BOOL strip_excess_angle_brackets; /* Surrounding route-addrs */ extern BOOL strip_trailing_dot; /* Remove dots at ends of domains */ extern uschar *submission_domain; /* Domain for submission mode */ diff --git a/src/src/log.c b/src/src/log.c index 67a3d8543..0995621ac 100644 --- a/src/src/log.c +++ b/src/src/log.c @@ -255,11 +255,12 @@ if (type == lt_process) /* The names of the other three logs are controlled by file_path. The panic log is written to the same directory as the main and reject logs, but its name does -not have a datestamp. The use of datestamps is indicated by %D in file_path. -When opening the panic log, if %D is present, we remove the datestamp from the -generated name; if it is at the start, remove a following non-alphameric -character as well; otherwise, remove a preceding non-alphameric character. This -is definitely kludgy, but it sort of does what people want, I hope. */ +not have a datestamp. The use of datestamps is indicated by %D/%M in file_path. +When opening the panic log, if %D or %M is present, we remove the datestamp +from the generated name; if it is at the start, remove a following +non-alphanumeric character as well; otherwise, remove a preceding +non-alphanumeric character. This is definitely kludgy, but it sort of does what +people want, I hope. */ else { @@ -307,7 +308,7 @@ else else if (string_datestamp_offset >= 0) { uschar *from = buffer + string_datestamp_offset; - uschar *to = from + Ustrlen(tod_stamp(tod_log_datestamp)); + uschar *to = from + string_datestamp_length; if (from == buffer || from[-1] == '/') { if (!isalnum(*to)) to++; @@ -858,7 +859,7 @@ if ((flags & LOG_MAIN) != 0 && if (mainlog_datestamp != NULL) { - uschar *nowstamp = tod_stamp(tod_log_datestamp); + uschar *nowstamp = tod_stamp(string_datestamp_type); if (Ustrncmp (mainlog_datestamp, nowstamp, Ustrlen(nowstamp)) != 0) { (void)close(mainlogfd); /* Close the file */ @@ -981,7 +982,7 @@ if ((flags & LOG_REJECT) != 0) if (rejectlog_datestamp != NULL) { - uschar *nowstamp = tod_stamp(tod_log_datestamp); + uschar *nowstamp = tod_stamp(string_datestamp_type); if (Ustrncmp (rejectlog_datestamp, nowstamp, Ustrlen(nowstamp)) != 0) { (void)close(rejectlogfd); /* Close the file */ diff --git a/src/src/macros.h b/src/src/macros.h index 3f24025af..610221fd9 100644 --- a/src/src/macros.h +++ b/src/src/macros.h @@ -191,8 +191,9 @@ enum { RESET_NEXT, RESET_ANSWERS, RESET_AUTHORITY, RESET_ADDITIONAL }; /* Argument values for the time-of-day function */ -enum { tod_log, tod_log_bare, tod_log_zone, tod_log_datestamp, - tod_zone, tod_full, tod_bsdin, tod_mbx, tod_epoch, tod_zulu }; +enum { tod_log, tod_log_bare, tod_log_zone, tod_log_datestamp_daily, + tod_log_datestamp_monthly, tod_zone, tod_full, tod_bsdin, + tod_mbx, tod_epoch, tod_zulu }; /* For identifying types of driver */ diff --git a/src/src/readconf.c b/src/src/readconf.c index b9d3747a5..f5e895ac6 100644 --- a/src/src/readconf.c +++ b/src/src/readconf.c @@ -3038,8 +3038,8 @@ if (s == NULL) spool_directory = s; /* Expand log_file_path, which must contain "%s" in any component that isn't -the null string or "syslog". It is also allowed to contain one instance of %D. -However, it must NOT contain % followed by anything else. */ +the null string or "syslog". It is also allowed to contain one instance of %D +or %M. However, it must NOT contain % followed by anything else. */ if (*log_file_path != 0) { @@ -3063,7 +3063,7 @@ if (*log_file_path != 0) t = Ustrchr(sss, '%'); if (t != NULL) { - if (t[1] != 'D' || Ustrchr(t+2, '%') != NULL) + if ((t[1] != 'D' && t[1] != 'M') || Ustrchr(t+2, '%') != NULL) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "log_file_path \"%s\" contains " "unexpected \"%%\" character", s); } diff --git a/src/src/string.c b/src/src/string.c index e7a3b92a5..49ffc9685 100644 --- a/src/src/string.c +++ b/src/src/string.c @@ -1039,7 +1039,7 @@ as a va_list item. The formats are the usual printf() ones, with some omissions (never used) and two additions for strings: %S forces lower case, and %#s or %#S prints nothing for a NULL string. Without the # "NULL" is printed (useful in debugging). There -is also the addition of %D, which inserts the date in the form used for +is also the addition of %D and %M, which insert the date in the form used for datestamped log files. Arguments: @@ -1075,6 +1075,8 @@ uschar *p = buffer; uschar *last = buffer + buflen - 1; string_datestamp_offset = -1; /* Datestamp not inserted */ +string_datestamp_length = 0; /* Datestamp not inserted */ +string_datestamp_type = 0; /* Datestamp not inserted */ /* Scan the format and handle the insertions */ @@ -1229,19 +1231,31 @@ while (*fp != 0) *p++ = va_arg(ap, int); break; - case 'D': /* Insert datestamp for log file names */ - s = CS tod_stamp(tod_log_datestamp); + case 'D': /* Insert daily datestamp for log file names */ + s = CS tod_stamp(tod_log_datestamp_daily); string_datestamp_offset = p - buffer; /* Passed back via global */ + string_datestamp_length = Ustrlen(s); /* Passed back via global */ + string_datestamp_type = tod_log_datestamp_daily; + slen = string_datestamp_length; + goto INSERT_STRING; + + case 'M': /* Insert monthly datestamp for log file names */ + s = CS tod_stamp(tod_log_datestamp_monthly); + string_datestamp_offset = p - buffer; /* Passed back via global */ + string_datestamp_length = Ustrlen(s); /* Passed back via global */ + string_datestamp_type = tod_log_datestamp_monthly; + slen = string_datestamp_length; goto INSERT_STRING; case 's': case 'S': /* Forces *lower* case */ s = va_arg(ap, char *); - INSERT_STRING: /* Come to from %D above */ if (s == NULL) s = null; slen = Ustrlen(s); + INSERT_STRING: /* Come to from %D or %M above */ + /* If the width is specified, check that there is a precision set; if not, set it to the width to prevent overruns of long strings. */ diff --git a/src/src/tod.c b/src/src/tod.c index 85f7e7861..207711170 100644 --- a/src/src/tod.c +++ b/src/src/tod.c @@ -34,17 +34,18 @@ a leading zero for the full stamp, since Ustrftime() doesn't provide this option. Argument: type of timestamp required: - tod_bsdin BSD inbox format - tod_epoch Unix epoch format - tod_full full date and time - tod_log log file data line format, - with zone if log_timezone is TRUE - tod_log_bare always without zone - tod_log_datestamp for log file names when datestamped - tod_log_zone always with zone - tod_mbx MBX inbox format - tod_zone just the timezone offset - tod_zulu time in 8601 zulu format + tod_bsdin BSD inbox format + tod_epoch Unix epoch format + tod_full full date and time + tod_log log file data line format, + with zone if log_timezone is TRUE + tod_log_bare always without zone + tod_log_datestamp_daily for log file names when datestamped daily + tod_log_datestamp_monthly for log file names when datestamped monthly + tod_log_zone always with zone + tod_mbx MBX inbox format + tod_zone just the timezone offset + tod_zulu time in 8601 zulu format Returns: pointer to fixed buffer containing the timestamp */ @@ -91,16 +92,25 @@ switch(type) /* Format used as suffix of log file name when 'log_datestamp' is active. For testing purposes, it changes the file every second. */ - case tod_log_datestamp: #ifdef TESTING_LOG_DATESTAMP + case tod_log_datestamp_daily: + case tod_log_datestamp_monthly: (void) sprintf(CS timebuf, "%04d%02d%02d%02d%02d", 1900 + t->tm_year, 1 + t->tm_mon, t->tm_mday, t->tm_hour, t->tm_min); + break; + #else + case tod_log_datestamp_daily: (void) sprintf(CS timebuf, "%04d%02d%02d", 1900 + t->tm_year, 1 + t->tm_mon, t->tm_mday); - #endif break; + case tod_log_datestamp_monthly: + (void) sprintf(CS timebuf, "%04d%02d", + 1900 + t->tm_year, 1 + t->tm_mon); + break; + #endif + /* Format used in BSD inbox separator lines. Sort-of documented in RFC 976 ("UUCP Mail Interchange Format Standard") but only by example, not by explicit definition. The examples show no timezone offsets, and some MUAs -- 2.30.2