[logrotate] - fix #661181 - fixed SIGBUS when config file is empty or 4096 bytes - fix #666677 - preserve ACLs w

Jan Kaluža jkaluza at fedoraproject.org
Wed Jan 5 10:47:56 UTC 2011


commit 3ad5b60452a43a5f8154b7ee067d4c83aa17b73e
Author: Jan Kaluza <hanzz.k at gmail.com>
Date:   Wed Jan 5 11:47:45 2011 +0100

    - fix #661181 - fixed SIGBUS when config file is empty or 4096 bytes
    - fix #666677 - preserve ACLs when rotating files

 logrotate-3.7.9-acl.patch    |  161 ++++
 logrotate-3.7.9-config.patch | 2024 ++++++++++++++++++++++++++++++++++++++++++
 logrotate.spec               |   16 +-
 3 files changed, 2197 insertions(+), 4 deletions(-)
---
diff --git a/logrotate-3.7.9-acl.patch b/logrotate-3.7.9-acl.patch
new file mode 100644
index 0000000..63743a8
--- /dev/null
+++ b/logrotate-3.7.9-acl.patch
@@ -0,0 +1,161 @@
+Index: /trunk/logrotate.c
+===================================================================
+--- /trunk/logrotate.c (revision 296)
++++ /trunk/logrotate.c (revision 299)
+@@ -33,4 +33,9 @@
+ #endif
+ 
++#ifdef WITH_ACL
++#include "sys/acl.h"
++static acl_t prev_acl = NULL;
++#endif
++
+ #include "basenames.h"
+ #include "log.h"
+@@ -317,4 +322,29 @@
+ 	return 1;
+     }
++
++#ifdef WITH_ACL
++	if ((prev_acl = acl_get_fd(inFile)) == NULL) {
++		if (errno != ENOTSUP) {
++			message(MESS_ERROR, "getting file ACL %s: %s\n",
++				name, strerror(errno));
++			close(inFile);
++			close(outFile);
++			return 1;
++		}
++	}
++	if (prev_acl) {
++		if (acl_set_fd(outFile, prev_acl) == -1) {
++			message(MESS_ERROR, "setting ACL for %s: %s\n",
++			compressedName, strerror(errno));
++			acl_free(prev_acl);
++			prev_acl = NULL;
++			close(inFile);
++			close(outFile);
++			return 1;
++		}
++		acl_free(prev_acl);
++		prev_acl = NULL;
++	}
++#endif /* WITH_ACL */
+ 
+     if (!fork()) {
+@@ -490,4 +520,14 @@
+ 	}
+ #endif
++#ifdef WITH_ACL
++	if ((prev_acl = acl_get_fd(fdcurr)) == NULL) {
++		if (errno != ENOTSUP) {
++			message(MESS_ERROR, "getting file ACL %s: %s\n",
++				currLog, strerror(errno));
++			close(fdcurr);
++			return 1;
++		}
++	}
++#endif /* WITH_ACL */
+ 	fdsave =
+ 	    createOutputFile(saveLog, O_WRONLY | O_CREAT | O_TRUNC, sb);
+@@ -501,6 +541,26 @@
+ 	if (fdsave < 0) {
+ 	    close(fdcurr);
++#ifdef WITH_ACL
++		if (prev_acl)
++			acl_free(prev_acl);
++#endif /* WITH_ACL */
+ 	    return 1;
+ 	}
++#ifdef WITH_ACL
++	if (prev_acl) {
++		if (acl_set_fd(fdsave, prev_acl) == -1) {
++			message(MESS_ERROR, "setting ACL for %s: %s\n",
++			saveLog, strerror(errno));
++			acl_free(prev_acl);
++			prev_acl = NULL;
++			close(fdsave);
++			close(fdcurr);
++			return 1;
++		}
++		acl_free(prev_acl);
++		prev_acl = NULL;
++	}
++#endif /* WITH_ACL */
++
+ 	while ((cnt = read(fdcurr, buf, sizeof(buf))) > 0) {
+ 	    if (write(fdsave, buf, cnt) != cnt) {
+@@ -1087,4 +1147,13 @@
+ 		}
+ #endif
++#ifdef WITH_ACL
++		if ((prev_acl = acl_get_file(log->files[logNum], ACL_TYPE_ACCESS)) == NULL) {
++			if (errno != ENOTSUP) {
++				message(MESS_ERROR, "getting file ACL %s: %s\n",
++					log->files[logNum], strerror(errno));
++				hasErrors = 1;
++			}
++		}
++#endif /* WITH_ACL */
+ 		message(MESS_DEBUG, "renaming %s to %s\n", log->files[logNum],
+ 		    rotNames->finalName);
+@@ -1134,10 +1203,33 @@
+ 
+ 	    if (!debug) {
++#ifdef WITH_ACL
++			if (prev_acl == NULL && (prev_acl = acl_get_file(log->files[logNum], ACL_TYPE_ACCESS)) == NULL) {
++				if (errno != ENOTSUP) {
++					message(MESS_ERROR, "getting file ACL %s: %s\n",
++						log->files[logNum], strerror(errno));
++					hasErrors = 1;
++				}
++			}
++#endif /* WITH_ACL */
++			if (!hasErrors) {
+ 			fd = createOutputFile(log->files[logNum], O_CREAT | O_RDWR,
+ 						  &sb);
+ 			if (fd < 0)
+ 				hasErrors = 1;
+-			else
++			else {
++#ifdef WITH_ACL
++				if (prev_acl) {
++					if (acl_set_fd(fd, prev_acl) == -1) {
++						message(MESS_ERROR, "setting ACL for %s: %s\n",
++						log->files[logNum], strerror(errno));
++						hasErrors = 1;
++					}
++					acl_free(prev_acl);
++					prev_acl = NULL;
++				}
++#endif /* WITH_ACL */
+ 				close(fd);
++			}
++			}
+ 	    }
+ 	}
+@@ -1156,4 +1248,11 @@
+ 			     &state->sb, log->flags);
+ 
++#ifdef WITH_ACL
++	if (prev_acl) {
++		acl_free(prev_acl);
++		prev_acl = NULL;
++	}
++#endif /* WITH_ACL */
++		
+     }
+     return hasErrors;
+Index: /trunk/Makefile
+===================================================================
+--- /trunk/Makefile (revision 296)
++++ /trunk/Makefile (revision 299)
+@@ -14,4 +14,9 @@
+ CFLAGS += -DWITH_SELINUX
+ LOADLIBES += -lselinux
++endif
++
++ifeq ($(WITH_ACL),yes)
++CFLAGS += -DWITH_ACL
++LOADLIBES += -lacl
+ endif
+ 
diff --git a/logrotate-3.7.9-config.patch b/logrotate-3.7.9-config.patch
new file mode 100644
index 0000000..5fc0362
--- /dev/null
+++ b/logrotate-3.7.9-config.patch
@@ -0,0 +1,2024 @@
+Index: config.c
+===================================================================
+--- config.c	(revision 290)
++++ config.c	(working copy)
+@@ -1,5 +1,9 @@
+ #include <sys/queue.h>
++#ifdef _ALLOCA_H
+ #include <alloca.h>
++#else
++#include <limits.h>
++#endif
+ #include <ctype.h>
+ #include <dirent.h>
+ #include <errno.h>
+@@ -30,13 +34,55 @@
+ 
+ #define REALLOC_STEP    10
+ 
+-#if defined(SunOS) 
+-#include <syslimits.h>
++#if defined(SunOS)
++#include <limits.h>
+ #if !defined(isblank)
+ #define isblank(c) 	( (c) == ' ' || (c) == '\t' ) ? 1 : 0
+ #endif
+ #endif
+ 
++#if !defined(asprintf)
++#include <stdarg.h>
++
++int asprintf(char **string_ptr, const char *format, ...)
++{
++	va_list arg;
++	char *str;
++	int size;
++	int rv;
++
++	va_start(arg, format);
++	size = vsnprintf(NULL, 0, format, arg);
++	size++;
++	va_start(arg, format);
++	str = malloc(size);
++	if (str == NULL) {
++		va_end(arg);
++		/*
++		 * Strictly speaking, GNU asprintf doesn't do this,
++		 * but the caller isn't checking the return value.
++		 */
++		fprintf(stderr, "failed to allocate memory\\n");
++		exit(1);
++	}
++	rv = vsnprintf(str, size, format, arg);
++	va_end(arg);
++
++	*string_ptr = str;
++	return (rv);
++}
++
++#endif
++
++enum {
++	STATE_DEFAULT = 2,
++	STATE_SKIP_LINE = 4,
++	STATE_DEFINITION_END = 8,
++	STATE_SKIP_CONFIG = 16,
++	STATE_LOAD_SCRIPT = 32,
++	STATE_ERROR = 64,
++};
++
+ static char *defTabooExts[] = { ".rpmsave", ".rpmorig", "~", ",v",
+     ".disabled", ".dpkg-old", ".dpkg-dist", ".dpkg-new", ".cfsaved",
+     ".ucf-old", ".ucf-dist", ".ucf-new",
+@@ -52,51 +98,71 @@
+ static int readConfigFile(const char *configFile, struct logInfo *defConfig);
+ static int globerr(const char *pathname, int theerr);
+ 
+-static int isolateValue(const char *fileName, int lineNum, char *key,
+-			char **startPtr, char **endPtr)
++static char *isolateLine(char **strt, char **buf, size_t length) {
++	char *endtag, *start, *tmp;
++	start = *strt;
++	endtag = start;
++	while (endtag - *buf < length && *endtag != '\n') {
++		endtag++;}
++	if (endtag - *buf > length)
++		return NULL;
++	tmp = endtag - 1;
++	while (isspace(*endtag))
++		endtag--;
++	char *key = strndup(start, endtag - start + 1);
++	*strt = tmp;
++	return key;
++}
++
++static char *isolateValue(const char *fileName, int lineNum, char *key,
++			char **startPtr, char **buf, size_t length)
+ {
+     char *chptr = *startPtr;
+ 
+-    while (isblank(*chptr))
++    while (chptr - *buf < length && isblank(*chptr))
+ 	chptr++;
+-    if (*chptr == '=') {
++    if (chptr - *buf < length && *chptr == '=') {
+ 	chptr++;
+-	while (*chptr && isblank(*chptr))
++	while ( chptr - *buf < length && isblank(*chptr))
+ 	    chptr++;
+     }
+ 
+-    if (*chptr == '\n') {
+-	message(MESS_ERROR, "%s:%d argument expected after %s\n",
+-		fileName, lineNum, key);
+-	return 1;
++    if (chptr - *buf < length && *chptr == '\n') {
++		message(MESS_ERROR, "%s:%d argument expected after %s\n",
++			fileName, lineNum, key);
++		return NULL;
+     }
+ 
+-    *startPtr = chptr;
++	*startPtr = chptr;
++	return isolateLine(startPtr, buf, length);
++}
+ 
+-    while (*chptr != '\n')
+-	chptr++;
+-
+-    while (isspace(*chptr))
+-	chptr--;
+-
+-    *endPtr = chptr + 1;
+-
+-    return 0;
++static char *isolateWord(char **strt, char **buf, size_t length) {
++	char *endtag, *start;
++	start = *strt;
++	while (start - *buf < length && isblank(*start))
++		start++;
++	endtag = start;
++	while (endtag - *buf < length && isalpha(*endtag)) {
++		endtag++;}
++	if (endtag - *buf > length)
++		return NULL;
++	char *key = strndup(start, endtag - start);
++	*strt = endtag;
++	return key;
+ }
+ 
+ static char *readPath(const char *configFile, int lineNum, char *key,
+-		      char **startPtr)
++		      char **startPtr, char **buf, size_t length)
+ {
+-    char oldchar;
+-    char *endtag, *chptr;
++    char *chptr;
+     char *start = *startPtr;
+     char *path;
+ 
+     wchar_t pwc;
+     size_t len;
+ 
+-    if (!isolateValue(configFile, lineNum, key, &start, &endtag)) {
+-	oldchar = *endtag, *endtag = '\0';
++    if ((start = isolateValue(configFile, lineNum, key, startPtr, buf, length)) != NULL) {
+ 
+ 	chptr = start;
+ 
+@@ -120,29 +186,23 @@
+ */
+ 
+ 	path = strdup(start);
++	free(start);
+ 
+-
+-	*endtag = oldchar, start = endtag;
+-
+-	*startPtr = start;
+-
+ 	return path;
+     } else
+ 	return NULL;
+ }
+ 
+ static char *readAddress(const char *configFile, int lineNum, char *key,
+-			 char **startPtr)
++			 char **startPtr, char **buf, size_t length)
+ {
+-    char oldchar;
+     char *endtag, *chptr;
+     char *start = *startPtr;
+     char *address;
+ 
+-    if (!isolateValue(configFile, lineNum, key, &start, &endtag)) {
+-	oldchar = *endtag, *endtag = '\0';
++    if ((endtag = isolateValue(configFile, lineNum, key, startPtr, buf, length)) != NULL) {
+ 
+-	chptr = start;
++	chptr = endtag;
+ 	while (*chptr && isprint(*chptr) && *chptr != ' ')
+ 	    chptr++;
+ 	if (*chptr) {
+@@ -151,12 +211,9 @@
+ 	    return NULL;
+ 	}
+ 
+-	address = strdup(start);
++	address = strdup(chptr);
++	free(endtag);
+ 
+-	*endtag = oldchar, start = endtag;
+-
+-	*startPtr = start;
+-
+ 	return address;
+     } else
+ 	return NULL;
+@@ -484,8 +541,8 @@
+ static int readConfigFile(const char *configFile, struct logInfo *defConfig)
+ {
+     int fd;
+-    char *buf, *endtag;
+-    char oldchar, foo;
++    char *buf, *endtag, *key = NULL;
++    char foo;
+     off_t length;
+     int lineNum = 1;
+     int multiplier;
+@@ -504,6 +561,8 @@
+     glob_t globResult;
+     const char **argv;
+     int argc, argNum;
++	int flags;
++	int state = STATE_DEFAULT;
+     int logerror = 0;
+     struct logInfo *log;
+ 	static unsigned recursion_depth = 0U;
+@@ -519,17 +578,28 @@
+        length arrays -- of course, if we aren't run setuid it doesn't
+        matter much */
+ 
+-    fd = open(configFile, O_RDONLY | O_CLOEXEC);
+-    if (fd < 0) {
+-	message(MESS_ERROR, "failed to open config file %s: %s\n",
+-		configFile, strerror(errno));
+-	return 1;
+-    }
++	fd = open(configFile, O_RDONLY);
++	if (fd < 0) {
++		message(MESS_ERROR, "failed to open config file %s: %s\n",
++			configFile, strerror(errno));
++		return 1;
++	}
++	if ((flags = fcntl(fd, F_GETFD)) == -1) {
++		message(MESS_ERROR, "Could not retrieve flags from file %s\n",
++				configFile);
++		return 1;
++	}
++	flags |= FD_CLOEXEC;
++	if (fcntl(fd, F_SETFD, flags) == -1) {
++		message(MESS_ERROR, "Could not set flags on file %s\n",
++				configFile);
++		return 1;
++	}
+ 	/* We don't want anybody to change the file while we parse it,
+ 	 * let's try to lock it for reading. */
+ 	if (fcntl(fd, F_SETLK, &fd_lock) == -1) {
+-		message(MESS_ERROR, "Could not lock file %s for reading\n",
+-				configFile);
++	message(MESS_ERROR, "Could not lock file %s for reading\n",
++			configFile);
+ 	}
+     if (fstat(fd, &sb)) {
+ 	message(MESS_ERROR, "fstat of %s failed: %s\n", configFile,
+@@ -546,8 +616,24 @@
+     }
+ 
+ 	length = sb.st_size;
+-	buf = mmap(NULL, (size_t)(length + 2), PROT_READ | PROT_WRITE,
+-			MAP_PRIVATE | MAP_POPULATE, fd, (off_t) 0);
++
++	/* We can't mmap empty file... */
++	if (length == 0) {
++		message(MESS_DEBUG,
++			"Ignoring %s because it's empty.\n",
++			configFile);
++		close(fd);
++		return 0;
++	}
++
++#ifdef MAP_POPULATE
++ 	buf = mmap(NULL, (size_t) length, PROT_READ,
++ 			MAP_PRIVATE | MAP_POPULATE, fd, (off_t) 0);
++#else /* MAP_POPULATE */
++	buf = mmap(NULL, (size_t) length, PROT_READ,
++			MAP_PRIVATE, fd, (off_t) 0);
++#endif /* MAP_POPULATE */
++
+ 	if (buf == MAP_FAILED) {
+ 		message(MESS_ERROR, "Error mapping config file %s: %s\n",
+ 				configFile, strerror(errno));
+@@ -555,964 +641,812 @@
+ 		return 1;
+ 	}
+ 
+-	/* knowing the buffer ends with a newline makes things (a bit) cleaner */
+-	buf[length + 1] = '\0';
+-	buf[length] = '\n';
++#ifdef MADV_DONTFORK
+ 	madvise(buf, (size_t)(length + 2),
+ 			MADV_SEQUENTIAL | MADV_WILLNEED | MADV_DONTFORK);
++#else /* MADV_DONTFORK */
++	madvise(buf, (size_t)(length + 2),
++			MADV_SEQUENTIAL | MADV_WILLNEED);
++#endif /* MADV_DONTFORK */
+ 
+     message(MESS_DEBUG, "reading config file %s\n", configFile);
+ 
+-    start = buf;
+-    while (*start) {
+-	if (logerror) {
+-	    assert(newlog != defConfig);
+-
+-	    message(MESS_ERROR, "found error in %s, skipping\n",
+-		    newlog->pattern ? newlog->pattern : "log config");
+-
+-	    while (*start != '}') {
+-		if (*start == 0) {
+-		    message(MESS_ERROR, "%s:%d } expected \n",
+-			    configFile, lineNum);
+-		    goto error;
+-		} else if (*start == '\n') {
+-		    while (isspace(*start) && (*start)) {
+-			if (*start == '\n')
+-			    lineNum++;
+-			start++;
+-		    }
+-		} else if (
+-		    (strncmp(start, "postrotate", 10) == 0) ||
+-		    (strncmp(start, "prerotate", 9) == 0) ||
+-		    (strncmp(start, "firstrotate", 11) == 0) ||
+-		    (strncmp(start, "lastrotate", 10) == 0)
+-		    )
+-		{
+-		    while (*start) {
+-			while ((*start != '\n') && (*start))
+-			    start++;
+-			while (isspace(*start) && (*start)) {
+-			    if (*start == '\n')
+-				lineNum++;
+-			    start++;
++	start = buf;
++    for (start = buf; start - buf < length; start++) {
++	if (key) {
++		free(key);
++		key = NULL;
++	}
++	switch (state) {
++		case STATE_DEFAULT:
++			if (isblank(*start))
++				continue;
++			/* Skip comment */
++			if (*start == '#') {
++				state = STATE_SKIP_LINE;
++				continue;
+ 			}
+-			if (strncmp(start, "endscript", 9) == 0) {
+-			    start += 9;
+-			    break;
+-			}
+-		    }
+-		} else {
+-		    start++;
+-		}
+-	    }
+-	    start++;
++			
++			if (isalpha(*start)) {
++				if ((key = isolateWord(&start, &buf, length)) == NULL)
++					continue;
++				if (!strcmp(key, "compress")) {
++					newlog->flags |= LOG_FLAG_COMPRESS;
++				} else if (!strcmp(key, "nocompress")) {
++					newlog->flags &= ~LOG_FLAG_COMPRESS;
++				} else if (!strcmp(key, "compress")) {
++					newlog->flags |= LOG_FLAG_COMPRESS;
++				} else if (!strcmp(key, "nocompress")) {
++					newlog->flags &= ~LOG_FLAG_COMPRESS;
++				} else if (!strcmp(key, "delaycompress")) {
++					newlog->flags |= LOG_FLAG_DELAYCOMPRESS;
++				} else if (!strcmp(key, "nodelaycompress")) {
++					newlog->flags &= ~LOG_FLAG_DELAYCOMPRESS;
++				} else if (!strcmp(key, "shred")) {
++					newlog->flags |= LOG_FLAG_SHRED;
++				} else if (!strcmp(key, "noshred")) { 
++					newlog->flags &= ~LOG_FLAG_SHRED;
++				} else if (!strcmp(key, "sharedscripts")) {
++					newlog->flags |= LOG_FLAG_SHAREDSCRIPTS;
++				} else if (!strcmp(key, "nosharedscripts")) {
++					newlog->flags &= ~LOG_FLAG_SHAREDSCRIPTS;
++				} else if (!strcmp(key, "copytruncate")) {
++					newlog->flags |= LOG_FLAG_COPYTRUNCATE;
++				} else if (!strcmp(key, "nocopytruncate")) {
++					newlog->flags &= ~LOG_FLAG_COPYTRUNCATE;
++				} else if (!strcmp(key, "copy")) {
++					newlog->flags |= LOG_FLAG_COPY;
++				} else if (!strcmp(key, "nocopy")) {
++					newlog->flags &= ~LOG_FLAG_COPY;
++				} else if (!strcmp(key, "ifempty")) {
++					newlog->flags |= LOG_FLAG_IFEMPTY;
++				} else if (!strcmp(key, "notifempty")) {
++					newlog->flags &= ~LOG_FLAG_IFEMPTY;
++				} else if (!strcmp(key, "dateext")) {
++					newlog->flags |= LOG_FLAG_DATEEXT;
++				} else if (!strcmp(key, "nodateext")) {
++					newlog->flags &= ~LOG_FLAG_DATEEXT;
++				} else if (!strcmp(key, "dateformat")) {
++					freeLogItem(dateformat);
++					newlog->dateformat = isolateLine(&start, &buf, length);
++					if (newlog->dateformat == NULL)
++						continue;
++				} else if (!strcmp(key, "noolddir")) {
++					newlog->oldDir = NULL;
++				} else if (!strcmp(key, "mailfirst")) {
++					newlog->flags |= LOG_FLAG_MAILFIRST;
++				} else if (!strcmp(key, "maillast")) {
++					newlog->flags &= ~LOG_FLAG_MAILFIRST;
++				} else if (!strcmp(key, "create")) {
++					free(key);
++					key = isolateLine(&start, &buf, length);
++					if (key == NULL)
++						continue;
+ 
+-	    freeTailLogs(1);
+-	    newlog = defConfig;
+-	    logerror = 0;
+-	}				
+-	while (isblank(*start) && (*start))
+-	    start++;
+-	if (*start == '#') {
+-	    while (*start != '\n')
+-		start++;
+-	}
++					rc = sscanf(key, "%o %s %s%c", &createMode,
++							createOwner, createGroup, &foo);
++					if (rc == 4) {
++						message(MESS_ERROR, "%s:%d extra arguments for "
++							"create\n", configFile, lineNum);
++						if (newlog != defConfig) {
++							state = STATE_ERROR;
++							continue;
++						} else {
++							goto error;
++						}
++					}
+ 
+-	if (*start == '\n') {
+-	    start++;
+-	    lineNum++;
+-	    continue;
+-	}
++					if (rc > 0)
++						newlog->createMode = createMode;
+ 
+-	if (scriptStart) {
+-	    if (!strncmp(start, "endscript", 9)) {
+-		chptr = start + 9;
+-		while (isblank(*chptr))
+-		    chptr++;
+-		if (*chptr == '\n') {
+-		    endtag = start;
+-		    while (*endtag != '\n')
+-			endtag--;
+-		    endtag++;
+-		    *scriptDest = malloc(endtag - scriptStart + 1);
+-		    strncpy(*scriptDest, scriptStart,
+-			    endtag - scriptStart);
+-		    (*scriptDest)[endtag - scriptStart] = '\0';
+-		    start = chptr + 1;
+-		    lineNum++;
++					if (rc > 1) {
++						pw = getpwnam(createOwner);
++						if (!pw) {
++							message(MESS_ERROR, "%s:%d unknown user '%s'\n",
++								configFile, lineNum, createOwner);
++							if (newlog != defConfig) {
++								state = STATE_ERROR;
++								continue;
++							} else {
++								goto error;
++							}
++						}
++						newlog->createUid = pw->pw_uid;
++						endpwent();
++					}
++					if (rc > 2) {
++						group = getgrnam(createGroup);
++						if (!group) {
++							message(MESS_ERROR, "%s:%d unknown group '%s'\n",
++								configFile, lineNum, createGroup);
++							if (newlog != defConfig) {
++								state = STATE_ERROR;
++								continue;
++							} else {
++								goto error;
++							}
++						}
++						newlog->createGid = group->gr_gid;
++						endgrent();
++					}
+ 
+-		    scriptDest = NULL;
+-		    scriptStart = NULL;
+-		}
+-	    }
++					newlog->flags |= LOG_FLAG_CREATE;
++				} else if (!strcmp(key, "nocreate")) {
++					newlog->flags &= ~LOG_FLAG_CREATE;
++				} else if (!strcmp(key, "size") || !strcmp(key, "minsize")) {
++					unsigned long long size = 0;
++					char *opt = key;
++							
++					if ((key = isolateValue(configFile, lineNum, opt, &start,
++							&buf, length)) != NULL) {
++						free(opt);
++						int l = strlen(key) - 1;
++						if (key[l] == 'k') {
++							key[l] = '\0';
++							multiplier = 1024;
++						} else if (key[l] == 'M') {
++							key[l] = '\0';
++							multiplier = 1024 * 1024;
++						} else if (key[l] == 'G') {
++							key[l] = '\0';
++							multiplier = 1024 * 1024 * 1024;
++						} else if (!isdigit(key[l])) {
++							message(MESS_ERROR, "%s:%d unknown unit '%c'\n",
++								configFile, lineNum, key[l]);
++							if (newlog != defConfig) {
++								state = STATE_ERROR;
++								continue;
++							} else {
++								goto error;
++							}
++						} else {
++							multiplier = 1;
++						}
+ 
+-	    if (scriptStart) {
+-		while (*start != '\n')
+-		    start++;
+-		lineNum++;
+-		start++;
+-	    }
+-	} else if (isalpha(*start)) {
+-	    endtag = start;
+-	    while (isalpha(*endtag))
+-		endtag++;
+-	    oldchar = *endtag;
+-	    *endtag = '\0';
++						size = multiplier * strtoul(key, &chptr, 0);
++						if (*chptr) {
++							message(MESS_ERROR, "%s:%d bad size '%s'\n",
++								configFile, lineNum, key);
++							if (newlog != defConfig) {
++								state = STATE_ERROR;
++								continue;
++							} else {
++								goto error;
++							}
++						}
++						if (!strncmp(key, "size", 4)) {
++						  newlog->criterium = ROT_SIZE;
++						  newlog->threshhold = size;
++						} else
++						  newlog->minsize = size;
++					}
++					else {
++						free(opt);
++						continue;
++					}
++				} else if (!strcmp(key, "shredcycles")) {
++					free(key);
++					if ((key = isolateValue(configFile, lineNum, "shred cycles", 
++							&start, &buf, length)) != NULL) {
++						newlog->shred_cycles = strtoul(key, &chptr, 0);
++						if (*chptr || newlog->shred_cycles < 0) {
++							message(MESS_ERROR, "%s:%d bad shred cycles '%s'\n",
++									configFile, lineNum, key);
++							goto error;
++						}
++					}
++					else continue;
++				} else if (!strcmp(key, "daily")) {
++					newlog->criterium = ROT_DAYS;
++					newlog->threshhold = 1;
++				} else if (!strcmp(key, "monthly")) {
++					newlog->criterium = ROT_MONTHLY;
++				} else if (!strcmp(key, "weekly")) {
++					newlog->criterium = ROT_WEEKLY;
++				} else if (!strcmp(key, "yearly")) {
++					newlog->criterium = ROT_YEARLY;
++				} else if (!strcmp(key, "rotate")) {
++					free(key);
++					if ((key = isolateValue
++						(configFile, lineNum, "rotate count", &start,
++						&buf, length)) != NULL) {
+ 
+-	    if (!strcmp(start, "compress")) {
+-		newlog->flags |= LOG_FLAG_COMPRESS;
++						newlog->rotateCount = strtoul(key, &chptr, 0);
++						if (*chptr || newlog->rotateCount < 0) {
++							message(MESS_ERROR,
++								"%s:%d bad rotation count '%s'\n",
++								configFile, lineNum, key);
++							if (newlog != defConfig) {
++								state = STATE_ERROR;
++								continue;
++							} else {
++								goto error;
++							}
++						}
++					}
++					else continue;
++				} else if (!strcmp(key, "start")) {
++					free(key);
++					if ((key = isolateValue
++						(configFile, lineNum, "start count", &start,
++						&buf, length)) != NULL) {
+ 
+-		*endtag = oldchar, start = endtag;
+-	    } else if (!strcmp(start, "nocompress")) {
+-		newlog->flags &= ~LOG_FLAG_COMPRESS;
++						newlog->logStart = strtoul(start, &chptr, 0);
++						if (*chptr || newlog->logStart < 0) {
++							message(MESS_ERROR, "%s:%d bad start count '%s'\n",
++								configFile, lineNum, key);
++							if (newlog != defConfig) {
++								state = STATE_ERROR;
++								continue;
++							} else {
++								goto error;
++							}
++						}
++					}
++					else continue;
++				} else if (!strcmp(key, "maxage")) {
++					free(key);
++					if ((key = isolateValue
++						(configFile, lineNum, "maxage count", &start,
++						&buf, length)) != NULL) {
++						newlog->rotateAge = strtoul(start, &chptr, 0);
++						if (*chptr || newlog->rotateAge < 0) {
++							message(MESS_ERROR, "%s:%d bad maximum age '%s'\n",
++								configFile, lineNum, start);
++							if (newlog != defConfig) {
++								state = STATE_ERROR;
++								continue;
++							} else {
++								goto error;
++							}
++						}
++					}
++					else continue;
++				} else if (!strcmp(key, "errors")) {
++					message(MESS_DEBUG,
++						"%s: %d: the errors directive is deprecated and no longer used.\n",
++						configFile, lineNum);
++				} else if (!strcmp(key, "mail")) {
++					freeLogItem(logAddress);
++					if (!(newlog->logAddress = readAddress(configFile, lineNum,
++										"mail", &start, &buf, length))) {
++						if (newlog != defConfig) {
++						state = STATE_ERROR;
++						continue;
++						} else {
++						goto error;
++						}
++					}
++					else continue;
++				} else if (!strcmp(key, "nomail")) {
++					freeLogItem(logAddress);
++				} else if (!strcmp(key, "missingok")) {
++					newlog->flags |= LOG_FLAG_MISSINGOK;
++				} else if (!strcmp(key, "nomissingok")) {
++					newlog->flags &= ~LOG_FLAG_MISSINGOK;
++				} else if (!strcmp(key, "prerotate")) {
++					freeLogItem (pre);
++					scriptStart = start;
++					scriptDest = &newlog->pre;
++					state = STATE_LOAD_SCRIPT;
++				} else if (!strcmp(key, "firstaction")) {
++					freeLogItem (first);
++					scriptStart = start;
++					scriptDest = &newlog->first;
++					state = STATE_LOAD_SCRIPT;
++				} else if (!strcmp(key, "postrotate")) {
++					freeLogItem (post);
++					scriptStart = start;
++					scriptDest = &newlog->post;
++					state = STATE_LOAD_SCRIPT;
++				} else if (!strcmp(key, "lastaction")) {
++					freeLogItem (last);
++					scriptStart = start;
++					scriptDest = &newlog->last;
++					state = STATE_LOAD_SCRIPT;
++				} else if (!strcmp(key, "tabooext")) {
++					if (newlog != defConfig) {
++						message(MESS_ERROR,
++							"%s:%d tabooext may not appear inside "
++							"of log file definition\n", configFile,
++							lineNum);
++						state = STATE_ERROR;
++						continue;
++					}
++					free(key);
++					if ((key = isolateValue(configFile, lineNum, "tabooext", &start,
++							&buf, length)) != NULL) {
+ 
+-		*endtag = oldchar, start = endtag;
+-	    } else if (!strcmp(start, "delaycompress")) {
+-		newlog->flags |= LOG_FLAG_DELAYCOMPRESS;
++						if (*key == '+') {
++							key++;
++							while (isspace(*key) && *key)
++								key++;
++						} else {
++							free_2d_array(tabooExts, tabooCount);
++							tabooCount = 0;
++							tabooExts = malloc(1);
++						}
+ 
+-		*endtag = oldchar, start = endtag;
+-	    } else if (!strcmp(start, "nodelaycompress")) {
+-		newlog->flags &= ~LOG_FLAG_DELAYCOMPRESS;
++						endtag = key;
++						while (*endtag) {
++							chptr = endtag;
++							while (!isspace(*chptr) && *chptr != ',' && *chptr)
++								chptr++;
+ 
+-		*endtag = oldchar, start = endtag;
+-		} else if (!strcmp(start, "shred")) {
+-		newlog->flags |= LOG_FLAG_SHRED;
++							tabooExts = realloc(tabooExts, sizeof(*tabooExts) *
++										(tabooCount + 1));
++							tabooExts[tabooCount] = malloc(chptr - endtag + 1);
++							strncpy(tabooExts[tabooCount], endtag,
++								chptr - endtag);
++							tabooExts[tabooCount][chptr - endtag] = '\0';
++							tabooCount++;
+ 
+-		*endtag = oldchar, start = endtag;
+-		} else if (!strcmp(start, "noshred")) { 
+-		newlog->flags &= ~LOG_FLAG_SHRED;
++							endtag = chptr;
++							if (*endtag == ',')
++								start++;
++							while (isspace(*endtag) && *endtag)
++								endtag++;
++						}
++					}
++					else continue;
++				} else if (!strcmp(key, "include")) {
++					free(key);
++					if ((key = isolateValue(configFile, lineNum, "include", &start,
++							&buf, length)) != NULL) {
+ 
+-		*endtag = oldchar, start = endtag;
+-	    } else if (!strcmp(start, "sharedscripts")) {
+-		newlog->flags |= LOG_FLAG_SHAREDSCRIPTS;
++						message(MESS_DEBUG, "including %s\n", key);
++						if (++recursion_depth > MAX_NESTING) {
++							message(MESS_ERROR, "%s:%d include nesting too deep\n",
++									configFile, lineNum);
++							--recursion_depth;
++							goto error;
++						}
++						if (readConfigPath(key, newlog)) {
++							--recursion_depth;
++							goto error;
++						}
++						--recursion_depth;
++					}
++					else continue;
++				} else if (!strcmp(key, "olddir")) {
++					freeLogItem (oldDir);
+ 
+-		*endtag = oldchar, start = endtag;
+-	    } else if (!strcmp(start, "nosharedscripts")) {
+-		newlog->flags &= ~LOG_FLAG_SHAREDSCRIPTS;
++					if (!(newlog->oldDir = readPath(configFile, lineNum,
++									"olddir", &start, &buf, length))) {
++						if (newlog != defConfig) {
++							state = STATE_ERROR;
++							continue;
++						} else {
++							goto error;
++						}
++					}
++					else continue;
++#if 0
++					if (stat(newlog->oldDir, &sb)) {
++						message(MESS_ERROR, "%s:%d error verifying olddir "
++							"path %s: %s\n", configFile, lineNum,
++							newlog->oldDir, strerror(errno));
++						free(newlog->oldDir);
++						goto error;
++					}
+ 
+-		*endtag = oldchar, start = endtag;
+-	    } else if (!strcmp(start, "copytruncate")) {
+-		newlog->flags |= LOG_FLAG_COPYTRUNCATE;
++					if (!S_ISDIR(sb.st_mode)) {
++						message(MESS_ERROR, "%s:%d olddir path %s is not a "
++							"directory\n", configFile, lineNum,
++							newlog->oldDir);
++						free(newlog->oldDir);
++						goto error;
++					}
++#endif
++					message(MESS_DEBUG, "olddir is now %s\n", newlog->oldDir);
++				} else if (!strcmp(key, "extension")) {
++					if ((key = isolateValue
++						(configFile, lineNum, "extension name", &start,
++							&buf, length)) != NULL) {
++						freeLogItem (extension);
++						newlog->extension = key;
++						key = NULL;
++					}
++					else continue;
+ 
+-		*endtag = oldchar, start = endtag;
+-	    } else if (!strcmp(start, "nocopytruncate")) {
+-		newlog->flags &= ~LOG_FLAG_COPYTRUNCATE;
++					message(MESS_DEBUG, "extension is now %s\n",
++						newlog->extension);
+ 
+-		*endtag = oldchar, start = endtag;
+-	    } else if (!strcmp(start, "copy")) {
+-		newlog->flags |= LOG_FLAG_COPY;
++				} else if (!strcmp(key, "compresscmd")) {
++					freeLogItem (compress_prog);
+ 
+-		*endtag = oldchar, start = endtag;
+-	    } else if (!strcmp(start, "nocopy")) {
+-		newlog->flags &= ~LOG_FLAG_COPY;
++					if (!
++						(newlog->compress_prog =
++							readPath(configFile, lineNum, "compress", &start, &buf, length))) {
++						if (newlog != defConfig) {
++							state = STATE_ERROR;
++							continue;
++						} else {
++							goto error;
++						}
++					}
++					else continue;
+ 
+-		*endtag = oldchar, start = endtag;
+-	    } else if (!strcmp(start, "ifempty")) {
+-		newlog->flags |= LOG_FLAG_IFEMPTY;
++					if (access(newlog->compress_prog, X_OK)) {
++						message(MESS_ERROR,
++							"%s:%d compression program %s is not an executable file\n",
++							configFile, lineNum, newlog->compress_prog);
++						if (newlog != defConfig) {
++							state = STATE_ERROR;
++							continue;
++						} else {
++							goto error;
++						}
++					}
+ 
+-		*endtag = oldchar, start = endtag;
+-	    } else if (!strcmp(start, "notifempty")) {
+-		newlog->flags &= ~LOG_FLAG_IFEMPTY;
++					message(MESS_DEBUG, "compress_prog is now %s\n",
++						newlog->compress_prog);
+ 
+-		*endtag = oldchar, start = endtag;
+-	    } else if (!strcmp(start, "dateext")) {
+-		newlog->flags |= LOG_FLAG_DATEEXT;
++				} else if (!strcmp(key, "uncompresscmd")) {
++					freeLogItem (uncompress_prog);
+ 
+-		*endtag = oldchar, start = endtag;
+-	    } else if (!strcmp(start, "nodateext")) {
+-		newlog->flags &= ~LOG_FLAG_DATEEXT;
+-		
+-		*endtag = oldchar, start = endtag;
+-	    } else if (!strcmp(start, "dateformat")) {
+-		*endtag = oldchar, start = endtag;
+-		
+-		endtag = start;
+-		while (*endtag != '\n')
+-		    endtag++;
+-		while (isspace(*endtag))
+-		    endtag--;
+-		endtag++;
+-		oldchar = *endtag, *endtag = '\0';
++					if (!
++						(newlog->uncompress_prog =
++							readPath(configFile, lineNum, "uncompress",
++								&start, &buf, length))) {
++						if (newlog != defConfig) {
++							state = STATE_ERROR;
++							continue;
++						} else {
++							goto error;
++						}
++					}
++					else continue;
+ 
+-		freeLogItem(dateformat);
+-		newlog->dateformat = strdup(start);
++					if (access(newlog->uncompress_prog, X_OK)) {
++						message(MESS_ERROR,
++							"%s:%d uncompression program %s is not an executable file\n",
++							configFile, lineNum, newlog->uncompress_prog);
++						if (newlog != defConfig) {
++							state = STATE_ERROR;
++							continue;
++						} else {
++							goto error;
++						}
++					}
+ 
+-		*endtag = oldchar, start = endtag;
+-	    } else if (!strcmp(start, "noolddir")) {
+-		newlog->oldDir = NULL;
++					message(MESS_DEBUG, "uncompress_prog is now %s\n",
++						newlog->uncompress_prog);
+ 
+-		*endtag = oldchar, start = endtag;
+-	    } else if (!strcmp(start, "mailfirst")) {
+-		newlog->flags |= LOG_FLAG_MAILFIRST;
++				} else if (!strcmp(key, "compressoptions")) {
++					char *options;
+ 
+-		*endtag = oldchar, start = endtag;
+-	    } else if (!strcmp(start, "maillast")) {
+-		newlog->flags &= ~LOG_FLAG_MAILFIRST;
++					if (newlog->compress_options_list) {
++						free(newlog->compress_options_list);
++						newlog->compress_options_list = NULL;
++						newlog->compress_options_count = 0;
++					}
+ 
+-		*endtag = oldchar, start = endtag;
+-	    } else if (!strcmp(start, "create")) {
+-		*endtag = oldchar, start = endtag;
++					if (!
++						(options =
++							readPath(configFile, lineNum, "compressoptions",
++								&start, &buf, length))) {
++						if (newlog != defConfig) {
++							state = STATE_ERROR;
++							continue;
++						} else {
++							goto error;
++						}
++					} else continue;
+ 
+-		endtag = start;
+-		while (*endtag != '\n')
+-		    endtag++;
+-		while (isspace(*endtag))
+-		    endtag--;
+-		endtag++;
+-		oldchar = *endtag, *endtag = '\0';
++					if (poptParseArgvString(options,
++								&newlog->compress_options_count,
++								&newlog->compress_options_list)) {
++						message(MESS_ERROR,
++							"%s:%d invalid compression options\n",
++							configFile, lineNum);
++						free(options);
++						if (newlog != defConfig) {
++							state = STATE_ERROR;
++							continue;
++						} else {
++							goto error;
++						}
++					}
+ 
+-		rc = sscanf(start, "%o %s %s%c", &createMode,
+-			    createOwner, createGroup, &foo);
+-		if (rc == 4) {
+-		    message(MESS_ERROR, "%s:%d extra arguments for "
+-			    "create\n", configFile, lineNum);
+-		    if (newlog != defConfig) {
+-			*endtag = oldchar, start = endtag;
+-			logerror = 1;
+-			continue;
+-		    } else {
+-			goto error;
+-		    }
+-		}
++					message(MESS_DEBUG, "compress_options is now %s\n",
++						options);
++					free(options);
++				} else if (!strcmp(key, "compressext")) {
++					freeLogItem (compress_ext);
+ 
+-		if (rc > 0)
+-		    newlog->createMode = createMode;
++					if (!
++						(newlog->compress_ext =
++							readPath(configFile, lineNum, "compress-ext",
++								&start, &buf, length))) {
++						if (newlog != defConfig) {
++							state = STATE_ERROR;
++							continue;
++						} else {
++							goto error;
++						}
++					} else continue;
+ 
+-		if (rc > 1) {
+-		    pw = getpwnam(createOwner);
+-		    if (!pw) {
+-			message(MESS_ERROR, "%s:%d unknown user '%s'\n",
+-				configFile, lineNum, createOwner);
+-			if (newlog != defConfig) {
+-			    *endtag = oldchar, start = endtag;
+-			    logerror = 1;
+-			    continue;
+-			} else {
+-			    goto error;
+-			}
+-		    }
+-		    newlog->createUid = pw->pw_uid;
+-		    endpwent();
+-		}
+-		if (rc > 2) {
+-		    group = getgrnam(createGroup);
+-		    if (!group) {
+-			message(MESS_ERROR, "%s:%d unknown group '%s'\n",
+-				configFile, lineNum, createGroup);
+-			if (newlog != defConfig) {
+-			    *endtag = oldchar, start = endtag;
+-			    logerror = 1;
+-			    continue;
+-			} else {
+-			    goto error;
+-			}
+-		    }
+-		    newlog->createGid = group->gr_gid;
+-		    endgrent();
+-		}
++					message(MESS_DEBUG, "compress_ext is now %s\n",
++						newlog->compress_ext);
++				} else {
++					message(MESS_ERROR, "%s:%d unknown option '%s' "
++						"-- ignoring line\n", configFile, lineNum, key);
++					if (*start != '\n')
++						state = STATE_SKIP_LINE;
++				}
++				free(key);
++				key = NULL;
++			} else if (*start == '/' || *start == '"' || *start == '\'') {
++				if (newlog != defConfig) {
++					message(MESS_ERROR, "%s:%d unexpected log filename\n",
++						configFile, lineNum);
++					state = STATE_ERROR;
++					continue;
++				}
+ 
+-		newlog->flags |= LOG_FLAG_CREATE;
++				/* If no compression options were found in config file, set
++				default values */
++				if (!newlog->compress_prog)
++					newlog->compress_prog = strdup(COMPRESS_COMMAND);
++				if (!newlog->uncompress_prog)
++					newlog->uncompress_prog = strdup(UNCOMPRESS_COMMAND);
++				if (!newlog->compress_ext)
++					newlog->compress_ext = strdup(COMPRESS_EXT);
+ 
+-		*endtag = oldchar, start = endtag;
+-	    } else if (!strcmp(start, "nocreate")) {
+-		newlog->flags &= ~LOG_FLAG_CREATE;
++				/* Allocate a new logInfo structure and insert it into the logs
++				queue, copying the actual values from defConfig */
++				if ((newlog = newLogInfo(defConfig)) == NULL)
++					goto error;
+ 
+-		*endtag = oldchar, start = endtag;
+-	    } else if (!strcmp(start, "size") || !strcmp(start, "minsize")) {
+-		unsigned long long size = 0;
+-		char *opt = start;
+-		*endtag = oldchar, start = endtag;
++				endtag = start;
++				while (endtag - buf < length && *endtag != '{' && *endtag != '\0') {
++					endtag++;}
++				if (endtag - buf > length)
++					continue;
++				char *key = strndup(start, endtag - start);
++				start = endtag;
+ 
+-		if (!isolateValue(configFile, lineNum, opt, &start,
+-				  &endtag)) {
+-		    oldchar = *endtag, *endtag = '\0';
++				if (poptParseArgvString(key, &argc, &argv)) {
++				message(MESS_ERROR, "%s:%d error parsing filename\n",
++					configFile, lineNum);
++				free(key);
++				goto error;
++				} else if (argc < 1) {
++				message(MESS_ERROR,
++					"%s:%d { expected after log file name(s)\n",
++					configFile, lineNum);
++				free(key);
++				goto error;
++				}
+ 
+-		    length = strlen(start) - 1;
+-		    if (start[length] == 'k') {
+-			start[length] = '\0';
+-			multiplier = 1024;
+-		    } else if (start[length] == 'M') {
+-			start[length] = '\0';
+-			multiplier = 1024 * 1024;
+-		    } else if (start[length] == 'G') {
+-			start[length] = '\0';
+-			multiplier = 1024 * 1024 * 1024;
+-		    } else if (!isdigit(start[length])) {
+-			message(MESS_ERROR, "%s:%d unknown unit '%c'\n",
+-				configFile, lineNum, start[length]);
+-			if (newlog != defConfig) {
+-			    *endtag = oldchar, start = endtag;
+-			    logerror = 1;
+-			    continue;
+-			} else {
+-			    goto error;
+-			}
+-		    } else {
+-			multiplier = 1;
+-		    }
++				newlog->files = NULL;
++				newlog->numFiles = 0;
++				for (argNum = 0; argNum < argc && logerror != 1; argNum++) {
++				if (globerr_msg) {
++					free(globerr_msg);
++					globerr_msg = NULL;
++				}
++					
++				rc = glob(argv[argNum], GLOB_NOCHECK, globerr,
++					&globResult);
++				if (rc == GLOB_ABORTED) {
++					if (newlog->flags & LOG_FLAG_MISSINGOK) {
++						continue;
++					}
+ 
+-		    size = multiplier * strtoul(start, &chptr, 0);
+-		    if (*chptr) {
+-			message(MESS_ERROR, "%s:%d bad size '%s'\n",
+-				configFile, lineNum, start);
+-			if (newlog != defConfig) {
+-			    *endtag = oldchar, start = endtag;
+-			    logerror = 1;
+-			    continue;
+-			} else {
+-			    goto error;
+-			}
+-		    }
++				/* We don't yet know whether this stanza has "missingok"
++					* set, so store the error message for later. */
++					rc = asprintf(&globerr_msg, "%s:%d glob failed for %s: %s\n",
++						configFile, lineNum, argv[argNum], strerror(glob_errno));
++					if (rc == -1)
++					globerr_msg = NULL;
++					
++					globResult.gl_pathc = 0;
++				}
+ 
+-		    if (!strncmp(opt, "size", 4)) {
+-			newlog->criterium = ROT_SIZE;
+-			newlog->threshhold = size;
+-		    } else
+-			newlog->minsize = size;
++				newlog->files =
++					realloc(newlog->files,
++						sizeof(*newlog->files) * (newlog->numFiles +
++									globResult.
++									gl_pathc));
+ 
+-		    *endtag = oldchar, start = endtag;
+-		}
+-#if 0				/* this seems like such a good idea :-( */
+-	    } else if (!strcmp(start, "days")) {
+-		*endtag = oldchar, start = endtag;
++				for (i = 0; i < globResult.gl_pathc; i++) {
++					/* if we glob directories we can get false matches */
++					if (!lstat(globResult.gl_pathv[i], &sb) &&
++					S_ISDIR(sb.st_mode)) {
++						continue;
++					}
+ 
+-		if (!isolateValue(configFile, lineNum, "size", &start,
+-				  &endtag)) {
+-		    oldchar = *endtag, *endtag = '\0';
++					for (log = logs.tqh_first; log != NULL;
++						log = log->list.tqe_next) {
++					for (k = 0; k < log->numFiles; k++) {
++						if (!strcmp(log->files[k],
++							globResult.gl_pathv[i])) {
++						message(MESS_ERROR,
++							"%s:%d duplicate log entry for %s\n",
++							configFile, lineNum,
++							globResult.gl_pathv[i]);
++						logerror = 1;
++						goto duperror;
++						}
++					}
++					}
+ 
+-		    newlog->threshhold = strtoul(start, &chptr, 0);
+-		    if (*chptr) {
+-			message(MESS_ERROR,
+-				"%s:%d bad number of days'%s'\n",
+-				configFile, lineNum, start);
+-			goto error;
+-		    }
++					newlog->files[newlog->numFiles] =
++					strdup(globResult.gl_pathv[i]);
++					newlog->numFiles++;
++				}
++		duperror:
++				globfree(&globResult);
++				}
+ 
+-		    newlog->criterium = ROT_DAYS;
++				newlog->pattern = key;
+ 
+-		    *endtag = oldchar, start = endtag;
+-		}
+-#endif
+-	    } else if (!strcmp(start, "shredcycles")) {
+-		*endtag = oldchar, start = endtag;
++// 				if (!logerror)
++// 				message(MESS_DEBUG, "reading config info for %s\n", start);
+ 
+-		if (!isolateValue(configFile, lineNum, "shred cycles", 
+-				&start, &endtag)) {
+-			oldchar = *endtag, *endtag = '\0';
++				free(argv);
+ 
+-			newlog->shred_cycles = strtoul(start, &chptr, 0);
+-			if (*chptr || newlog->shred_cycles < 0) {
+-				message(MESS_ERROR, "%s:%d bad shred cycles '%s'\n",
+-						configFile, lineNum, start);
+-				goto error;
+-			}
+-			*endtag = oldchar, start = endtag;
+-		}
+-		} else if (!strcmp(start, "daily")) {
+-		*endtag = oldchar, start = endtag;
++// 				start = endtag + 1;
++			} else if (*start == '}') {
++				if (newlog == defConfig) {
++					message(MESS_ERROR, "%s:%d unexpected }\n", configFile,
++						lineNum);
++					goto error;
++				}
++			if (globerr_msg) {
++				if (!(newlog->flags & LOG_FLAG_MISSINGOK))
++					message(MESS_ERROR, globerr_msg);
++				free(globerr_msg);
++				globerr_msg = NULL;
++				if (!(newlog->flags & LOG_FLAG_MISSINGOK))
++					return 1;
++				}
+ 
+-		newlog->criterium = ROT_DAYS;
+-		newlog->threshhold = 1;
+-	    } else if (!strcmp(start, "monthly")) {
+-		*endtag = oldchar, start = endtag;
++				if (newlog->oldDir) {
++				for (i = 0; i < newlog->numFiles; i++) {
++					char *ld;
++					dirName = ourDirName(newlog->files[i]);
++					if (stat(dirName, &sb2)) {
++					message(MESS_ERROR,
++						"%s:%d error verifying log file "
++						"path %s: %s\n", configFile, lineNum,
++						dirName, strerror(errno));
++					free(dirName);
++					goto error;
++					}
++					ld = alloca(strlen(dirName) + strlen(newlog->oldDir) +
++						2);
++					sprintf(ld, "%s/%s", dirName, newlog->oldDir);
++					free(dirName);
+ 
+-		newlog->criterium = ROT_MONTHLY;
+-	    } else if (!strcmp(start, "weekly")) {
+-		*endtag = oldchar, start = endtag;
++					if (newlog->oldDir[0] != '/')
++					dirName = ld;
++					else
++					dirName = newlog->oldDir;
++					if (stat(dirName, &sb)) {
++					message(MESS_ERROR, "%s:%d error verifying olddir "
++						"path %s: %s\n", configFile, lineNum,
++						dirName, strerror(errno));
++					goto error;
++					}
+ 
+-		newlog->criterium = ROT_WEEKLY;
+-	    } else if (!strcmp(start, "yearly")) {
+-		*endtag = oldchar, start = endtag;
++					if (sb.st_dev != sb2.st_dev) {
++					message(MESS_ERROR,
++						"%s:%d olddir %s and log file %s "
++						"are on different devices\n", configFile,
++						lineNum, newlog->oldDir, newlog->files[i]);
++					goto error;
++					}
++				}
++				}
+ 
+-		newlog->criterium = ROT_YEARLY;
+-	    } else if (!strcmp(start, "rotate")) {
+-		*endtag = oldchar, start = endtag;
+-
+-		if (!isolateValue
+-		    (configFile, lineNum, "rotate count", &start,
+-		     &endtag)) {
+-		    oldchar = *endtag, *endtag = '\0';
+-
+-		    newlog->rotateCount = strtoul(start, &chptr, 0);
+-		    if (*chptr || newlog->rotateCount < 0) {
+-			message(MESS_ERROR,
+-				"%s:%d bad rotation count '%s'\n",
+-				configFile, lineNum, start);
+-			if (newlog != defConfig) {
+-			    *endtag = oldchar, start = endtag;
+-			    logerror = 1;
+-			    continue;
+-			} else {
+-			    goto error;
++				newlog = defConfig;
++				state = STATE_DEFINITION_END;
++			} else if (*start != '\n') {
++				message(MESS_ERROR, "%s:%d lines must begin with a keyword "
++					"or a filename (possibly in double quotes)\n",
++					configFile, lineNum);
++					state = STATE_SKIP_LINE;
+ 			}
+-		    }
+-		    *endtag = oldchar, start = endtag;
+-		}
+-	    } else if (!strcmp(start, "start")) {
+-		*endtag = oldchar, start = endtag;
+-
+-		if (!isolateValue
+-		    (configFile, lineNum, "start count", &start,
+-		     &endtag)) {
+-		    oldchar = *endtag, *endtag = '\0';
+-
+-		    newlog->logStart = strtoul(start, &chptr, 0);
+-		    if (*chptr || newlog->logStart < 0) {
+-			message(MESS_ERROR, "%s:%d bad start count '%s'\n",
+-				configFile, lineNum, start);
+-			if (newlog != defConfig) {
+-			    *endtag = oldchar, start = endtag;
+-			    logerror = 1;
+-			    continue;
+-			} else {
+-			    goto error;
++			break;
++		case STATE_SKIP_LINE:
++		case STATE_SKIP_LINE | STATE_SKIP_CONFIG:
++			if (*start == '\n')
++				state = state & STATE_SKIP_CONFIG ? STATE_SKIP_CONFIG : STATE_DEFAULT;
++			break;
++		case STATE_SKIP_LINE | STATE_LOAD_SCRIPT:
++			if (*start == '\n')
++				state = STATE_LOAD_SCRIPT;
++			break;
++		case STATE_SKIP_LINE | STATE_LOAD_SCRIPT | STATE_SKIP_CONFIG:
++			if (*start == '\n')
++				state = STATE_LOAD_SCRIPT | STATE_SKIP_CONFIG;
++			break;
++		case STATE_DEFINITION_END:
++		case STATE_DEFINITION_END | STATE_SKIP_CONFIG:
++			if (isblank(*start))
++				continue;
++			if (*start != '\n') {
++				message(MESS_ERROR, "%s:%d, unexpected text after }\n",
++					configFile, lineNum);
++				state = STATE_SKIP_LINE | (state & STATE_SKIP_CONFIG ? STATE_SKIP_CONFIG : 0);
+ 			}
+-		    }
+-		    *endtag = oldchar, start = endtag;
+-		}
+-	    } else if (!strcmp(start, "maxage")) {
+-		*endtag = oldchar, start = endtag;
++			else
++				state = state & STATE_SKIP_CONFIG ? STATE_SKIP_CONFIG : STATE_DEFAULT;
++			break;
++		case STATE_ERROR:
++			assert(newlog != defConfig);
+ 
+-		if (!isolateValue
+-		    (configFile, lineNum, "maxage count", &start,
+-		     &endtag)) {
+-		    oldchar = *endtag, *endtag = '\0';
++			message(MESS_ERROR, "found error in %s, skipping\n",
++				newlog->pattern ? newlog->pattern : "log config");
+ 
+-		    newlog->rotateAge = strtoul(start, &chptr, 0);
+-		    if (*chptr || newlog->rotateAge < 0) {
+-			message(MESS_ERROR, "%s:%d bad maximum age '%s'\n",
+-				configFile, lineNum, start);
+-			if (newlog != defConfig) {
+-			    *endtag = oldchar, start = endtag;
+-			    logerror = 1;
+-			    continue;
+-			} else {
+-			    goto error;
+-			}
+-		    }
+-		    *endtag = oldchar, start = endtag;
+-		}
+-	    } else if (!strcmp(start, "errors")) {
+-		message(MESS_DEBUG,
+-			"%s: %d: the errors directive is deprecated and no longer used.\n",
+-			configFile, lineNum);
+-	    } else if (!strcmp(start, "mail")) {
+-		*endtag = oldchar, start = endtag;
+-		freeLogItem(logAddress);
+-		if (!(newlog->logAddress = readAddress(configFile, lineNum,
+-						       "mail", &start))) {
+-		    if (newlog != defConfig) {
+-			logerror = 1;
+-			continue;
+-		    } else {
+-			goto error;
+-		    }
+-		}
+-	    } else if (!strcmp(start, "nomail")) {
+-	        freeLogItem(logAddress);
++			state = STATE_SKIP_CONFIG;
++			break;
++		case STATE_LOAD_SCRIPT:
++		case STATE_LOAD_SCRIPT | STATE_SKIP_CONFIG:
++			if ((key = isolateWord(&start, &buf, length)) == NULL)
++				continue;
+ 
+-		*endtag = oldchar, start = endtag;
+-	    } else if (!strcmp(start, "missingok")) {
+-		newlog->flags |= LOG_FLAG_MISSINGOK;
++			if (strcmp(key, "endscript") == 0) {
++				if (state & STATE_SKIP_CONFIG) {
++					state = STATE_SKIP_CONFIG;
++				}
++				else {
++					endtag = start - 9;
++					while (*endtag != '\n')
++					endtag--;
++					endtag++;
++					*scriptDest = malloc(endtag - scriptStart + 1);
++					strncpy(*scriptDest, scriptStart,
++						endtag - scriptStart);
++					(*scriptDest)[endtag - scriptStart] = '\0';
+ 
+-		*endtag = oldchar, start = endtag;
+-	    } else if (!strcmp(start, "nomissingok")) {
+-		newlog->flags &= ~LOG_FLAG_MISSINGOK;
+-
+-		*endtag = oldchar, start = endtag;
+-	    } else if (!strcmp(start, "prerotate")) {
+-		*endtag = oldchar, start = endtag;
+-
+-		freeLogItem (pre);
+-
+-		scriptStart = start;
+-		scriptDest = &newlog->pre;
+-
+-		while (*start != '\n')
+-		    start++;
+-	    } else if (!strcmp(start, "firstaction")) {
+-		*endtag = oldchar, start = endtag;
+-
+-		freeLogItem (first);
+-
+-		scriptStart = start;
+-		scriptDest = &newlog->first;
+-
+-		while (*start != '\n')
+-		    start++;
+-	    } else if (!strcmp(start, "postrotate")) {
+-		*endtag = oldchar, start = endtag;
+-
+-		freeLogItem (post);
+-
+-		scriptStart = start;
+-		scriptDest = &newlog->post;
+-
+-		while (*start != '\n')
+-		    start++;
+-	    } else if (!strcmp(start, "lastaction")) {
+-		*endtag = oldchar, start = endtag;
+-
+-		freeLogItem (last);
+-
+-		scriptStart = start;
+-		scriptDest = &newlog->last;
+-
+-		while (*start != '\n')
+-		    start++;
+-	    } else if (!strcmp(start, "tabooext")) {
+-		if (newlog != defConfig) {
+-		    message(MESS_ERROR,
+-			    "%s:%d tabooext may not appear inside "
+-			    "of log file definition\n", configFile,
+-			    lineNum);
+-		    *endtag = oldchar, start = endtag;
+-		    logerror = 1;
+-		    continue;
+-		}
+-
+-		*endtag = oldchar, start = endtag;
+-		if (!isolateValue(configFile, lineNum, "tabooext", &start,
+-				  &endtag)) {
+-		    oldchar = *endtag, *endtag = '\0';
+-
+-		    if (*start == '+') {
+-			start++;
+-			while (isspace(*start) && *start)
+-			    start++;
+-		    } else {
+-			free_2d_array(tabooExts, tabooCount);
+-			tabooCount = 0;
+-			tabooExts = malloc(1);
+-		    }
+-
+-		    while (*start) {
+-			chptr = start;
+-			while (!isspace(*chptr) && *chptr != ',' && *chptr)
+-			    chptr++;
+-
+-			tabooExts = realloc(tabooExts, sizeof(*tabooExts) *
+-					    (tabooCount + 1));
+-			tabooExts[tabooCount] = malloc(chptr - start + 1);
+-			strncpy(tabooExts[tabooCount], start,
+-				chptr - start);
+-			tabooExts[tabooCount][chptr - start] = '\0';
+-			tabooCount++;
+-
+-			start = chptr;
+-			if (*start == ',')
+-			    start++;
+-			while (isspace(*start) && *start)
+-			    start++;
+-		    }
+-
+-		    *endtag = oldchar, start = endtag;
+-		}
+-	    } else if (!strcmp(start, "include")) {
+-// 		if (newlog != defConfig) {
+-// 		    message(MESS_ERROR,
+-// 			    "%s:%d include may not appear inside "
+-// 			    "of log file definition\n", configFile,
+-// 			    lineNum);
+-// 		    *endtag = oldchar, start = endtag;
+-// 		    logerror = 1;
+-// 		    continue;
+-// 		}
+-
+-		*endtag = oldchar, start = endtag;
+-		if (!isolateValue(configFile, lineNum, "include", &start,
+-				  &endtag)) {
+-		    oldchar = *endtag, *endtag = '\0';
+-
+-		    message(MESS_DEBUG, "including %s\n", start);
+-			if (++recursion_depth > MAX_NESTING) {
+-				message(MESS_ERROR, "%s:%d include nesting too deep\n",
+-						configFile, lineNum);
+-				--recursion_depth;
+-				goto error;
++					scriptDest = NULL;
++					scriptStart = NULL;
++				}
++				state = state & STATE_SKIP_CONFIG ? STATE_SKIP_CONFIG : STATE_DEFAULT;
+ 			}
+-		    if (readConfigPath(start, newlog)) {
+-				--recursion_depth;
+-				goto error;
++			else {
++				state = (*start == '\n' ? 0 : STATE_SKIP_LINE) |
++					STATE_LOAD_SCRIPT |
++					(state & STATE_SKIP_CONFIG ? STATE_SKIP_CONFIG : 0);
+ 			}
+-			--recursion_depth;
+-
+-		    *endtag = oldchar, start = endtag;
+-		}
+-	    } else if (!strcmp(start, "olddir")) {
+-		*endtag = oldchar, start = endtag;
+-
+-		freeLogItem (oldDir);
+-
+-		if (!(newlog->oldDir = readPath(configFile, lineNum,
+-						"olddir", &start))) {
+-		    if (newlog != defConfig) {
+-			logerror = 1;
+-			continue;
+-		    } else {
+-			goto error;
+-		    }
+-		}
+-#if 0
+-		if (stat(newlog->oldDir, &sb)) {
+-		    message(MESS_ERROR, "%s:%d error verifying olddir "
+-			    "path %s: %s\n", configFile, lineNum,
+-			    newlog->oldDir, strerror(errno));
+-		    free(newlog->oldDir);
+-		    goto error;
+-		}
+-
+-		if (!S_ISDIR(sb.st_mode)) {
+-		    message(MESS_ERROR, "%s:%d olddir path %s is not a "
+-			    "directory\n", configFile, lineNum,
+-			    newlog->oldDir);
+-		    free(newlog->oldDir);
+-		    goto error;
+-		}
+-#endif
+-
+-		message(MESS_DEBUG, "olddir is now %s\n", newlog->oldDir);
+-	    } else if (!strcmp(start, "extension")) {
+-		*endtag = oldchar, start = endtag;
+-
+-		if (!isolateValue
+-		    (configFile, lineNum, "extension name", &start,
+-		     &endtag)) {
+-		    oldchar = *endtag, *endtag = '\0';
+-
+-		    freeLogItem (extension);
+-		    newlog->extension = strdup(start);
+-
+-		    *endtag = oldchar, start = endtag;
+-		}
+-
+-		message(MESS_DEBUG, "extension is now %s\n",
+-			newlog->extension);
+-
+-	    } else if (!strcmp(start, "compresscmd")) {
+-		*endtag = oldchar, start = endtag;
+-
+-		freeLogItem (compress_prog);
+-
+-		if (!
+-		    (newlog->compress_prog =
+-		     readPath(configFile, lineNum, "compress", &start))) {
+-		    if (newlog != defConfig) {
+-			logerror = 1;
+-			continue;
+-		    } else {
+-			goto error;
+-		    }
+-		}
+-
+-		if (access(newlog->compress_prog, X_OK)) {
+-		    message(MESS_ERROR,
+-			    "%s:%d compression program %s is not an executable file\n",
+-			    configFile, lineNum, newlog->compress_prog);
+-		    if (newlog != defConfig) {
+-			logerror = 1;
+-			continue;
+-		    } else {
+-			goto error;
+-		    }
+-		}
+-
+-		message(MESS_DEBUG, "compress_prog is now %s\n",
+-			newlog->compress_prog);
+-
+-	    } else if (!strcmp(start, "uncompresscmd")) {
+-		*endtag = oldchar, start = endtag;
+-
+-		freeLogItem (uncompress_prog);
+-
+-		if (!
+-		    (newlog->uncompress_prog =
+-		     readPath(configFile, lineNum, "uncompress",
+-			      &start))) {
+-		    if (newlog != defConfig) {
+-			logerror = 1;
+-			continue;
+-		    } else {
+-			goto error;
+-		    }
+-		}
+-
+-		if (access(newlog->uncompress_prog, X_OK)) {
+-		    message(MESS_ERROR,
+-			    "%s:%d uncompression program %s is not an executable file\n",
+-			    configFile, lineNum, newlog->uncompress_prog);
+-		    if (newlog != defConfig) {
+-			logerror = 1;
+-			continue;
+-		    } else {
+-			goto error;
+-		    }
+-		}
+-
+-		message(MESS_DEBUG, "uncompress_prog is now %s\n",
+-			newlog->uncompress_prog);
+-
+-	    } else if (!strcmp(start, "compressoptions")) {
+-		char *options;
+-
+-		if (newlog->compress_options_list) {
+-		    free(newlog->compress_options_list);
+-		    newlog->compress_options_list = NULL;
+-		    newlog->compress_options_count = 0;
+-		}
+-
+-		*endtag = oldchar, start = endtag;
+-		if (!
+-		    (options =
+-		     readPath(configFile, lineNum, "compressoptions",
+-			      &start))) {
+-		    if (newlog != defConfig) {
+-			logerror = 1;
+-			continue;
+-		    } else {
+-			goto error;
+-		    }
+-		}
+-
+-		if (poptParseArgvString(options,
+-					&newlog->compress_options_count,
+-					&newlog->compress_options_list)) {
+-		    message(MESS_ERROR,
+-			    "%s:%d invalid compression options\n",
+-			    configFile, lineNum);
+-		    free(options);
+-		    if (newlog != defConfig) {
+-			logerror = 1;
+-			continue;
+-		    } else {
+-			goto error;
+-		    }
+-		}
+-
+-		message(MESS_DEBUG, "compress_options is now %s\n",
+-			options);
+-		free(options);
+-	    } else if (!strcmp(start, "compressext")) {
+-		*endtag = oldchar, start = endtag;
+-
+-		freeLogItem (compress_ext);
+-
+-		if (!
+-		    (newlog->compress_ext =
+-		     readPath(configFile, lineNum, "compress-ext",
+-			      &start))) {
+-		    if (newlog != defConfig) {
+-			logerror = 1;
+-			continue;
+-		    } else {
+-			goto error;
+-		    }
+-		}
+-
+-		message(MESS_DEBUG, "compress_ext is now %s\n",
+-			newlog->compress_ext);
+-	    } else {
+-		message(MESS_ERROR, "%s:%d unknown option '%s' "
+-			"-- ignoring line\n", configFile, lineNum, start);
+-
+-		*endtag = oldchar, start = endtag;
+-	    }
+-
+-	    while (isblank(*start))
+-		start++;
+-
+-	    if (*start != '\n') {
+-		message(MESS_ERROR, "%s:%d unexpected text\n", configFile,
+-			lineNum);
+-		while (*start != '\n')
+-		    start++;
+-	    }
+-
+-	    lineNum++;
+-	    start++;
+-	} else if (*start == '/' || *start == '"' || *start == '\'') {
+-	    if (newlog != defConfig) {
+-		message(MESS_ERROR, "%s:%d unexpected log filename\n",
+-			configFile, lineNum);
+-		logerror = 1;
+-		continue;
+-	    }
+-
+-	    /* If no compression options were found in config file, set
+-	       default values */
+-	    if (!newlog->compress_prog)
+-		newlog->compress_prog = strdup(COMPRESS_COMMAND);
+-	    if (!newlog->uncompress_prog)
+-		newlog->uncompress_prog = strdup(UNCOMPRESS_COMMAND);
+-	    if (!newlog->compress_ext)
+-		newlog->compress_ext = strdup(COMPRESS_EXT);
+-
+-	    /* Allocate a new logInfo structure and insert it into the logs
+-	       queue, copying the actual values from defConfig */
+-	    if ((newlog = newLogInfo(defConfig)) == NULL)
+-		goto error;
+-
+-	    endtag = start;
+-	    while (*endtag != '{' && *endtag != '\0')
+-		endtag++;
+-	    if (*endtag != '{') {
+-		message(MESS_ERROR, "%s:%d missing end of line\n",
+-			configFile, lineNum);
+-	    }
+-	    *endtag = '\0';
+-
+-	    if (poptParseArgvString(start, &argc, &argv)) {
+-		message(MESS_ERROR, "%s:%d error parsing filename\n",
+-			configFile, lineNum);
+-		goto error;
+-	    } else if (argc < 1) {
+-		message(MESS_ERROR,
+-			"%s:%d { expected after log file name(s)\n",
+-			configFile, lineNum);
+-		goto error;
+-	    }
+-
+-	    newlog->files = NULL;
+-	    newlog->numFiles = 0;
+-	    for (argNum = 0; argNum < argc && logerror != 1; argNum++) {
+-		if (globerr_msg) {
+-		    free(globerr_msg);
+-		    globerr_msg = NULL;
+-		}
+-			
+-		rc = glob(argv[argNum], GLOB_NOCHECK, globerr,
+-			  &globResult);
+-		if (rc == GLOB_ABORTED) {
+-		    if (newlog->flags & LOG_FLAG_MISSINGOK)
+-			continue;
+-
+-          /* We don't yet know whether this stanza has "missingok"
+-		     * set, so store the error message for later. */
+-		    rc = asprintf(&globerr_msg, "%s:%d glob failed for %s: %s\n",
+-			    configFile, lineNum, argv[argNum], strerror(glob_errno));
+-		    if (rc == -1)
+-			globerr_msg = NULL;
+-		    
+-		    globResult.gl_pathc = 0;
+-		}
+-
+-		newlog->files =
+-		    realloc(newlog->files,
+-			    sizeof(*newlog->files) * (newlog->numFiles +
+-						      globResult.
+-						      gl_pathc));
+-
+-		for (i = 0; i < globResult.gl_pathc; i++) {
+-		    /* if we glob directories we can get false matches */
+-		    if (!lstat(globResult.gl_pathv[i], &sb) &&
+-			S_ISDIR(sb.st_mode))
+-			continue;
+-
+-		    for (log = logs.tqh_first; log != NULL;
+-				log = log->list.tqe_next) {
+-			for (k = 0; k < log->numFiles; k++) {
+-			    if (!strcmp(log->files[k],
+-					globResult.gl_pathv[i])) {
+-				message(MESS_ERROR,
+-					"%s:%d duplicate log entry for %s\n",
+-					configFile, lineNum,
+-					globResult.gl_pathv[i]);
+-				logerror = 1;
+-				goto duperror;
+-			    }
++			break;
++		case STATE_SKIP_CONFIG:
++			if (*start == '}') {
++				state = STATE_DEFAULT;
++				freeTailLogs(1);
++				newlog = defConfig;
+ 			}
+-		    }
+-
+-		    newlog->files[newlog->numFiles] =
+-			strdup(globResult.gl_pathv[i]);
+-		    newlog->numFiles++;
+-		}
+-duperror:
+-		globfree(&globResult);
+-	    }
+-
+-	    newlog->pattern = strdup(start);
+-
+-	    if (!logerror)
+-		message(MESS_DEBUG, "reading config info for %s\n", start);
+-
+-	    free(argv);
+-
+-	    start = endtag + 1;
+-	} else if (*start == '}') {
+-	    if (newlog == defConfig) {
+-		message(MESS_ERROR, "%s:%d unexpected }\n", configFile,
+-			lineNum);
+-		goto error;
+-	    }
+-	if (globerr_msg) {
+-		if (!(newlog->flags & LOG_FLAG_MISSINGOK))
+-		    message(MESS_ERROR, globerr_msg);
+-		free(globerr_msg);
+-		globerr_msg = NULL;
+-		if (!(newlog->flags & LOG_FLAG_MISSINGOK))
+-		    return 1;
+-	    }
+-
+-	    if (newlog->oldDir) {
+-		for (i = 0; i < newlog->numFiles; i++) {
+-		    char *ld;
+-		    dirName = ourDirName(newlog->files[i]);
+-		    if (stat(dirName, &sb2)) {
+-			message(MESS_ERROR,
+-				"%s:%d error verifying log file "
+-				"path %s: %s\n", configFile, lineNum,
+-				dirName, strerror(errno));
+-			free(dirName);
+-			goto error;
+-		    }
+-		    ld = alloca(strlen(dirName) + strlen(newlog->oldDir) +
+-				2);
+-		    sprintf(ld, "%s/%s", dirName, newlog->oldDir);
+-		    free(dirName);
+-
+-		    if (newlog->oldDir[0] != '/')
+-			dirName = ld;
+-		    else
+-			dirName = newlog->oldDir;
+-		    if (stat(dirName, &sb)) {
+-			message(MESS_ERROR, "%s:%d error verifying olddir "
+-				"path %s: %s\n", configFile, lineNum,
+-				dirName, strerror(errno));
+-			goto error;
+-		    }
+-
+-		    if (sb.st_dev != sb2.st_dev) {
+-			message(MESS_ERROR,
+-				"%s:%d olddir %s and log file %s "
+-				"are on different devices\n", configFile,
+-				lineNum, newlog->oldDir, newlog->files[i]);
+-			goto error;
+-		    }
+-		}
+-	    }
+-
+-	    newlog = defConfig;
+-
+-	    start++;
+-	    while (isblank(*start))
+-		start++;
+-
+-	    if (*start != '\n') {
+-		message(MESS_ERROR, "%s:%d, unexpected text after {\n",
+-			configFile, lineNum);
+-	    }
+-	} else {
+-	    message(MESS_ERROR, "%s:%d lines must begin with a keyword "
+-		    "or a filename (possibly in double quotes)\n",
+-		    configFile, lineNum);
+-
+-	    while (*start != '\n')
+-		start++;
++			else {
++				if ((key = isolateWord(&start, &buf, length)) == NULL)
++					continue;
++				if (
++					(strcmp(key, "postrotate") == 0) ||
++					(strcmp(key, "prerotate") == 0) ||
++					(strcmp(key, "firstrotate") == 0) ||
++					(strcmp(key, "lastrotate") == 0)
++					) {
++					state = STATE_LOAD_SCRIPT | STATE_SKIP_CONFIG;
++				}
++				else {
++					state = STATE_SKIP_LINE | STATE_SKIP_CONFIG;
++				}
++				free(key);
++				key = NULL;
++			}
++			break;
++	}
++	if (key) {
++		free(key);
++		key = NULL;
++	}
++	if (*start == '\n') {
+ 	    lineNum++;
+-	    start++;
+ 	}
++
+     }
+ 
+     if (scriptStart) {
+@@ -1521,11 +1455,14 @@
+ 		configFile);
+ 	goto error;
+     }
+-	munmap(buf, (size_t)(length + 2));
++
++	munmap(buf, (size_t) length);
+ 	close(fd);
+     return 0;
+ error:
+-	munmap(buf, (size_t)(length + 2));
++	if (key)
++		free(key);
++	munmap(buf, (size_t) length);
+ 	close(fd);
+     return 1;
+ }
diff --git a/logrotate.spec b/logrotate.spec
index 8443c07..9f28658 100644
--- a/logrotate.spec
+++ b/logrotate.spec
@@ -1,16 +1,18 @@
 Summary: Rotates, compresses, removes and mails system log files
 Name: logrotate
 Version: 3.7.9
-Release: 4%{?dist}
+Release: 5%{?dist}
 License: GPL+
 Group: System Environment/Base
 Source: https://fedorahosted.org/releases/l/o/logrotate/logrotate-%{version}.tar.gz
 Patch1: logrotate-3.7.8-man-authors.patch
 Patch2: logrotate-3.7.9-man-size.patch
 Patch3: logrotate-3.7.9-man-page.patch
+Patch4: logrotate-3.7.9-config.patch
+Patch5: logrotate-3.7.9-acl.patch
 
-Requires: coreutils >= 5.92 libsepol libselinux popt
-BuildRequires: libselinux-devel popt-devel
+Requires: coreutils >= 5.92 libsepol libselinux popt libacl
+BuildRequires: libselinux-devel popt-devel libacl-devel
 BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
 
 %description
@@ -29,9 +31,11 @@ log files on your system.
 %patch1 -p2
 %patch2
 %patch3 -p1
+%patch4
+%patch5 -p2
 
 %build
-make %{?_smp_mflags} RPM_OPT_FLAGS="$RPM_OPT_FLAGS" WITH_SELINUX=yes
+make %{?_smp_mflags} RPM_OPT_FLAGS="$RPM_OPT_FLAGS" WITH_SELINUX=yes WITH_ACL=yes
 
 %install
 rm -rf $RPM_BUILD_ROOT
@@ -59,6 +63,10 @@ rm -rf $RPM_BUILD_ROOT
 %attr(0644, root, root) %verify(not size md5 mtime) %config(noreplace) %{_localstatedir}/lib/logrotate.status
 
 %changelog
+* Wed Dec 15 2010 Jan Kaluza <jkaluza at redhat.com> 3.7.9-5
+- fix #661181 - fixed SIGBUS when config file is empty or 4096 bytes
+- fix #666677 - preserve ACLs when rotating files
+
 * Tue Oct 19 2010 Jan Kaluza <jkaluza at redhat.com> 3.7.9-4
 - fix #644309 - mention all logrotate params in man page
 


More information about the scm-commits mailing list