[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