diff options
author | Todd C. Miller <millert@cvs.openbsd.org> | 2003-02-12 19:17:37 +0000 |
---|---|---|
committer | Todd C. Miller <millert@cvs.openbsd.org> | 2003-02-12 19:17:37 +0000 |
commit | 3f9baa9576d20be60eae7d8ed51b759a615d8ce5 (patch) | |
tree | c5d362c519b3985d0b129dfa8f75b3e8f71bf105 /usr.bin/newsyslog | |
parent | edf626817ff1f325da1d31e8659fb958a4605c52 (diff) |
Add support for rotating files at a specific time; from FreeBSD
Diffstat (limited to 'usr.bin/newsyslog')
-rw-r--r-- | usr.bin/newsyslog/newsyslog.8 | 193 | ||||
-rw-r--r-- | usr.bin/newsyslog/newsyslog.c | 255 |
2 files changed, 410 insertions, 38 deletions
diff --git a/usr.bin/newsyslog/newsyslog.8 b/usr.bin/newsyslog/newsyslog.8 index ec02d73a517..4e01c911d42 100644 --- a/usr.bin/newsyslog/newsyslog.8 +++ b/usr.bin/newsyslog/newsyslog.8 @@ -1,4 +1,4 @@ -.\" $OpenBSD: newsyslog.8,v 1.32 2003/02/09 07:26:45 jmc Exp $ +.\" $OpenBSD: newsyslog.8,v 1.33 2003/02/12 19:17:36 millert Exp $ .\" .\" Copyright (c) 1997, Jason Downs. All rights reserved. .\" @@ -77,7 +77,7 @@ the last period's logs in it, has the next to last period's logs in it, and so on, up to a user-specified number of archived logs. -Optionally the archived logs can be compressed to save space. +The archived logs may be optionally compressed to save space. .Pp The options are as follows: .Bl -tag -width Ds @@ -116,7 +116,9 @@ signal to .Xr syslogd 8 , so this option should only be used in debugging. .It Fl v -Be verbose. +Place +.Nm newsyslog +in verbose mode. In this mode it will print out each log and its reasons for either trimming that log or skipping it. .It Fl a Ar directory @@ -180,12 +182,12 @@ are ignored. The fields of the configuration file are as follows: .Bl -tag -width XXXXXXXXXXXXXXXX -.It logfile name +.It Ar logfile_name The full pathname of the system log file to be archived. -.It owner.group of archives (optional) -Specify an ownership and group for the archive file. +.It Ar owner:group +This optional field specifies the owner and group for the archive file. The -.Ql \&. +.Ql : is essential, even if the .Ar owner or @@ -193,15 +195,19 @@ or field is left blank. The fields may be numeric, or a name which is looked up in the system password and group databases. -.It mode of logfile & archives -Octal mode of created log files and archives. -.It number of archives -Specify the number of archives to be kept besides the log file itself. -.It size of archives +For backwards compatibility, a +.Ql \&. +may be used instead of a +.Ql : . +.It Ar mode +File mode (in octal) to use for created log files and archives. +.It Ar count +The number of archives to be kept besides the log file itself. +.It Ar size When the size of the log file (in kilobytes) reaches this point, the log file is trimmed as described above. -If this field is replaced by a -.Ql * , +If this field is replaced by an +.Ql \&* , then the size of the log file is not taken into account when determining when to trim the log file. @@ -212,14 +218,138 @@ This prevents .Nm from rotating files consisting solely of a message indicating that the log file has been turned over. -.It archive interval -Specify the time separation between the trimming of the log file. -If this field is replaced by a -.Ql * , -the number of hours since the last time the -log was trimmed will not be taken into consideration. -.It flags (optional) +.It Ar when The +.Ar when +field can consist of an interval, a specific time, or both. +If the +.Ar when +field consists of an asterisk +.Pq Ql \&* , +log rotation will depend only on the contents of the +.Ar size +field. +Otherwise, the +.Ar when +field consists of an optional interval in hours, possibly followed +by an +.So Li \&@ Sc Ns No -sign +and a time in a restricted +.Tn ISO 8601 +format or by a +.So Li \&$ Sc Ns No -sign +and a time specification for logfile rotation at a fixed time once +per day, per week or per month. +.Pp +If a time is specified, the log file will only be trimmed if +.Nm +is run within one hour of the specified time. +If an interval is specified, the log file will be trimmed if that +many hours have passed since the last rotation. +When both a time and an interval are specified, both conditions +must be satisfied for the rotation to take place. +.Pp +There is no provision for the specification of a timezone. +There is little point in specifying an explicit minutes or seconds +component in the current implementation, since the only comparison is +.Sq within the hour . +.Ss ISO 8601 restricted time format +The lead-in character for a restricted +.Tn ISO 8601 +time is an +.So Li \&@ Sc Ns No -sign . +The particular format of the time in restricted +.Tn ISO 8601 +is: +.Sm off +.Oo Oo Oo Oo Oo +.Va \&cc Oc +.Va \&yy Oc +.Va \&mm Oc +.Va \&dd Oc +.Oo Li \&T +.Oo Va \&hh +.Oo Va \&mm +.Oo Va \&ss +.Oc Oc Oc Oc Oc . +.Sm on +Optional date fields default to the appropriate component of the +current date; optional time fields default to midnight +For example, if today is January 22, 1999, the following date specifications +are all equivalent: +.Pp +.Bl -item -compact -offset indent +.It +.Ql 19990122T000000 +.It +.Ql 990122T000000 +.It +.Ql 0122T000000 +.It +.Ql 22T000000 +.It +.Ql T000000 +.It +.Ql T0000 +.It +.Ql T00 +.It +.Ql 22T +.It +.Ql \&T +.It +.Ql \& +.El +.Ss Day, week and month time format +The lead-in character for day, week and month specification is a +.So Li \&$ Sc Ns No -sign . +The particular format of day, week and month specification is: +.Op Va D\&hh , +.Op Va W\&w Ns Op Va D\&hh +and +.Op Va M\&dd Ns Op Va D\&hh , +respectively. +Optional time fields default to midnight. +The ranges for day and hour specifications are: +.Pp +.Bl -tag -width Ds -compact -offset indent +.It Ar hh +hours, range 0 ... 23 +.It Ar w +day of week, range 0 ... 6, 0 = Sunday +.It Ar dd +day of month, range 1 ... 31, or the letter +.Em L +or +.Em l +to specify the last day of the month. +.El +.Ss Some examples: +.Bl -tag -width Ds -compact -offset indent +.It Ar $D0 +rotate every night at midnight +(same as +.Ar @T00 ) +.It Ar $D23 +rotate every day at 23:00 hr +(same as +.Ar @T23 ) +.It Ar $W0D23 +rotate every week on Sunday at 23:00 hr +.It Ar $W5D16 +rotate every week on Friday at 16:00 hr +.It Ar $M1D0 +rotate on the first day of every month at midnight +(i.e., the start of the day; same as +.Ar @01T00 ) +.It Ar $M5D6 +rotate on every 5th day of the month at 6:00 hr +(same as +.Ar @05T06 ) +.El +.Pp +.It Ar flags +The optional .Ar flags field specifies if the archives should have any special processing done to the archived log files. @@ -245,19 +375,23 @@ log file. The .Sq F flag specifies that symbolic links should be followed. -.It monitor notification (only used with the `M' flag) +.It Ar monitor Specify the username (or email address) that should receive notification messages if this is a monitored log file. Notification messages are sent as email; the operator deserves what they get if they mark the .Xr sendmail 8 log file as monitored. -.It pid file (optional) -Specify a file containing the PID of a process to send a -.Dv SIGHUP -signal to instead of +This field is only valid when the +.Sq M +flag is set. +.It Ar pid_file +This optional field specifies a file containing the PID of a process to send a +signal (usually +.Dv SIGHUP ) +to instead of .Pa /var/run/syslog.pid . -.It signal (optional) +.It Ar signal Specify the signal to send to the process instead of .Dv SIGHUP . Signal names @@ -265,8 +399,9 @@ must start with .Dq SIG and be the signal name, not the number, e.g., .Dv SIGUSR1 . -.It command (optional) -Specify a command to run instead of sending a signal to the process. +.It Ar command +This optional field specifies a command to run instead of sending a signal +to the process. The command must be enclosed in double quotes .Pq Ql \&" . The empty string, diff --git a/usr.bin/newsyslog/newsyslog.c b/usr.bin/newsyslog/newsyslog.c index c7c966ce1d9..975020fd5b6 100644 --- a/usr.bin/newsyslog/newsyslog.c +++ b/usr.bin/newsyslog/newsyslog.c @@ -1,4 +1,4 @@ -/* $OpenBSD: newsyslog.c,v 1.62 2003/01/25 05:16:50 millert Exp $ */ +/* $OpenBSD: newsyslog.c,v 1.63 2003/02/12 19:17:36 millert Exp $ */ /* * Copyright (c) 1999, 2002, 2003 Todd C. Miller <Todd.Miller@courtesan.com> @@ -86,7 +86,7 @@ */ #ifndef lint -static const char rcsid[] = "$OpenBSD: newsyslog.c,v 1.62 2003/01/25 05:16:50 millert Exp $"; +static const char rcsid[] = "$OpenBSD: newsyslog.c,v 1.63 2003/02/12 19:17:36 millert Exp $"; #endif /* not lint */ #ifndef CONF @@ -132,6 +132,7 @@ static const char rcsid[] = "$OpenBSD: newsyslog.c,v 1.62 2003/01/25 05:16:50 mi /* status messages */ #define CE_MONITOR 0x08 /* Monitory for changes */ #define CE_FOLLOW 0x10 /* Follow symbolic links */ +#define CE_TRIMAT 0x20 /* trim at a specific time */ #define MIN_PID 4 /* Don't touch pids lower than this */ #define MIN_SIZE 256 /* Don't rotate if smaller (in bytes) */ @@ -147,6 +148,7 @@ struct conf_entry { int numlogs; /* Number of logs to keep */ off_t size; /* Size cutoff to trigger trimming the log */ int hours; /* Hours between log trimming */ + time_t trim_at; /* Specific time at which to do trimming */ int permissions; /* File permissions on the log */ int signal; /* Signal to send (defaults to SIGHUP) */ int flags; /* Flags (CE_COMPACT & CE_BINARY) */ @@ -192,6 +194,8 @@ void run_command(char *); void send_signal(char *, int); char *lstat_log(char *, size_t, int); int stat_suffix(char *, size_t, char *, struct stat *, int (*)()); +time_t parse8601(char *); +time_t parseDWM(char *); int main(int argc, char **argv) @@ -324,6 +328,16 @@ do_entry(struct conf_entry *ent) if (size < 0) { DPRINTF(("does not exist.\n")); } else { + if (ent->flags & CE_TRIMAT && !force) { + if (timenow < ent->trim_at || + difftime(timenow, ent->trim_at) >= 60 * 60) { + DPRINTF(("--> will trim at %s", + ctime(&ent->trim_at))); + return; + } else if (verbose && ent->hours <= 0) { + DPRINTF(("--> time is up\n")); + } + } if (ent->size > 0) DPRINTF(("size (KB): %.2f [%d] ", size / 1024.0, (int)(ent->size / 1024))); @@ -333,6 +347,7 @@ do_entry(struct conf_entry *ent) DPRINTF(("--> monitored\n")); else if (!monitormode && (force || (ent->size > 0 && size >= ent->size) || + (ent->hours <= 0 && (ent->flags & CE_TRIMAT)) || (ent->hours > 0 && (modtime >= ent->hours || modtime < 0) && ((ent->flags & CE_BINARY) || size >= MIN_SIZE)))) { DPRINTF(("--> trimming log....\n")); @@ -471,8 +486,9 @@ struct conf_entry * parse_file(int *nentries) { FILE *f; - char line[BUFSIZ], *parse, *q, *errline, *group, *tmp; + char line[BUFSIZ], *parse, *q, *errline, *group, *tmp, *ep; int lineno; + unsigned long ul; struct conf_entry *first = NULL; struct conf_entry *working = NULL; struct passwd *pwd; @@ -516,7 +532,8 @@ parse_file(int *nentries) q = parse = missing_field(sob(++parse), errline, lineno); *(parse = son(parse)) = '\0'; - if ((group = strchr(q, '.')) != NULL) { + if ((group = strchr(q, ':')) != NULL || + (group = strrchr(q, '.')) != NULL) { *group++ = '\0'; if (*q) { if (!(isnumberstr(q))) { @@ -564,14 +581,38 @@ parse_file(int *nentries) else working->size = -1; + working->flags = 0; q = parse = missing_field(sob(++parse), errline, lineno); *(parse = son(parse)) = '\0'; - if (isdigit(*q)) - working->hours = atoi(q); - else - working->hours = -1; + ul = strtoul(q, &ep, 10); + if (ul > INT_MAX) + errx(1, "%s:%d: interval out of range: %s", conf, + lineno, q); + working->hours = (int)ul; + switch (*ep) { + case '\0': + break; + case '@': + working->trim_at = parse8601(ep + 1); + if (working->trim_at == (time_t) - 1) + errx(1, "%s:%d: bad time: %s", conf, lineno, q); + working->flags |= CE_TRIMAT; + break; + case '$': + working->trim_at = parseDWM(ep + 1); + if (working->trim_at == (time_t) - 1) + errx(1, "%s:%d: bad time: %s", conf, lineno, q); + working->flags |= CE_TRIMAT; + break; + case '*': + if (q == ep) + break; + /* FALLTHROUGH */ + default: + errx(1, "%s:%d: bad interval/at: %s", conf, lineno, q); + break; + } - working->flags = 0; q = sob(++parse); /* Optional field */ if (*q == 'Z' || *q == 'z' || *q == 'B' || *q == 'b' || *q == 'M' || *q == 'm') { @@ -1093,3 +1134,199 @@ lstat_log(char *file, size_t size, int flags) } return (NULL); } + +/* + * Parse a limited subset of ISO 8601. The specific format is as follows: + * + * [CC[YY[MM[DD]]]][THH[MM[SS]]] (where `T' is the literal letter) + * + * We don't accept a timezone specification; missing fields (including timezone) + * are defaulted to the current date but time zero. + */ +time_t +parse8601(char *s) +{ + char *t; + struct tm tm, *tmp; + unsigned long ul; + + tmp = localtime(&timenow); + tm = *tmp; + + tm.tm_hour = tm.tm_min = tm.tm_sec = 0; + + ul = strtoul(s, &t, 10); + if (*t != '\0' && *t != 'T') + return (-1); + + /* + * Now t points either to the end of the string (if no time was + * provided) or to the letter `T' which separates date and time in + * ISO 8601. The pointer arithmetic is the same for either case. + */ + switch (t - s) { + case 8: + tm.tm_year = ((ul / 1000000) - 19) * 100; + ul = ul % 1000000; + case 6: + tm.tm_year -= tm.tm_year % 100; + tm.tm_year += ul / 10000; + ul = ul % 10000; + case 4: + tm.tm_mon = (ul / 100) - 1; + ul = ul % 100; + case 2: + tm.tm_mday = ul; + case 0: + break; + default: + return (-1); + } + + /* sanity check */ + if (tm.tm_year < 70 || tm.tm_mon < 0 || tm.tm_mon > 12 + || tm.tm_mday < 1 || tm.tm_mday > 31) + return (-1); + + if (*t != '\0') { + s = ++t; + ul = strtoul(s, &t, 10); + if (*t != '\0' && !isspace(*t)) + return (-1); + + switch (t - s) { + case 6: + tm.tm_sec = ul % 100; + ul /= 100; + case 4: + tm.tm_min = ul % 100; + ul /= 100; + case 2: + tm.tm_hour = ul; + case 0: + break; + default: + return (-1); + } + + /* sanity check */ + if (tm.tm_sec < 0 || tm.tm_sec > 60 || tm.tm_min < 0 + || tm.tm_min > 59 || tm.tm_hour < 0 || tm.tm_hour > 23) + return (-1); + } + return (mktime(&tm)); +} + +/*- + * Parse a cyclic time specification, the format is as follows: + * + * [Dhh] or [Wd[Dhh]] or [Mdd[Dhh]] + * + * to rotate a logfile cyclic at + * + * - every day (D) within a specific hour (hh) (hh = 0...23) + * - once a week (W) at a specific day (d) OR (d = 0..6, 0 = Sunday) + * - once a month (M) at a specific day (d) (d = 1..31,l|L) + * + * We don't accept a timezone specification; missing fields + * are defaulted to the current date but time zero. + */ +time_t +parseDWM(char *s) +{ + char *t; + struct tm tm, *tmp; + long l; + int nd; + static int mtab[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + int WMseen = 0; + int Dseen = 0; + + tmp = localtime(&timenow); + tm = *tmp; + + /* set no. of days per month */ + + nd = mtab[tm.tm_mon]; + + if (tm.tm_mon == 1) { + if (((tm.tm_year + 1900) % 4 == 0) && + ((tm.tm_year + 1900) % 100 != 0) && + ((tm.tm_year + 1900) % 400 == 0)) { + nd++; /* leap year, 29 days in february */ + } + } + tm.tm_hour = tm.tm_min = tm.tm_sec = 0; + + for (;;) { + switch (*s) { + case 'D': + if (Dseen) + return (-1); + Dseen++; + s++; + l = strtol(s, &t, 10); + if (l < 0 || l > 23) + return (-1); + tm.tm_hour = l; + break; + + case 'W': + if (WMseen) + return (-1); + WMseen++; + s++; + l = strtol(s, &t, 10); + if (l < 0 || l > 6) + return (-1); + if (l != tm.tm_wday) { + int save; + + if (l < tm.tm_wday) { + save = 6 - tm.tm_wday; + save += (l + 1); + } else { + save = l - tm.tm_wday; + } + + tm.tm_mday += save; + + if (tm.tm_mday > nd) { + tm.tm_mon++; + tm.tm_mday = tm.tm_mday - nd; + } + } + break; + + case 'M': + if (WMseen) + return (-1); + WMseen++; + s++; + if (tolower(*s) == 'l') { + tm.tm_mday = nd; + s++; + t = s; + } else { + l = strtol(s, &t, 10); + if (l < 1 || l > 31) + return (-1); + + if (l > nd) + return (-1); + tm.tm_mday = l; + } + break; + + default: + return (-1); + break; + } + + if (*t == '\0' || isspace(*t)) + break; + else + s = t; + } + return (mktime(&tm)); +} |