From owner-svn-src-all@FreeBSD.ORG Thu Jul 22 11:23:18 2010 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id A60D0106564A; Thu, 22 Jul 2010 11:23:18 +0000 (UTC) (envelope-from simon@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id 8AF4B8FC17; Thu, 22 Jul 2010 11:23:18 +0000 (UTC) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.3/8.14.3) with ESMTP id o6MBNIsJ030866; Thu, 22 Jul 2010 11:23:18 GMT (envelope-from simon@svn.freebsd.org) Received: (from simon@localhost) by svn.freebsd.org (8.14.3/8.14.3/Submit) id o6MBNIl8030833; Thu, 22 Jul 2010 11:23:18 GMT (envelope-from simon@svn.freebsd.org) Message-Id: <201007221123.o6MBNIl8030833@svn.freebsd.org> From: "Simon L. Nielsen" Date: Thu, 22 Jul 2010 11:23:18 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r210372 - head/usr.sbin/newsyslog X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 22 Jul 2010 11:23:18 -0000 Author: simon Date: Thu Jul 22 11:23:18 2010 New Revision: 210372 URL: http://svn.freebsd.org/changeset/base/210372 Log: Add support for creating the archived log filenames using a time-stamp instead of the traditional simple counter. Using the time-stamp based file-names, once a log file is archived, it will not change name until it is deleted. This means that many backup systems will only perform one backup of the archived log file, instead for performing a new backup of the logfile upon each logfile rotation. This implementation is separate from the patches in the mentioned PR, as I wasn't aware of the existence of the PR until after I had implemented the same functionality as the patches in the PR provide. Unlike the PR, this new code does honor the 'log count' in newsyslog.conf so old logfiles are deleted. This new code does not currently support never deleting the archived logfiles. PR: bin/29363 MFC after: 3 weeks Modified: head/usr.sbin/newsyslog/newsyslog.8 head/usr.sbin/newsyslog/newsyslog.c Modified: head/usr.sbin/newsyslog/newsyslog.8 ============================================================================== --- head/usr.sbin/newsyslog/newsyslog.8 Thu Jul 22 10:24:28 2010 (r210371) +++ head/usr.sbin/newsyslog/newsyslog.8 Thu Jul 22 11:23:18 2010 (r210372) @@ -30,6 +30,7 @@ .Op Fl a Ar directory .Op Fl d Ar directory .Op Fl f Ar config_file +.Op Fl t Ar timefmt .Op Ar .Sh DESCRIPTION The @@ -50,6 +51,11 @@ 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. +It is also possible to let archived log filenames be created using the +time the log file was archived instead of the sequential number using +the +.Fl t +option. Optionally the archived logs can be compressed to save space. .Pp @@ -141,6 +147,31 @@ However, this option is most likely to b with the .Fl R option, and in that case the compression will be done. +.It Fl t Ar timefmt +If specified +.Nm +will create the +.Dq rotated +logfiles using the specified time format instead of the default +sequential filenames. +The time format is described in the +.Xr strftime 3 +manual page. +If the +.Ar timefmt +argument is set to an empty string or the string +.Dq DEFAULT , +the default built in time format +is used. +If the +.Ar timefmt +string is changed the old files created using the previous time format +will not be be automatically removed (unless the new format is very +similar to the old format). +This is also the case when changing from sequential filenames to time +based file names, and the other way around. +The time format should contain at least year, month, day, and hour to +make sure rotating of old logfiles can select the correct logfiles. .It Fl C If specified once, then .Nm Modified: head/usr.sbin/newsyslog/newsyslog.c ============================================================================== --- head/usr.sbin/newsyslog/newsyslog.c Thu Jul 22 10:24:28 2010 (r210371) +++ head/usr.sbin/newsyslog/newsyslog.c Thu Jul 22 11:23:18 2010 (r210372) @@ -69,9 +69,11 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include +#include #include #include #include @@ -80,6 +82,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -112,6 +115,9 @@ __FBSDID("$FreeBSD$"); #define DEFAULT_MARKER "" #define DEBUG_MARKER "" #define INCLUDE_MARKER "" +#define DEFAULT_TIMEFNAME_FMT "%Y%m%dT%H%M%S" + +#define MAX_OLDLOGS 65536 /* Default maximum number of old logfiles */ struct conf_entry { STAILQ_ENTRY(conf_entry) cf_nextp; @@ -155,6 +161,11 @@ struct include_entry { const char *file; /* Name of file to process */ }; +struct oldlog_entry { + char *fname; /* Filename of the log file */ + time_t t; /* Parses timestamp of the logfile */ +}; + typedef enum { FREE_ENT, KEEP_ENT } fk_entry; @@ -182,6 +193,7 @@ int rotatereq = 0; /* -R = Always rotat /* that a list of files *are* given on */ /* the run command). */ char *requestor; /* The name given on a -R request */ +char *timefnamefmt = NULL; /* Use time based filenames instead of .0 etc */ char *archdirname; /* Directory path to old logfiles archive */ char *destdir = NULL; /* Directory to treat at root for logs */ const char *conf; /* Configuration file to use */ @@ -585,7 +597,7 @@ parse_args(int argc, char **argv) *p = '\0'; /* Parse command line options. */ - while ((ch = getopt(argc, argv, "a:d:f:nrsvCD:FNPR:")) != -1) + while ((ch = getopt(argc, argv, "a:d:f:nrst:vCD:FNPR:")) != -1) switch (ch) { case 'a': archtodir++; @@ -606,6 +618,13 @@ parse_args(int argc, char **argv) case 's': nosignal = 1; break; + case 't': + if (optarg[0] == '\0' || + strcmp(optarg, "DEFAULT") == 0) + timefnamefmt = strdup(DEFAULT_TIMEFNAME_FMT); + else + timefnamefmt = strdup(optarg); + break; case 'v': verbose++; break; @@ -728,7 +747,7 @@ usage(void) fprintf(stderr, "usage: newsyslog [-CFNnrsv] [-a directory] [-d directory] [-f config-file]\n" - " [ [-R requestor] filename ... ]\n"); + " [-t timefmt ] [ [-R requestor] filename ... ]\n"); exit(1); } @@ -1365,6 +1384,177 @@ missing_field(char *p, char *errline) } /* + * In our sort we return it in the reverse of what qsort normally + * would do, as we want the newest files first. If we have two + * entries with the same time we don't really care about order. + * + * Support function for qsort() in delete_oldest_timelog(). + */ +static int +oldlog_entry_compare(const void *a, const void *b) +{ + const struct oldlog_entry *ola = a, *olb = b; + + if (ola->t > olb->t) + return (-1); + else if (ola->t < olb->t) + return (1); + else + return (0); +} + +/* + * Delete the oldest logfiles, when using time based filenames. + */ +static void +delete_oldest_timelog(const struct conf_entry *ent, const char *archive_dir) +{ + char *logfname, *s, *dir, errbuf[80]; + int logcnt, max_logcnt, dirfd, i; + struct oldlog_entry *oldlogs; + size_t logfname_len; + struct dirent *dp; + const char *cdir; + struct tm tm; + DIR *dirp; + + oldlogs = malloc(MAX_OLDLOGS * sizeof(struct oldlog_entry)); + max_logcnt = MAX_OLDLOGS; + logcnt = 0; + + if (archive_dir != NULL && archive_dir[0] != '\0') + cdir = archive_dir; + else + if ((cdir = dirname(ent->log)) == NULL) + err(1, "dirname()"); + if ((dir = strdup(cdir)) == NULL) + err(1, "strdup()"); + + if ((s = basename(ent->log)) == NULL) + err(1, "basename()"); + if ((logfname = strdup(s)) == NULL) + err(1, "strdup()"); + logfname_len = strlen(logfname); + if (strcmp(logfname, "/") == 0) + errx(1, "Invalid log filename - became '/'"); + + if (verbose > 2) + printf("Searching for old logs in %s\n", dir); + + /* First we create a 'list' of all archived logfiles */ + if ((dirp = opendir(dir)) == NULL) + err(1, "Cannot open log directory '%s'", dir); + dirfd = dirfd(dirp); + while ((dp = readdir(dirp)) != NULL) { + if (dp->d_type != DT_REG) + continue; + + /* Ignore everything but files with our logfile prefix */ + if (strncmp(dp->d_name, logfname, logfname_len) != 0) + continue; + /* Ignore the actual non-rotated logfile */ + if (dp->d_namlen == logfname_len) + continue; + /* + * Make sure we created have found a logfile, so the + * postfix is valid, IE format is: '.