Implement %M datestamping in log filenames.
authorPhil Pennock <pdp@exim.org>
Sun, 13 Feb 2011 05:31:49 +0000 (00:31 -0500)
committerPhil Pennock <pdp@exim.org>
Sun, 13 Feb 2011 05:31:49 +0000 (00:31 -0500)
Patch from Simon Arlott.

fixes bug 486

13 files changed:
doc/doc-docbook/spec.xfpt
doc/doc-txt/ChangeLog
doc/doc-txt/NewStuff
src/exim_monitor/em_globals.c
src/exim_monitor/em_main.c
src/src/expand.c
src/src/globals.c
src/src/globals.h
src/src/log.c
src/src/macros.h
src/src/readconf.c
src/src/string.c
src/src/tod.c

index b75e36ba4ba6540491ea1a3211b6db1f27fc4d63..bc9dd2e145442ab1f7e77a06be4a7377ef8cd414 100644 (file)
@@ -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.
 
 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 &<<SECTdatlogfil>>& below.
+.new
+A log file path may also contain &`%D`& or &`%M`& if datestamped log file names
+are in use &-- see section &<<SECTdatlogfil>>& below.
+.wen
 
 Here are some examples of possible settings:
 .display
 
 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,
 .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
 .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:
 .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/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
 .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.
 
 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
 .code
 /var/spool/exim/log/paniclog
 /var/log/exim-panic.log
 /var/spool/exim/log/paniclog
+/var/log/exim/panic
 .endd
 .endd
+.wen
 
 
 .section "Logging to syslog" "SECID249"
 
 
 .section "Logging to syslog" "SECID249"
index 439e80abaace8b27762b663e2e61b3da0c1dba07..63a73c9bb590bf8138488a88954299b3ccafea2e 100644 (file)
@@ -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.
 
       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
 -----------------
 
 Exim version 4.74
 -----------------
index 46fd6c4d865bbfe6e2bead648e3c58457cda7a70..6159bf4433d0c80c8fe108d42f77b179807fc644 100644 (file)
@@ -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.
 
     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
 ------------
 
 Version 4.74
 ------------
index b333a94617b55a294bfd453a4ba3b86f75a83f85..a9e4980b1ac01d8ac6f93082c118fbdcaec288da 100644 (file)
@@ -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;
 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;
 
 BOOL    timestamps_utc         = FALSE;
 BOOL    tls_certificate_verified = FALSE;
index 2756ab0151cd5f02e86f266e281e4fdb8cc09aa1..187dba3b06f9782519a62b6cdf14b9601df8fe5e 100644 (file)
@@ -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
 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
 string_format.
 
 Once opened, save the file's inode so that we can detect when the file is
index 287129c2f99d8209c434d74745e540cf42bccdff..1fd4335da70c74d1b2584f26e54e96f8ee5a0da9 100644 (file)
@@ -1587,7 +1587,7 @@ while (last > first)
     return tod_stamp(tod_zulu);
 
     case vtype_todlf:                          /* Log file datestamp tod */
     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,
 
     case vtype_reply:                          /* Get reply address */
     s = find_header(US"reply-to:", exists_only, newsize, TRUE,
index 8631c7d8cc42031029e3cd1a45f7ad6086b27820..3882a30741d86e2726a6c2b0c1f7e961ce8ba658 100644 (file)
@@ -1164,6 +1164,8 @@ BOOL    srs_usetimestamp       = TRUE;
 #endif
 BOOL    strict_acl_vars        = FALSE;
 int     string_datestamp_offset= -1;
 #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;
 BOOL    strip_excess_angle_brackets = FALSE;
 BOOL    strip_trailing_dot     = FALSE;
 uschar *submission_domain      = NULL;
index bdc9bcf6dcdcefc3ed275b2112f0868ddef6454a..cc9021e1b80948ddd969f1f9e239ca06a3d69b54 100644 (file)
@@ -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 */
 #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 */
 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 */
index 67a3d8543335598b675024742a6bc8844b57794c..0995621ac62a5edb3a32e24963d16be37ccc9b02 100644 (file)
@@ -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
 
 /* 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
   {
 
 else
   {
@@ -307,7 +308,7 @@ else
   else if (string_datestamp_offset >= 0)
     {
     uschar *from = buffer + string_datestamp_offset;
   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++;
     if (from == buffer || from[-1] == '/')
       {
       if (!isalnum(*to)) to++;
@@ -858,7 +859,7 @@ if ((flags & LOG_MAIN) != 0 &&
 
     if (mainlog_datestamp != NULL)
       {
 
     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 */
       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)
       {
 
     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 */
       if (Ustrncmp (rejectlog_datestamp, nowstamp, Ustrlen(nowstamp)) != 0)
         {
         (void)close(rejectlogfd);       /* Close the file */
index 3f24025af3a7ebda1c4ab883386b32d5c6f516fd..610221fd96f01f52764939dfab242e02d0d91602 100644 (file)
@@ -191,8 +191,9 @@ enum { RESET_NEXT, RESET_ANSWERS, RESET_AUTHORITY, RESET_ADDITIONAL };
 
 /* Argument values for the time-of-day function */
 
 
 /* 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 */
 
 
 /* For identifying types of driver */
 
index b9d3747a5e6eb7d887579e1cd0dcdf563e7939a7..f5e895ac65378fec5af29706d63c67bfc1ffe72c 100644 (file)
@@ -3038,8 +3038,8 @@ if (s == NULL)
 spool_directory = s;
 
 /* Expand log_file_path, which must contain "%s" in any component that isn't
 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)
   {
 
 if (*log_file_path != 0)
   {
@@ -3063,7 +3063,7 @@ if (*log_file_path != 0)
     t = Ustrchr(sss, '%');
     if (t != NULL)
       {
     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);
       }
         log_write(0, LOG_MAIN|LOG_PANIC_DIE, "log_file_path \"%s\" contains "
           "unexpected \"%%\" character", s);
       }
index e7a3b92a580419f6c49965febd8264ce49da45e9..49ffc968548ff183445c132e2652cb631ab12408 100644 (file)
@@ -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
 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:
 datestamped log files.
 
 Arguments:
@@ -1075,6 +1075,8 @@ uschar *p = buffer;
 uschar *last = buffer + buflen - 1;
 
 string_datestamp_offset = -1;  /* Datestamp not inserted */
 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 */
 
 
 /* Scan the format and handle the insertions */
 
@@ -1229,19 +1231,31 @@ while (*fp != 0)
     *p++ = va_arg(ap, int);
     break;
 
     *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_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 *);
 
     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);
 
     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. */
     /* 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. */
index 85f7e7861ef59445ce5d4aafcdf1ed9fa1912eac..2077111703deb842e067ce2d07c8973b85b49a42 100644 (file)
@@ -34,17 +34,18 @@ a leading zero for the full stamp, since Ustrftime() doesn't provide this
 option.
 
 Argument:  type of timestamp required:
 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
 */
 
 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. */
 
   /* 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
   #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);
   (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
   #else
+  case tod_log_datestamp_daily:
   (void) sprintf(CS timebuf, "%04d%02d%02d",
     1900 + t->tm_year, 1 + t->tm_mon, t->tm_mday);
   (void) sprintf(CS timebuf, "%04d%02d%02d",
     1900 + t->tm_year, 1 + t->tm_mon, t->tm_mday);
-  #endif
   break;
 
   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
   /* 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