summaryrefslogtreecommitdiff
path: root/usr.bin/newsyslog
diff options
context:
space:
mode:
authorTodd C. Miller <millert@cvs.openbsd.org>2003-02-12 19:17:37 +0000
committerTodd C. Miller <millert@cvs.openbsd.org>2003-02-12 19:17:37 +0000
commit3f9baa9576d20be60eae7d8ed51b759a615d8ce5 (patch)
treec5d362c519b3985d0b129dfa8f75b3e8f71bf105 /usr.bin/newsyslog
parentedf626817ff1f325da1d31e8659fb958a4605c52 (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.8193
-rw-r--r--usr.bin/newsyslog/newsyslog.c255
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));
+}