diff --git a/SOURCES/logrotate-3.8.6-compress-subject.patch b/SOURCES/logrotate-3.8.6-compress-subject.patch new file mode 100644 index 0000000..9ccb0dc --- /dev/null +++ b/SOURCES/logrotate-3.8.6-compress-subject.patch @@ -0,0 +1,139 @@ +diff --git a/logrotate.c b/logrotate.c +index 4ef044e..9faf341 100644 +--- a/logrotate.c ++++ b/logrotate.c +@@ -662,22 +662,20 @@ static int mailLog(struct logInfo *log, char *logFile, char *mailCommand, + static int mailLogWrapper(char *mailFilename, char *mailCommand, + int logNum, struct logInfo *log) + { +- /* if the log is compressed (and we're not mailing a +- * file whose compression has been delayed), we need +- * to uncompress it */ +- if ((log->flags & LOG_FLAG_COMPRESS) && +- !((log->flags & LOG_FLAG_DELAYCOMPRESS) && +- (log->flags & LOG_FLAG_MAILFIRST))) { +- if (mailLog(log, mailFilename, mailCommand, +- log->uncompress_prog, log->logAddress, +- log->files[logNum])) +- return 1; +- } else { +- if (mailLog(log, mailFilename, mailCommand, NULL, +- log->logAddress, mailFilename)) +- return 1; +- } +- return 0; ++ /* if the log is compressed (and we're not mailing a ++ * file whose compression has been delayed), we need ++ * to uncompress it */ ++ if ((log->flags & LOG_FLAG_COMPRESS) && !(log->flags & LOG_FLAG_DELAYCOMPRESS)) { ++ if (mailLog(log, mailFilename, mailCommand, ++ log->uncompress_prog, log->logAddress, ++ (log->flags & LOG_FLAG_MAILFIRST) ? log->files[logNum] : mailFilename)) ++ return 1; ++ } else { ++ if (mailLog(log, mailFilename, mailCommand, NULL, ++ log->logAddress, mailFilename)) ++ return 1; ++ } ++ return 0; + } + + static int copyTruncate(char *currLog, char *saveLog, struct stat *sb, +diff --git a/test/test b/test/test +index e9ce46f..f7f3cf4 100755 +--- a/test/test ++++ b/test/test +@@ -132,6 +132,9 @@ createlogs() { + + checkmail() { + (echo -s $PWD/$1 user@myhost.org; echo $2) | diff -u - mail-out ++ if [ $? != 0 ]; then ++ exit 5 ++ fi + } + + checkoutput() { +@@ -429,7 +432,7 @@ test.log 0 + scriptout 0 foo + EOF + +-checkmail test.log first ++checkmail test.log.2.gz first + + # check rotation into a directory given as a relative pathname + cleanup 12 +@@ -1424,4 +1427,37 @@ fi + + rm -f *test.log* + ++cleanup 64 ++ ++# ------------------------------- Test 64 ------------------------------------ ++# filename in mail's subject with compress directive and maillast directive ++# should be the name of the removed log ++preptest test.log 64 1 0 ++ ++DATESTRING=$(/bin/date +%Y%m%d) ++ ++$RLR test-config.64 --force ++checkoutput < notation now */ ++ if (rc == 0) { ++ rc = sscanf(key, "%199s %199s%c", u, g, &tmp); ++ /* Simulate that we have read mode and keep the default value. */ ++ if (rc > 0) { ++ m = *mode; ++ rc += 1; ++ } ++ } ++ ++ if (rc == 4) { ++ message(MESS_ERROR, "%s:%d extra arguments for " ++ "%s\n", configFile, lineNum, directive); ++ return -1; ++ } ++ ++ if (rc > 0) { ++ *mode = m; ++ } ++ ++ if (rc > 1) { ++ pw = getpwnam(u); ++ if (!pw) { ++ message(MESS_ERROR, "%s:%d unknown user '%s'\n", ++ configFile, lineNum, u); ++ return -1; ++ } ++ *uid = pw->pw_uid; ++ endpwent(); ++ } ++ if (rc > 2) { ++ group = getgrnam(g); ++ if (!group) { ++ message(MESS_ERROR, "%s:%d unknown group '%s'\n", ++ configFile, lineNum, g); ++ return -1; ++ } ++ *gid = group->gr_gid; ++ endgrent(); ++ } ++ ++ return 0; ++} ++ + static char *readAddress(const char *configFile, int lineNum, char *key, + char **startPtr, char **buf, size_t length) + { +@@ -249,6 +304,55 @@ static char *readAddress(const char *configFile, int lineNum, char *key, + return NULL; + } + ++static int do_mkdir(const char *path, mode_t mode, uid_t uid, gid_t gid) { ++ struct stat sb; ++ ++ if (stat(path, &sb) != 0) { ++ if (mkdir(path, mode) != 0 && errno != EEXIST) { ++ message(MESS_ERROR, "error creating %s: %s\n", ++ path, strerror(errno)); ++ return -1; ++ } ++ if ((uid != sb.st_uid || gid != sb.st_gid) && ++ chown(path, uid, gid)) { ++ message(MESS_ERROR, "error setting owner of %s to uid %d and gid %d: %s\n", ++ path, uid, gid, strerror(errno)); ++ return -1; ++ } ++ } ++ else if (!S_ISDIR(sb.st_mode)) { ++ message(MESS_ERROR, "path %s already exists, but it is not a directory\n", ++ path); ++ errno = ENOTDIR; ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static int mkpath(const char *path, mode_t mode, uid_t uid, gid_t gid) { ++ char *pp; ++ char *sp; ++ int rv; ++ char *copypath = strdup(path); ++ ++ rv = 0; ++ pp = copypath; ++ while (rv == 0 && (sp = strchr(pp, '/')) != 0) { ++ if (sp != pp) { ++ *sp = '\0'; ++ rv = do_mkdir(copypath, mode, uid, gid); ++ *sp = '/'; ++ } ++ pp = sp + 1; ++ } ++ if (rv == 0) { ++ rv = do_mkdir(path, mode, uid, gid); ++ } ++ free(copypath); ++ return rv; ++} ++ + static int checkFile(const char *fname) + { + int i; +@@ -330,6 +434,9 @@ static void copyLogInfo(struct logInfo *to, struct logInfo *from) + to->createGid = from->createGid; + to->suUid = from->suUid; + to->suGid = from->suGid; ++ to->olddirMode = from->olddirMode; ++ to->olddirUid = from->olddirUid; ++ to->olddirGid = from->olddirGid; + if (from->compress_options_count) { + poptDupArgv(from->compress_options_count, from->compress_options_list, + &to->compress_options_count, &to->compress_options_list); +@@ -539,6 +646,11 @@ int readAllConfigPaths(const char **paths) + .createMode = NO_MODE, + .createUid = NO_UID, + .createGid = NO_GID, ++ .olddirMode = NO_MODE, ++ .olddirUid = NO_UID, ++ .olddirGid = NO_GID, ++ .suUid = NO_UID, ++ .suGid = NO_GID, + .compress_options_list = NULL, + .compress_options_count = 0 + }; +@@ -579,13 +691,19 @@ static int globerr(const char *pathname, int theerr) + free(newlog->what); \ + newlog->what = NULL; \ + } while (0); ++#define RAISE_ERROR() \ ++ if (newlog != defConfig) { \ ++ state = STATE_ERROR; \ ++ continue; \ ++ } else { \ ++ goto error; \ ++ } + #define MAX_NESTING 16U + + static int readConfigFile(const char *configFile, struct logInfo *defConfig) + { + int fd; + char *buf, *endtag, *key = NULL; +- char foo; + off_t length; + int lineNum = 1; + unsigned long long multiplier; +@@ -595,11 +713,8 @@ static int readConfigFile(const char *configFile, struct logInfo *defConfig) + struct logInfo *newlog = defConfig; + char *start, *chptr; + char *dirName; +- struct group *group; + struct passwd *pw = NULL; + int rc; +- char createOwner[200], createGroup[200]; +- int createMode; + struct stat sb, sb2; + glob_t globResult; + const char **argv; +@@ -611,6 +726,7 @@ static int readConfigFile(const char *configFile, struct logInfo *defConfig) + static unsigned recursion_depth = 0U; + char *globerr_msg = NULL; + int in_config = 0; ++ int rv; + struct flock fd_lock = { + .l_start = 0, + .l_len = 0, +@@ -807,53 +923,22 @@ static int readConfigFile(const char *configFile, struct logInfo *defConfig) + } else if (!strcmp(key, "maillast")) { + newlog->flags &= ~LOG_FLAG_MAILFIRST; + } else if (!strcmp(key, "su")) { ++ mode_t tmp_mode = NO_MODE; + free(key); + key = isolateLine(&start, &buf, length); + if (key == NULL) + continue; + +- rc = sscanf(key, "%199s %199s%c", createOwner, +- createGroup, &foo); +- if (rc == 3) { +- message(MESS_ERROR, "%s:%d extra arguments for " +- "su\n", configFile, lineNum); +- if (newlog != defConfig) { +- state = STATE_ERROR; +- continue; +- } else { +- goto error; +- } ++ rv = readModeUidGid(configFile, lineNum, key, "su", ++ &tmp_mode, &newlog->suUid, ++ &newlog->suGid); ++ if (rv == -1) { ++ RAISE_ERROR(); + } +- +- if (rc > 0) { +- 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->suUid = pw->pw_uid; +- endpwent(); +- } +- if (rc > 1) { +- 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->suGid = group->gr_gid; +- endgrent(); ++ else if (tmp_mode != NO_MODE) { ++ message(MESS_ERROR, "%s:%d extra arguments for " ++ "su\n", configFile, lineNum); ++ RAISE_ERROR(); + } + + newlog->flags |= LOG_FLAG_SU; +@@ -863,65 +948,30 @@ static int readConfigFile(const char *configFile, struct logInfo *defConfig) + if (key == NULL) + continue; + +- rc = sscanf(key, "%o %199s %199s%c", &createMode, +- createOwner, createGroup, &foo); +- /* We support 'create notation now */ +- if (rc == 0) { +- rc = sscanf(key, "%199s %199s%c", +- createOwner, createGroup, &foo); +- /* Simulate that we have read createMode and se it +- * to NO_MODE. */ +- if (rc > 0) { +- createMode = NO_MODE; +- rc += 1; +- } +- } +- 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; +- } ++ rv = readModeUidGid(configFile, lineNum, key, "create", ++ &newlog->createMode, &newlog->createUid, ++ &newlog->createGid); ++ if (rv == -1) { ++ RAISE_ERROR(); + } + +- if (rc > 0) +- newlog->createMode = createMode; +- +- 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(); ++ newlog->flags |= LOG_FLAG_CREATE; ++ } else if (!strcmp(key, "createolddir")) { ++ free(key); ++ key = isolateLine(&start, &buf, length); ++ if (key == NULL) ++ continue; ++ ++ rv = readModeUidGid(configFile, lineNum, key, "createolddir", ++ &newlog->olddirMode, &newlog->olddirUid, ++ &newlog->olddirGid); ++ if (rv == -1) { ++ RAISE_ERROR(); + } + +- newlog->flags |= LOG_FLAG_CREATE; ++ newlog->flags |= LOG_FLAG_OLDDIRCREATE; ++ } else if (!strcmp(key, "nocreateolddir")) { ++ newlog->flags &= ~LOG_FLAG_OLDDIRCREATE; + } else if (!strcmp(key, "nocreate")) { + newlog->flags &= ~LOG_FLAG_CREATE; + } else if (!strcmp(key, "size") || !strcmp(key, "minsize") || +@@ -945,12 +995,7 @@ static int readConfigFile(const char *configFile, struct logInfo *defConfig) + free(opt); + message(MESS_ERROR, "%s:%d unknown unit '%c'\n", + configFile, lineNum, key[l]); +- if (newlog != defConfig) { +- state = STATE_ERROR; +- continue; +- } else { +- goto error; +- } ++ RAISE_ERROR(); + } else { + multiplier = 1; + } +@@ -960,12 +1005,7 @@ static int readConfigFile(const char *configFile, struct logInfo *defConfig) + message(MESS_ERROR, "%s:%d bad size '%s'\n", + configFile, lineNum, key); + free(opt); +- if (newlog != defConfig) { +- state = STATE_ERROR; +- continue; +- } else { +- goto error; +- } ++ RAISE_ERROR(); + } + if (!strncmp(opt, "size", 4)) { + newlog->criterium = ROT_SIZE; +@@ -1015,12 +1055,7 @@ static int readConfigFile(const char *configFile, struct logInfo *defConfig) + message(MESS_ERROR, + "%s:%d bad rotation count '%s'\n", + configFile, lineNum, key); +- if (newlog != defConfig) { +- state = STATE_ERROR; +- continue; +- } else { +- goto error; +- } ++ RAISE_ERROR(); + } + } + else continue; +@@ -1034,12 +1069,7 @@ static int readConfigFile(const char *configFile, struct logInfo *defConfig) + 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; +- } ++ RAISE_ERROR(); + } + } + else continue; +@@ -1052,12 +1082,7 @@ static int readConfigFile(const char *configFile, struct logInfo *defConfig) + 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; +- } ++ RAISE_ERROR(); + } + } + else continue; +@@ -1069,12 +1094,7 @@ static int readConfigFile(const char *configFile, struct logInfo *defConfig) + freeLogItem(logAddress); + if (!(newlog->logAddress = readAddress(configFile, lineNum, + "mail", &start, &buf, length))) { +- if (newlog != defConfig) { +- state = STATE_ERROR; +- continue; +- } else { +- goto error; +- } ++ RAISE_ERROR(); + } + else continue; + } else if (!strcmp(key, "nomail")) { +@@ -1177,31 +1197,8 @@ static int readConfigFile(const char *configFile, struct logInfo *defConfig) + + if (!(newlog->oldDir = readPath(configFile, lineNum, + "olddir", &start, &buf, length))) { +- if (newlog != defConfig) { +- state = STATE_ERROR; +- 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; ++ RAISE_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(key, "extension")) { + if ((key = isolateValue +@@ -1222,24 +1219,14 @@ static int readConfigFile(const char *configFile, struct logInfo *defConfig) + if (! + (newlog->compress_prog = + readPath(configFile, lineNum, "compress", &start, &buf, length))) { +- if (newlog != defConfig) { +- state = STATE_ERROR; +- continue; +- } else { +- goto error; +- } ++ RAISE_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) { +- state = STATE_ERROR; +- continue; +- } else { +- goto error; +- } ++ RAISE_ERROR(); + } + + message(MESS_DEBUG, "compress_prog is now %s\n", +@@ -1252,24 +1239,14 @@ static int readConfigFile(const char *configFile, struct logInfo *defConfig) + (newlog->uncompress_prog = + readPath(configFile, lineNum, "uncompress", + &start, &buf, length))) { +- if (newlog != defConfig) { +- state = STATE_ERROR; +- continue; +- } else { +- goto error; +- } ++ RAISE_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) { +- state = STATE_ERROR; +- continue; +- } else { +- goto error; +- } ++ RAISE_ERROR(); + } + + message(MESS_DEBUG, "uncompress_prog is now %s\n", +@@ -1285,12 +1262,7 @@ static int readConfigFile(const char *configFile, struct logInfo *defConfig) + } + + if (!(options = isolateLine(&start, &buf, length))) { +- if (newlog != defConfig) { +- state = STATE_ERROR; +- continue; +- } else { +- goto error; +- } ++ RAISE_ERROR(); + } + + if (poptParseArgvString(options, +@@ -1300,12 +1272,7 @@ static int readConfigFile(const char *configFile, struct logInfo *defConfig) + "%s:%d invalid compression options\n", + configFile, lineNum); + free(options); +- if (newlog != defConfig) { +- state = STATE_ERROR; +- continue; +- } else { +- goto error; +- } ++ RAISE_ERROR(); + } + + message(MESS_DEBUG, "compress_options is now %s\n", +@@ -1318,12 +1285,7 @@ static int readConfigFile(const char *configFile, struct logInfo *defConfig) + (newlog->compress_ext = + readPath(configFile, lineNum, "compress-ext", + &start, &buf, length))) { +- if (newlog != defConfig) { +- state = STATE_ERROR; +- continue; +- } else { +- goto error; +- } ++ RAISE_ERROR(); + } + + message(MESS_DEBUG, "compress_ext is now %s\n", +@@ -1484,45 +1446,57 @@ static int readConfigFile(const char *configFile, struct logInfo *defConfig) + globerr_msg = NULL; + if (!(newlog->flags & LOG_FLAG_MISSINGOK)) + goto error; +- } ++ } + +- if (newlog->oldDir) { ++ if (newlog->oldDir) { + for (i = 0; i < newlog->numFiles; i++) { + char *ld; ++ int rv; + 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; ++ 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); ++ 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 (newlog->oldDir[0] != '/') { ++ dirName = ld; ++ } ++ else { ++ dirName = newlog->oldDir; ++ } ++ ++ rv = stat(dirName, &sb); ++ if (rv) { ++ if (errno == ENOENT && newlog->flags & LOG_FLAG_OLDDIRCREATE) { ++ if (mkpath(dirName, newlog->olddirMode, ++ newlog->olddirUid, newlog->olddirGid)) { ++ goto error; ++ } ++ } ++ else { ++ 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; ++ 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; + state = STATE_DEFINITION_END; +diff --git a/logrotate.8 b/logrotate.8 +index 8cb9c2f..2cd2370 100644 +--- a/logrotate.8 ++++ b/logrotate.8 +@@ -216,6 +216,16 @@ file for the omitted attributes. This option can be disabled using the + \fBnocreate\fR option. + + .TP ++\fBcreateolddir \fImode\fR \fIowner\fR \fIgroup\fR ++If the directory specified by \fBolddir\fR directive does not exist, it is ++created. \fImode\fR specifies the mode for the \fBolddir\fR directory ++in octal (the same as \fBchmod\fR(2)), \fIowner\fR specifies the user name ++who will own the \fBolddir\fR directory, and \fIgroup\fR specifies the group ++the \fBolddir\fR directory will belong to. This option can be disabled using the ++\fBnocreateolddir\fR option. ++ ++ ++.TP + \fBdaily\fR + Log files are rotated every day. + +@@ -350,6 +360,10 @@ Do not truncate the original log file in place after creating a copy + New log files are not created (this overrides the \fBcreate\fR option). + + .TP ++\fBnocreateolddir\fR ++\fBolddir\fR directory is not created by logrotate when it does not exist. ++ ++.TP + \fBnodelaycompress\fR + Do not postpone compression of the previous log file to the next rotation cycle + (this overrides the \fBdelaycompress\fR option). +diff --git a/logrotate.h b/logrotate.h +index 813418e..cf42703 100644 +--- a/logrotate.h ++++ b/logrotate.h +@@ -20,6 +20,7 @@ + #define LOG_FLAG_SHRED (1 << 10) + #define LOG_FLAG_SU (1 << 11) + #define LOG_FLAG_DATEYESTERDAY (1 << 12) ++#define LOG_FLAG_OLDDIRCREATE (1 << 13) + + #define NO_MODE ((mode_t) -1) + #define NO_UID ((uid_t) -1) +@@ -55,6 +56,9 @@ struct logInfo { + gid_t createGid; + uid_t suUid; /* switch user to this uid and group to this gid */ + gid_t suGid; ++ mode_t olddirMode; ++ uid_t olddirUid; ++ uid_t olddirGid; + /* these are at the end so they end up nil */ + const char **compress_options_list; + int compress_options_count; +diff --git a/test/test b/test/test +index 25b76a6..e9ce46f 100755 +--- a/test/test ++++ b/test/test +@@ -453,9 +453,15 @@ cleanup 13 + # ------------------------------- Test 13 ------------------------------------ + preptest test.log 13 1 0 + rm -rf testdir +-mkdir testdir + $RLR test-config.13 --force + ++ls -l|grep testdir|grep "drwx------." 2>/dev/null >/dev/null ++if [ $? != 0 ]; then ++ echo "testdir should have mode 2700, but it has:" ++ ls -l|grep testdir ++ exit 3 ++fi ++ + checkoutput </dev/null + if [ $? != 0 ]; then +- echo "test.log must not contain user:nobody:rwx" ++ echo "test.log must contain user:nobody:rwx" ++ getfacl test.log + exit 3 + fi + + getfacl test.log.1|grep "user:nobody:rwx" >/dev/null + if [ $? != 0 ]; then + echo "test.log.1 must contain user:nobody:rwx" ++ getfacl test.log.1 + exit 3 + fi + +diff --git a/test/test-config.13.in b/test/test-config.13.in +index 31a29ef..dc2efd5 100644 +--- a/test/test-config.13.in ++++ b/test/test-config.13.in +@@ -4,4 +4,5 @@ create + monthly + rotate 1 + olddir &DIR&/testdir ++ createolddir 700 &USER& &GROUP& + } diff --git a/SOURCES/logrotate-3.8.6-r465.patch b/SOURCES/logrotate-3.8.6-r465.patch new file mode 100644 index 0000000..704dbfa --- /dev/null +++ b/SOURCES/logrotate-3.8.6-r465.patch @@ -0,0 +1,16 @@ +Index: /trunk/logrotate.8 +=================================================================== +--- a/logrotate.8 (revision 464) ++++ b/logrotate.8 (revision 465) +@@ -406,7 +406,8 @@ + .TP + \fBolddir \fIdirectory\fR +-Logs are moved into \fIdirectory\fR for rotation. The \fIdirectory\fR +-must be on the same physical device as the log file being rotated, +-and is assumed to be relative to the directory holding the log file ++Logs are moved into \fIdirectory\fR for rotation. The \fIdirectory\fR must be ++on the same physical device as the log file being rotated, unless \fBcopy\fR, ++\fBcopytruncate\fR or \fBrenamecopy\fR option is used. The \fIdirectory\fR ++is assumed to be relative to the directory holding the log file + unless an absolute path name is specified. When this option is used all + old versions of the log end up in \fIdirectory\fR. This option may be diff --git a/SOURCES/logrotate-3.8.6-sortglob.patch b/SOURCES/logrotate-3.8.6-sortglob.patch new file mode 100644 index 0000000..6537631 --- /dev/null +++ b/SOURCES/logrotate-3.8.6-sortglob.patch @@ -0,0 +1,209 @@ +diff --git a/logrotate.c b/logrotate.c +index 174a26b..4ef044e 100644 +--- a/logrotate.c ++++ b/logrotate.c +@@ -77,6 +77,11 @@ struct logNames { + char *baseName; + }; + ++struct compData { ++ int prefix_len; ++ const char *dformat; ++}; ++ + struct logStates { + LIST_HEAD(stateSet, logState) head; + } **states; +@@ -142,6 +147,34 @@ int switch_user_permanently(const struct logInfo *log) { + return 0; + } + ++static int compGlobResult(const void *result1, const void *result2, void *data) { ++ struct tm time; ++ time_t t1, t2; ++ struct compData *d = (struct compData *) data; ++ const char *r1 = *(const char **)(result1); ++ const char *r2 = *(const char **)(result2); ++ ++ memset(&time, 0, sizeof(struct tm)); ++ strptime(r1 + d->prefix_len, d->dformat, &time); ++ t1 = mktime(&time); ++ ++ memset(&time, 0, sizeof(struct tm)); ++ strptime(r2 + d->prefix_len, d->dformat, &time); ++ t2 = mktime(&time); ++ ++ if (t1 < t2) return -1; ++ if (t1 > t2) return 1; ++ return 0; ++} ++ ++static void sortGlobResult(glob_t *result, int prefix_len, const char *dformat) { ++ struct compData d; ++ if (!dformat || *dformat == '\0') return; ++ d.prefix_len = prefix_len; ++ d.dformat = dformat; ++ qsort_r(result->gl_pathv, result->gl_pathc, sizeof(char *), compGlobResult, &d); ++} ++ + static void unescape(char *arg) + { + char *p = arg; +@@ -923,7 +956,7 @@ int prerotateSingleLog(struct logInfo *log, int logNum, struct logState *state, + #define DATEEXT_LEN 64 + #define PATTERN_LEN (DATEEXT_LEN * 2) + char dext_str[DATEEXT_LEN]; +- char dformat[DATEEXT_LEN]; ++ char dformat[DATEEXT_LEN] = ""; + char dext_pattern[PATTERN_LEN]; + char *dext; + +@@ -1112,6 +1145,7 @@ int prerotateSingleLog(struct logInfo *log, int logNum, struct logState *state, + } + rc = glob(glob_pattern, 0, globerr, &globResult); + if (!rc && globResult.gl_pathc > 0) { ++ sortGlobResult(&globResult, strlen(rotNames->dirName) + 1 + strlen(rotNames->baseName), dformat); + for (i = 0; i < globResult.gl_pathc && !hasErrors; i++) { + struct stat sbprev; + +@@ -1176,6 +1210,7 @@ int prerotateSingleLog(struct logInfo *log, int logNum, struct logState *state, + /* remove the first (n - rotateCount) matches + * no real rotation needed, since the files have + * the date in their name */ ++ sortGlobResult(&globResult, strlen(rotNames->dirName) + 1 + strlen(rotNames->baseName), dformat); + for (i = 0; i < globResult.gl_pathc; i++) { + if (!stat((globResult.gl_pathv)[i], &fst_buf)) { + if ((i <= ((int) globResult.gl_pathc - rotateCount)) +diff --git a/test/test b/test/test +index 793bf77..25b76a6 100755 +--- a/test/test ++++ b/test/test +@@ -1347,4 +1347,73 @@ test.log 0 + test.log.1 0 zero + EOF + ++cleanup 54 ++ ++# ------------------------------- Test 54 ------------------------------------ ++# removing last log file when using %Y-%m-%d ++rm -f *test.log* ++preptest test.log 54 1 0 ++ ++DATE="" ++for i in {1..60} ++do ++ DATE=$(/bin/date "+%Y-%m-%d" --date "$i day ago" 2>/dev/null) ++ echo "x" > test.log-$DATE ++done ++ ++$RLR test-config.54 --force ++ ++if [ -e test.log-$DATE ]; then ++ echo "File test.log-$DATE should not exist (it should be deleted)" ++ exit 3 ++fi ++ ++rm -f *test.log* ++ ++cleanup 55 ++ ++# ------------------------------- Test 55 ------------------------------------ ++# removing last log file when using %s and hourly ++rm -f *test.log* ++preptest test.log 55 1 0 ++ ++DATE="" ++for i in {1..60} ++do ++ DATE=$(/bin/date "+%s" --date "$i hour ago" 2>/dev/null) ++ echo "x" > test.log-$DATE.gz ++done ++ ++$RLR test-config.55 --force ++ ++if [ -e test.log-$DATE.gz ]; then ++ echo "File test.log-$DATE.gz should not exist (it should be deleted)" ++ exit 3 ++fi ++ ++rm -f *test.log* ++ ++cleanup 56 ++ ++# ------------------------------- Test 56 ------------------------------------ ++# removing last log file when using %d-%m-%Y ++rm -f *test.log* ++preptest test.log 56 1 0 ++ ++DATE="" ++for i in {1..60} ++do ++ DATE=$(/bin/date "+%d-%m-%Y" --date "$i day ago" 2>/dev/null) ++ echo "x" > test.log-$DATE ++done ++ ++$RLR test-config.56 --force ++ ++if [ -e test.log-$DATE ]; then ++ echo "File test.log-$DATE should not exist (it should be deleted)" ++ exit 3 ++fi ++ ++rm -f *test.log* ++ + cleanup +diff --git a/test/test-config.54.in b/test/test-config.54.in +new file mode 100644 +index 0000000..c946af1 +--- /dev/null ++++ b/test/test-config.54.in +@@ -0,0 +1,8 @@ ++create ++ ++&DIR&/test.log { ++ daily ++ dateext ++ dateformat -%Y-%m-%d ++ rotate 60 ++} +diff --git a/test/test-config.55.in b/test/test-config.55.in +new file mode 100644 +index 0000000..8b10ad1 +--- /dev/null ++++ b/test/test-config.55.in +@@ -0,0 +1,21 @@ ++create ++ ++# continue and throw no error message when log file is not present ++missingok ++ ++# truncate the original log file in place after creating a copy ++copytruncate ++ ++# compress the file ++compress ++ ++# do only rotate when not empty ++notifempty ++ ++&DIR&/test.log { ++ hourly ++ dateext ++ dateformat -%s ++ rotate 60 ++ nosharedscripts ++} +diff --git a/test/test-config.56.in b/test/test-config.56.in +new file mode 100644 +index 0000000..adaf2a5 +--- /dev/null ++++ b/test/test-config.56.in +@@ -0,0 +1,8 @@ ++create ++ ++&DIR&/test.log { ++ daily ++ dateext ++ dateformat -%d-%m-%Y ++ rotate 60 ++} diff --git a/SPECS/logrotate.spec b/SPECS/logrotate.spec index e90b4e3..715294f 100644 --- a/SPECS/logrotate.spec +++ b/SPECS/logrotate.spec @@ -1,12 +1,16 @@ Summary: Rotates, compresses, removes and mails system log files Name: logrotate Version: 3.8.6 -Release: 4%{?dist} +Release: 6%{?dist} License: GPL+ Group: System Environment/Base Url: https://fedorahosted.org/logrotate/ Source: https://fedorahosted.org/releases/l/o/logrotate/logrotate-%{version}.tar.gz Patch0: logrotate-3.8.6-force.patch +Patch1: logrotate-3.8.6-r465.patch +Patch2: logrotate-3.8.6-sortglob.patch +Patch3: logrotate-3.8.6-r460.patch +Patch4: logrotate-3.8.6-compress-subject.patch Requires: coreutils >= 5.92 popt BuildRequires: libselinux-devel popt-devel libacl-devel acl @@ -27,6 +31,10 @@ log files on your system. %setup -q %patch0 -p1 -b .force +%patch1 -p1 -b .r465 +%patch2 -p1 -b .sortglob +%patch3 -p1 -b .r460 +%patch4 -p1 -b .compressmail %build make %{?_smp_mflags} RPM_OPT_FLAGS="$RPM_OPT_FLAGS" WITH_SELINUX=yes WITH_ACL=yes @@ -54,12 +62,21 @@ rm -rf $RPM_BUILD_ROOT %attr(0755, root, root) %{_sbindir}/logrotate %attr(0644, root, root) %{_mandir}/man8/logrotate.8* %attr(0644, root, root) %{_mandir}/man5/logrotate.conf.5* -%attr(0700, root, root) %{_sysconfdir}/cron.daily/logrotate +%attr(0700, root, root) %config(noreplace) %{_sysconfdir}/cron.daily/logrotate %attr(0644, root, root) %config(noreplace) %{_sysconfdir}/logrotate.conf %attr(0755, root, root) %dir %{_sysconfdir}/logrotate.d %attr(0644, root, root) %verify(not size md5 mtime) %config(noreplace) %{_localstatedir}/lib/logrotate.status %changelog +* Tue Oct 06 2015 Jan Kaluza - 3.8.6-6 +- fix #1244156 - make filename in subject consistent when used with compress + +* Thu Apr 23 2015 Jan Kaluza - 3.8.6-5 +- mention copy/copytruncate/renamecopy influence to olddir option (#1175292) +- delete last log when dateformat cannot be sorted alphabetically (#1174208) +- mark cron.daily/logrotate as config file (#1174207) +- create olddir diectory if it does not exist (#1187161) + * Fri Jan 24 2014 Daniel Mach - 3.8.6-4 - Mass rebuild 2014-01-24