Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 21 Nov 2010 13:20:15 +0100
From:      Oliver Pinter <oliver.pntr@gmail.com>
To:        "Simon L. Nielsen" <simon@freebsd.org>
Cc:        svn-src-stable@freebsd.org, svn-src-all@freebsd.org, src-committers@freebsd.org, svn-src-stable-8@freebsd.org
Subject:   Re: svn commit: r215624 - stable/8/usr.sbin/newsyslog
Message-ID:  <AANLkTikuYNBre%2BXfkqOV0fwPGFPSMJ2MuELziN=3z%2Byo@mail.gmail.com>
In-Reply-To: <201011211110.oALBAA18053112@svn.freebsd.org>
References:  <201011211110.oALBAA18053112@svn.freebsd.org>

next in thread | previous in thread | raw e-mail | index | archive | help
Hi!

MFC or MFS to 7-STABLE this patch?

On 11/21/10, Simon L. Nielsen <simon@freebsd.org> wrote:
> Author: simon
> Date: Sun Nov 21 11:10:09 2010
> New Revision: 215624
> URL: http://svn.freebsd.org/changeset/base/215624
>
> Log:
>   MFC r210372:
>
>   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.
>
> Modified:
>   stable/8/usr.sbin/newsyslog/newsyslog.8
>   stable/8/usr.sbin/newsyslog/newsyslog.c
> Directory Properties:
>   stable/8/usr.sbin/newsyslog/   (props changed)
>
> Modified: stable/8/usr.sbin/newsyslog/newsyslog.8
> ==============================================================================
> --- stable/8/usr.sbin/newsyslog/newsyslog.8	Sun Nov 21 11:08:22
> 2010	(r215623)
> +++ stable/8/usr.sbin/newsyslog/newsyslog.8	Sun Nov 21 11:10:09
> 2010	(r215624)
> @@ -31,6 +31,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
> @@ -51,6 +52,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
> @@ -142,6 +148,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: stable/8/usr.sbin/newsyslog/newsyslog.c
> ==============================================================================
> --- stable/8/usr.sbin/newsyslog/newsyslog.c	Sun Nov 21 11:08:22
> 2010	(r215623)
> +++ stable/8/usr.sbin/newsyslog/newsyslog.c	Sun Nov 21 11:10:09
> 2010	(r215624)
> @@ -69,9 +69,11 @@ __FBSDID("$FreeBSD$");
>  #include <sys/stat.h>
>  #include <sys/wait.h>
>
> +#include <assert.h>
>  #include <ctype.h>
>  #include <err.h>
>  #include <errno.h>
> +#include <dirent.h>
>  #include <fcntl.h>
>  #include <fnmatch.h>
>  #include <glob.h>
> @@ -80,6 +82,7 @@ __FBSDID("$FreeBSD$");
>  #include <pwd.h>
>  #include <signal.h>
>  #include <stdio.h>
> +#include <libgen.h>
>  #include <stdlib.h>
>  #include <string.h>
>  #include <time.h>
> @@ -112,6 +115,9 @@ __FBSDID("$FreeBSD$");
>  #define	DEFAULT_MARKER	"<default>"
>  #define	DEBUG_MARKER	"<debug>"
>  #define	INCLUDE_MARKER	"<include>"
> +#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 */
> @@ -587,7 +599,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++;
> @@ -608,6 +620,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;
> @@ -733,7 +752,7 @@ usage(void)
>
>  	fprintf(stderr,
>  	    "usage: newsyslog [-CFNnrsv] [-a directory] [-d directory] [-f
> config-file]\n"
> -	    "                 [-S pidfile] [ [-R requestor] filename ... ]\n");
> +	    "                 [-S pidfile] [-t timefmt ] [ [-R requestor] filename
> ... ]\n");
>  	exit(1);
>  }
>
> @@ -1370,6 +1389,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: '.<time>(.[bg]z)?'.
> +		 */
> +		if (dp->d_name[logfname_len] != '.') {
> +			if (verbose)
> +				printf("Ignoring %s which has unexpected "
> +				    "extension '%s'\n", dp->d_name,
> +				    &dp->d_name[logfname_len]);
> +			continue;
> +		}
> +		if ((s = strptime(&dp->d_name[logfname_len + 1],
> +			    timefnamefmt, &tm)) == NULL) {
> +			/*
> +			 * We could special case "old" sequentially
> +			 * named logfiles here, but we do not as that
> +			 * would require special handling to decide
> +			 * which one was the oldest compared to "new"
> +			 * time based logfiles.
> +			 */
> +			if (verbose)
> +				printf("Ignoring %s which does not "
> +				    "match time format\n", dp->d_name);
> +			continue;
> +		}
> +		if (*s != '\0' && !(strcmp(s, BZCOMPRESS_POSTFIX) == 0 ||
> +			strcmp(s, COMPRESS_POSTFIX) == 0))  {
> +			    if (verbose)
> +				printf("Ignoring %s which has unexpected "
> +				    "extension '%s'\n", dp->d_name, s);
> +			continue;
> +		}
> +
> +		/*
> +		 * We should now have old an old rotated logfile, so
> +		 * add it to the 'list'.
> +		 */
> +		if ((oldlogs[logcnt].t = timegm(&tm)) == -1)
> +			err(1, "Could not convert time string to time value");
> +		if ((oldlogs[logcnt].fname = strdup(dp->d_name)) == NULL)
> +			err(1, "strdup()");
> +		logcnt++;
> +
> +		/*
> +		 * It is very unlikely we ever run out of space in the
> +		 * logfile array from the default size, but lets
> +		 * handle it anyway...
> +		 */
> +		if (logcnt >= max_logcnt) {
> +			max_logcnt *= 4;
> +			/* Detect integer overflow */
> +			if (max_logcnt < logcnt)
> +				errx(1, "Too many old logfiles found");
> +			oldlogs = realloc(oldlogs,
> +			    max_logcnt * sizeof(struct oldlog_entry));
> +			if (oldlogs == NULL)
> +				err(1, "realloc()");
> +		}
> +	}
> +
> +	/* Second, if needed we delete oldest archived logfiles */
> +	if (logcnt > 0 && logcnt >= ent->numlogs && ent->numlogs > 1) {
> +		oldlogs = realloc(oldlogs, logcnt *
> +		    sizeof(struct oldlog_entry));
> +		if (oldlogs == NULL)
> +			err(1, "realloc()");
> +
> +		/*
> +		 * We now sort the logs in the order of newest to
> +		 * oldest.  That way we can simply skip over the
> +		 * number of records we want to keep.
> +		 */
> +		qsort(oldlogs, logcnt, sizeof(struct oldlog_entry),
> +		    oldlog_entry_compare);
> +		for (i = ent->numlogs - 1; i < logcnt; i++) {
> +			if (noaction)
> +				printf("\trm -f %s/%s\n", dir,
> +				    oldlogs[i].fname);
> +			else if (unlinkat(dirfd, oldlogs[i].fname, 0) != 0) {
> +				snprintf(errbuf, sizeof(errbuf),
> +				    "Could not delet old logfile '%s'",
> +				    oldlogs[i].fname);
> +				perror(errbuf);
> +			}
> +		}
> +	} else if (verbose > 1)
> +		printf("No old logs to delete for logfile %s\n", ent->log);
> +
> +	/* Third, cleanup */
> +	closedir(dirp);
> +	for (i = 0; i < logcnt; i++) {
> +		assert(oldlogs[i].fname != NULL);
> +		free(oldlogs[i].fname);
> +	}
> +	free(oldlogs);
> +	free(logfname);
> +	free(dir);
> +}
> +
> +/*
>   * Only add to the queue if the file hasn't already been added. This is
>   * done to prevent circular include loops.
>   */
> @@ -1403,10 +1593,13 @@ do_rotate(const struct conf_entry *ent)
>  	char file1[MAXPATHLEN], file2[MAXPATHLEN];
>  	char zfile1[MAXPATHLEN], zfile2[MAXPATHLEN];
>  	char jfile1[MAXPATHLEN];
> +	char datetimestr[30];
>  	int flags, numlogs_c;
>  	fk_entry free_or_keep;
>  	struct sigwork_entry *swork;
>  	struct stat st;
> +	struct tm tm;
> +	time_t now;
>
>  	flags = ent->flags;
>  	free_or_keep = FREE_ENT;
> @@ -1440,32 +1633,59 @@ do_rotate(const struct conf_entry *ent)
>  		/* name of oldest log */
>  		(void) snprintf(file1, sizeof(file1), "%s/%s.%d", dirpart,
>  		    namepart, ent->numlogs);
> -		(void) snprintf(zfile1, sizeof(zfile1), "%s%s", file1,
> -		    COMPRESS_POSTFIX);
> -		snprintf(jfile1, sizeof(jfile1), "%s%s", file1,
> -		    BZCOMPRESS_POSTFIX);
>  	} else {
> +		/*
> +		 * Tell delete_oldest_timelog() we are not using an
> +		 * archive dir.
> +		 */
> +		dirpart[0] = '\0';
> +
>  		/* name of oldest log */
>  		(void) snprintf(file1, sizeof(file1), "%s.%d", ent->log,
>  		    ent->numlogs);
> +	}
> +
> +	/* Delete old logs */
> +	if (timefnamefmt != NULL)
> +		delete_oldest_timelog(ent, dirpart);
> +	else {
> +		/* name of oldest log */
>  		(void) snprintf(zfile1, sizeof(zfile1), "%s%s", file1,
>  		    COMPRESS_POSTFIX);
>  		snprintf(jfile1, sizeof(jfile1), "%s%s", file1,
>  		    BZCOMPRESS_POSTFIX);
> -	}
>
> -	if (noaction) {
> -		printf("\trm -f %s\n", file1);
> -		printf("\trm -f %s\n", zfile1);
> -		printf("\trm -f %s\n", jfile1);
> -	} else {
> -		(void) unlink(file1);
> -		(void) unlink(zfile1);
> -		(void) unlink(jfile1);
> +		if (noaction) {
> +			printf("\trm -f %s\n", file1);
> +			printf("\trm -f %s\n", zfile1);
> +			printf("\trm -f %s\n", jfile1);
> +		} else {
> +			(void) unlink(file1);
> +			(void) unlink(zfile1);
> +			(void) unlink(jfile1);
> +		}
>  	}
>
> +	if (timefnamefmt != NULL) {
> +		/* If time functions fails we can't really do any sensible */
> +		if (time(&now) == (time_t)-1 ||
> +		    localtime_r(&now, &tm) == NULL)
> +			bzero(&tm, sizeof(tm));
> +
> +		strftime(datetimestr, sizeof(datetimestr), timefnamefmt, &tm);
> +		if (archtodir)
> +			(void) snprintf(file1, sizeof(file1), "%s/%s.%s",
> +			    dirpart, namepart, datetimestr);
> +		else
> +			(void) snprintf(file1, sizeof(file1), "%s.%s",
> +			    ent->log, datetimestr);
> +
> +		/* Don't run the code to move down logs */
> +		numlogs_c = 0;
> +	} else
> +		numlogs_c = ent->numlogs;		/* copy for countdown */
> +
>  	/* Move down log files */
> -	numlogs_c = ent->numlogs;		/* copy for countdown */
>  	while (numlogs_c--) {
>
>  		(void) strlcpy(file2, file1, sizeof(file2));
> _______________________________________________
> svn-src-stable@freebsd.org mailing list
> http://lists.freebsd.org/mailman/listinfo/svn-src-stable
> To unsubscribe, send any mail to "svn-src-stable-unsubscribe@freebsd.org"
>



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?AANLkTikuYNBre%2BXfkqOV0fwPGFPSMJ2MuELziN=3z%2Byo>