From 27719c46cd1486850c0576f2129a7fd6b5c6255b Mon Sep 17 00:00:00 2001 From: wollman Date: Fri, 22 Jan 1999 19:38:39 +0000 Subject: A slight bit of code and doco cleanup, but mostly: Add the much-needed and long-desired ability to force the log rotation to take place on a specific day or time, and document same. This works by extending the syntax of the `when' field to allow a restricted ISO 8601 time specification, and performing the log rotation if newsyslog is run during the same hour as is specified. --- usr.sbin/newsyslog/Makefile | 9 +- usr.sbin/newsyslog/newsyslog.8 | 136 +++++++++++++++++++++++++----- usr.sbin/newsyslog/newsyslog.c | 184 +++++++++++++++++++++++++++++++---------- usr.sbin/newsyslog/pathnames.h | 27 ++++++ 4 files changed, 284 insertions(+), 72 deletions(-) create mode 100644 usr.sbin/newsyslog/pathnames.h (limited to 'usr.sbin/newsyslog') diff --git a/usr.sbin/newsyslog/Makefile b/usr.sbin/newsyslog/Makefile index 5d83c09..ea2b61a 100644 --- a/usr.sbin/newsyslog/Makefile +++ b/usr.sbin/newsyslog/Makefile @@ -1,14 +1,7 @@ -# $Id: Makefile,v 1.4 1997/02/22 16:08:24 peter Exp $ +# $Id: Makefile,v 1.5 1998/09/19 22:42:11 obrien Exp $ PROG= newsyslog -CFLAGS+= -DOSF -CFLAGS+= -DCONF=\"/etc/newsyslog.conf\" -CFLAGS+= -DPIDFILE=\"/var/run/syslog.pid\" -CFLAGS+= -DCOMPRESS_PATH=\"/usr/bin/gzip\" -CFLAGS+= -DCOMPRESS_PROG=\"gzip\" -CFLAGS+= -DCOMPRESS_POSTFIX=\".gz\" - MAN8= newsyslog.8 .include diff --git a/usr.sbin/newsyslog/newsyslog.8 b/usr.sbin/newsyslog/newsyslog.8 index a13e176..d54de06 100644 --- a/usr.sbin/newsyslog/newsyslog.8 +++ b/usr.sbin/newsyslog/newsyslog.8 @@ -1,7 +1,7 @@ .\" This file contains changes from the Open Software Foundation. .\" .\" from: @(#)newsyslog.8 -.\" $Id: newsyslog.8,v 1.13 1998/05/10 19:04:05 hoek Exp $ +.\" $Id: newsyslog.8,v 1.14 1998/06/09 18:24:04 ache Exp $ .\" .\" Copyright 1988, 1989 by the Massachusetts Institute of Technology .\" @@ -17,7 +17,7 @@ .\" the suitability of this software for any purpose. It is .\" provided "as is" without express or implied warranty. .\" -.Dd January 12, 1989 +.Dd January 22, 1999 .Dt NEWSYSLOG 8 .Os .Sh NAME @@ -34,25 +34,41 @@ is a program that should be scheduled to run periodically by When it is executed it archives log files if necessary. If a log file is determined to require archiving, .Nm -rearranges the files so that ``logfile'' is empty, ``logfile.0'' has -the last period's logs in it, ``logfile.1'' has the next to last +rearranges the files so that +.Dq Va logfile +is empty, +.Dq Va logfile Ns Li \&.0 +has +the last period's logs in it, +.Dq Va logfile Ns Li \&.1 +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. .Pp -A log can be archived because of two reasons. The log file can have -grown bigger than a preset size in kilobytes, or a preset number of -hours may have elapsed since the last log archive. The granularity of +A log can be archived for three reasons: +.Bl -enum -offset indent +.It +It is larger than the configured size (in kilobytes). +.It +A configured number of hours have elapsed since the log was last +archived. +.It +This is the specific configured hour for rotation of the log. +.El +The granularity of .Nm is dependent on how often it is scheduled to run by .Xr cron 8 . Since the program is quite fast, it may be scheduled to run every hour -without any ill effects. +without any ill effects, +and mode three (above) assumes that this is so. .Pp When starting up, .Nm -reads in a configuration file to determine which logs should be looked -at. By default, this configuration file is +reads in a configuration file to determine which logs may potentially +be archived. +By default, this configuration file is .Pa /etc/newsyslog.conf . Each line of the file contains information about a particular log file that should be handled by @@ -85,18 +101,98 @@ besides the log file itself. When the size of the log file reaches .Ar size , the log file will be trimmed as described above. If this field -is replaced by a -.Ar * , +is replaced by an asterisk +.Pq Ql \&* , then the size of the log file is not taken into account when determining when to trim the log file. -.It Ar interval -When -.Ar interval -hours have passed, the log file will be trimmed. If this field is -replaced by a -.Ar * , -then the number of hours since the last time the log was -trimmed will not be taken into consideration. +.It Ar when +The +.Ar when +field can consist of an interval, a specific time, or both. If +the +.Ar when +field is 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, optionally followed +by an +.So Li \&@ Sc Ns No -sign +and a time in a restricted +.Tn ISO 8601 +format. If a time is specified, the log file will only be trimmed +if +.Nm newsyslog +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 +The particular format of the time 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; hence if today +is January 22, 1999, the following date specifications are all +equivalent: +.Pp +.Bl -item -compact -offset indent +.It +.Sq Li 19990122T000000 +.It +.Sq Li 990122T000000 +.It +.Sq Li 0122T000000 +.It +.Sq Li 22T000000 +.It +.Sq Li T000000 +.It +.Sq Li T0000 +.It +.Sq Li T00 +.It +.Sq Li 22T +.It +.Sq Li \&T +.It +.Sq Li \& +.El +.Pp +There is no provision for 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 `within the +hour'. .It Ar flags This optional field specifies if the archive should have any special processing done to the archived log files. diff --git a/usr.sbin/newsyslog/newsyslog.c b/usr.sbin/newsyslog/newsyslog.c index faae982..832a8c3 100644 --- a/usr.sbin/newsyslog/newsyslog.c +++ b/usr.sbin/newsyslog/newsyslog.c @@ -27,41 +27,33 @@ provided "as is" without express or implied warranty. #ifndef lint static const char rcsid[] = - "$Id: newsyslog.c,v 1.20 1998/06/09 18:24:04 ache Exp $"; + "$Id: newsyslog.c,v 1.21 1998/12/23 12:03:33 peter Exp $"; #endif /* not lint */ -#ifndef CONF -#define CONF "/etc/athena/newsyslog.conf" /* Configuration file */ -#endif -#ifndef PIDFILE -#define PIDFILE "/etc/syslog.pid" -#endif -#ifndef COMPRESS_PATH -#define COMPRESS_PATH "/usr/ucb/compress" /* File compression program */ -#endif -#ifndef COMPRESS_PROG -#define COMPRESS_PROG "compress" -#endif +#define OSF #ifndef COMPRESS_POSTFIX -#define COMPRESS_POSTFIX ".Z" +#define COMPRESS_POSTFIX ".gz" #endif #include #include #include #include +#include #include #include #include #include #include +#include #include #include -#include #include #include #include +#include "pathnames.h" + #define kbytes(size) (((size) + 1023) >> 10) #ifdef _IBMR2 /* Calculates (db * DEV_BSIZE) */ @@ -71,6 +63,8 @@ static const char rcsid[] = #define CE_COMPACT 1 /* Compact the achived log files */ #define CE_BINARY 2 /* Logfile is in binary, don't add */ /* status messages */ +#define CE_TRIMAT 4 /* trim at a specific time */ + #define NONE -1 struct conf_entry { @@ -81,6 +75,7 @@ struct conf_entry { int numlogs; /* Number of logs to keep */ int size; /* Size cutoff to trigger trimming the log */ int hours; /* Hours between log trimming */ + time_t trim_at; /* Specific time to do trimming */ int permissions; /* File permissions on the log */ int flags; /* Flags (CE_COMPACT & CE_BINARY) */ int sig; /* Signal to send */ @@ -91,17 +86,13 @@ int verbose = 0; /* Print out what's going on */ int needroot = 1; /* Root privs are necessary */ int noaction = 0; /* Don't do anything, just show it */ int force = 0; /* Force the trim no matter what*/ -char *conf = CONF; /* Configuration file to use */ +char *conf = _PATH_CONF; /* Configuration file to use */ time_t timenow; #define MIN_PID 5 #define MAX_PID 99999 /* was lower, see /usr/include/sys/proc.h */ char hostname[MAXHOSTNAMELEN+1]; /* hostname */ char *daytime; /* timenow in human readable form */ -#ifndef OSF -char *strdup(char *strp); -#endif - static struct conf_entry *parse_file(); static char *sob(char *p); static char *son(char *p); @@ -115,6 +106,7 @@ static void compress_log(char *log); static int sizefile(char *file); static int age_old_log(char *file); static pid_t get_pid(char *pid_file); +static time_t parse8601(const char *s); int main(argc,argv) int argc; @@ -155,11 +147,23 @@ static void do_entry(ent) if (verbose) printf("does not exist.\n"); } else { + if (ent->flags & CE_TRIMAT) { + if (timenow < ent->trim_at + || difftime(timenow, ent->trim_at) >= 60*60) { + if (verbose) + printf("--> will trim at %s", + ctime(&ent->trim_at)); + return; + } else if (verbose && ent->hours <= 0) { + printf("--> time is up\n"); + } + } if (verbose && (ent->size > 0)) printf("size (Kb): %d [%d] ", size, ent->size); if (verbose && (ent->hours > 0)) printf(" age (hr): %d [%d] ", modtime, ent->hours); if (force || ((ent->size > 0) && (size >= ent->size)) || + (ent->hours <= 0 && (ent->flags & CE_TRIMAT)) || ((ent->hours > 0) && ((modtime >= ent->hours) || (modtime < 0)))) { if (verbose) @@ -177,7 +181,7 @@ static void do_entry(ent) } else { /* Only try to notify syslog if we are root */ if (needroot) - pid_file = PIDFILE; + pid_file = _PATH_SYSLOGPID; else pid_file = NULL; } @@ -230,7 +234,7 @@ static void PRS(argc,argv) default: usage(); } - } +} static void usage() { @@ -341,14 +345,34 @@ static struct conf_entry *parse_file() else working->size = -1; + working->flags = 0; q = parse = missing_field(sob(++parse),errline); parse = son(parse); eol = !*parse; *parse = '\0'; - if (isdigit(*q)) - working->hours = atoi(q); - else - working->hours = -1; + { + char *ep; + u_long ul; + + ul = strtoul(q, &ep, 10); + if (ep == q) + working->hours = 0; + else if (*ep == '*') + working->hours = -1; + else if (ul > INT_MAX) + errx(1, "interval is too large:\n%s", errline); + else + working->hours = ul; + + if (*ep != '\0' && *ep != '@' && *ep != '*') + errx(1, "malformed interval/at:\n%s", errline); + if (*ep == '@') { + if ((working->trim_at = parse8601(ep + 1)) + == (time_t)-1) + errx(1, "malformed at:\n%s", errline); + working->flags |= CE_TRIMAT; + } + } if (eol) q = NULL; @@ -360,7 +384,6 @@ static struct conf_entry *parse_file() *parse = '\0'; } - working->flags = 0; while (q && *q && !isspace(*q)) { if ((*q == 'Z') || (*q == 'z')) working->flags |= CE_COMPACT; @@ -517,7 +540,7 @@ static void dotrim(log,pid_file,numdays,flags,perm,owner_uid,group_gid,sig) err(1, "can't add status message to log"); } if (noaction) - printf("chmod %o %s...",perm,log); + printf("chmod %o %s...\n", perm, log); else (void) chmod(log,perm); @@ -583,8 +606,8 @@ static void compress_log(log) if (pid < 0) err(1, "fork"); else if (!pid) { - (void) execl(COMPRESS_PATH,COMPRESS_PROG,"-f",tmp,0); - err(1, COMPRESS_PATH); + (void) execl(_PATH_GZIP, _PATH_GZIP, "-f", tmp, 0); + err(1, _PATH_GZIP); } } @@ -638,20 +661,6 @@ static pid_t get_pid(pid_file) return pid; } -#ifndef OSF -/* Duplicate a string using malloc */ - -char *strdup(strp) -register char *strp; -{ - register char *cp; - - if ((cp = malloc((unsigned) strlen(strp) + 1)) == NULL) - abort(); - return(strcpy (cp, strp)); -} -#endif - /* Skip Over Blanks */ char *sob(p) register char *p; @@ -669,3 +678,90 @@ char *son(p) p++; return(p); } + +/* + * 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. + */ +static time_t +parse8601(const char *s) +{ + char *t; + struct tm tm, *tmp; + u_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 - (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); +} + + diff --git a/usr.sbin/newsyslog/pathnames.h b/usr.sbin/newsyslog/pathnames.h new file mode 100644 index 0000000..2eb271d --- /dev/null +++ b/usr.sbin/newsyslog/pathnames.h @@ -0,0 +1,27 @@ +/* + * This file contains changes from the Open Software Foundation. + */ + +/* + +Copyright 1988, 1989 by the Massachusetts Institute of Technology + +Permission to use, copy, modify, and distribute this software +and its documentation for any purpose and without fee is +hereby granted, provided that the above copyright notice +appear in all copies and that both that copyright notice and +this permission notice appear in supporting documentation, +and that the names of M.I.T. and the M.I.T. S.I.P.B. not be +used in advertising or publicity pertaining to distribution +of the software without specific, written prior permission. +M.I.T. and the M.I.T. S.I.P.B. make no representations about +the suitability of this software for any purpose. It is +provided "as is" without express or implied warranty. + + $Id$ + +*/ + +#define _PATH_CONF "/etc/newsyslog.conf" +#define _PATH_SYSLOGPID _PATH_VARRUN "syslog.pid" +#define _PATH_GZIP "/usr/bin/gzip" -- cgit v1.1