--- logrotate-3.7.1/logrotate.h.dateext 2003-08-07 13:13:14.000000000 +0200 +++ logrotate-3.7.1/logrotate.h 2005-05-03 14:17:28.732914132 +0200 @@ -15,6 +15,7 @@ #define LOG_FLAG_MAILFIRST (1 << 6) #define LOG_FLAG_SHAREDSCRIPTS (1 << 7) #define LOG_FLAG_COPY (1 << 8) +#define LOG_FLAG_DATEEXT (1 << 9) #define NO_FORCE_ROTATE 0 #define FORCE_ROTATE 1 --- logrotate-3.7.1/logrotate.c.dateext 2005-05-03 14:17:28.730914415 +0200 +++ logrotate-3.7.1/logrotate.c 2005-05-03 14:31:29.916899912 +0200 @@ -11,6 +11,7 @@ #include #include #include +#include #ifdef WITH_SELINUX #include @@ -23,6 +24,10 @@ #include "log.h" #include "logrotate.h" +#if !defined(GLOB_ABORTED) && defined(GLOB_ABEND) +#define GLOB_ABORTED GLOB_ABEND +#endif + typedef struct { char * fn; struct tm lastRotated; /* only tm.mon, tm_mday, tm_year are good! */ @@ -43,6 +48,14 @@ char * mailCommand = DEFAULT_MAIL_COMMAND; time_t nowSecs = 0; +static int globerr(const char * pathname, int theerr) { + message(MESS_ERROR, "error accessing %s: %s\n", pathname, + strerror(theerr)); + + /* We want the glob operation to continue, so return 0 */ + return 1; +} + static logState * findState(const char * fn, struct stateSet * sip) { int i; logState * states = sip->states; @@ -93,6 +106,17 @@ return rc; } +static int removeLogFile(char * name) { + message(MESS_DEBUG, "removing old log %s\n", name); + + if (!debug && unlink(name)) { + message(MESS_ERROR, "Failed to remove old log %s: %s\n", + name, strerror(errno)); + return 1; + } + return 0; +} + static int compressLogFile(char * name, logInfo * log, struct stat *sb) { char * compressedName; const char ** fullCommand; @@ -237,6 +261,25 @@ return rc; } +static int mailLogWrapper (char * mailFilename, char * mailCommand, int logNum, 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(mailFilename, mailCommand, + log->uncompress_prog, log->logAddress, + log->files[logNum])) + return 1; + } else { + if (mailLog(mailFilename, mailCommand, NULL, + log->logAddress, mailFilename)) + return 1; + } + return 0; +} + static int copyTruncate(char * currLog, char * saveLog, struct stat * sb, int flags) { char buf[BUFSIZ]; int fdcurr = -1, fdsave = -1; @@ -456,6 +499,9 @@ char * baseName; char * dirName; char * firstRotated; + char * glob_pattern; + glob_t globResult; + int rc; size_t alloc_size; int rotateCount = log->rotateCount ? log->rotateCount : 1; int logStart = (log->logStart == -1) ? 1 : log->logStart; @@ -486,7 +532,7 @@ alloc_size = strlen(dirName) + strlen(baseName) + strlen(log->files[logNum]) + strlen(fileext) + - strlen(compext) + 10; + strlen(compext) + 18; oldName = alloca(alloc_size); newName = alloca(alloc_size); @@ -508,25 +554,106 @@ /* First compress the previous log when necessary */ if (log->flags & LOG_FLAG_COMPRESS && log->flags & LOG_FLAG_DELAYCOMPRESS) { - struct stat sbprev; - - sprintf(oldName, "%s/%s.%d%s", dirName, baseName, logStart, fileext); - if (stat(oldName, &sbprev)) { - message(MESS_DEBUG, "previous log %s does not exist\n", - oldName); - } else { - hasErrors = compressLogFile(oldName, log, &sbprev); + if (log->flags & LOG_FLAG_DATEEXT) { + /* glob for uncompressed files with our pattern */ + glob_pattern = malloc(strlen(dirName) + strlen(baseName) + + strlen(fileext) + 44 ); + sprintf(glob_pattern, + "%s/%s-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]%s", + dirName, baseName, fileext); + rc = glob(glob_pattern, 0, globerr, &globResult); + if (!rc && globResult.gl_pathc > 0) { + for (i = 0; i < globResult.gl_pathc && !hasErrors; i++) { + struct stat sbprev; + + sprintf(oldName,"%s",(globResult.gl_pathv)[i]); + if (stat(oldName, &sbprev)) { + message(MESS_DEBUG, "previous log %s does not exist\n", + oldName); + } else { + hasErrors = compressLogFile(oldName, log, &sbprev); + } + } + } else { + message (MESS_DEBUG, "glob finding logs to compress failed\n"); + /* fallback to old behaviour */ + sprintf(oldName, "%s/%s.%d%s", dirName, baseName, logStart, fileext); + } + globfree(&globResult); + free(glob_pattern); + } else { + struct stat sbprev; + + sprintf(oldName, "%s/%s.%d%s", dirName, baseName, logStart, fileext); + if (stat(oldName, &sbprev)) { + message(MESS_DEBUG, "previous log %s does not exist\n", + oldName); + } else { + hasErrors = compressLogFile(oldName, log, &sbprev); + } } } + firstRotated = alloca(strlen(dirName) + strlen(baseName) + + strlen(fileext) + strlen(compext) + 30); + + if(log->flags & LOG_FLAG_DATEEXT) { + /* glob for compressed files with our pattern + * and compress ext */ + glob_pattern = malloc(strlen(dirName)+strlen(baseName) + +strlen(fileext)+strlen(compext)+44); + sprintf(glob_pattern, + "%s/%s-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]%s%s", + dirName, baseName, fileext, compext); + rc = glob(glob_pattern, 0, globerr, &globResult); + if (!rc) { + /* search for files to drop, if we find one remember it, + * if we find another one mail and remove the first and + * remember the second and so on */ + struct stat fst_buf; + int mail_out = -1; + /* remove the first (n - rotateCount) matches + * no real rotation needed, since the files have + * the date in their name */ + for (i = 0; i < globResult.gl_pathc; i++) { + if( !stat((globResult.gl_pathv)[i],&fst_buf) ) { + if (i <= ((int)globResult.gl_pathc - rotateCount)) { + if ( mail_out != -1 ) { + if (!hasErrors && log->logAddress) { + char * mailFilename = (globResult.gl_pathv)[mail_out]; + hasErrors = mailLogWrapper(mailFilename, mailCommand, logNum, log); + if (!hasErrors) + hasErrors = removeLogFile(mailFilename); + } + } + mail_out = i; + } + } + } + if ( mail_out != -1 ) { + /* oldName is oldest Backup found (for unlink later) */ + sprintf(oldName, "%s", (globResult.gl_pathv)[mail_out]); + strcpy(disposeName, oldName); + } else + disposeName = NULL; + } else { + message (MESS_DEBUG, "glob finding old rotated logs failed\n"); + disposeName = NULL; + } + /* firstRotated is most recently created/compressed rotated log */ + sprintf(firstRotated, "%s/%s-%04d%02d%02d%s%s", + dirName, baseName, now.tm_year+1900, + now.tm_mon+1, now.tm_mday, fileext, compext); + globfree(&globResult); + free(glob_pattern); + } else { + sprintf(oldName, "%s/%s.%d%s%s", dirName, baseName, logStart + rotateCount, fileext, compext); strcpy(newName, oldName); strcpy(disposeName, oldName); - firstRotated = alloca(strlen(dirName) + strlen(baseName) + - strlen(fileext) + strlen(compext) + 30); sprintf(firstRotated, "%s/%s.%d%s%s", dirName, baseName, logStart, fileext, (log->flags & LOG_FLAG_DELAYCOMPRESS) ? "" : compext); @@ -582,12 +709,27 @@ hasErrors = 1; } } - } - + } + } /* !LOG_FLAG_DATEEXT */ + finalName = oldName; - /* note: the gzip extension is *not* used here! */ - sprintf(finalName, "%s/%s.%d%s", dirName, baseName, logStart, fileext); + if(log->flags & LOG_FLAG_DATEEXT) { + char * destFile = alloca(strlen(dirName) + strlen(baseName) + + strlen(fileext) + strlen(compext) + 30); + struct stat fst_buf; + sprintf(finalName, "%s/%s-%04d%02d%02d%s", + dirName, baseName, now.tm_year+1900, + now.tm_mon+1, now.tm_mday, fileext); + sprintf(destFile, "%s%s", finalName, compext); + if(!stat(destFile,&fst_buf)) { + message (MESS_DEBUG, "destination %s already exists, skipping rotation\n", firstRotated); + hasErrors = 1; + } + } else { + /* note: the gzip extension is *not* used here! */ + sprintf(finalName, "%s/%s.%d%s", dirName, baseName, logStart, fileext); + } /* if the last rotation doesn't exist, that's okay */ if (!debug && access(disposeName, F_OK)) { @@ -596,9 +738,6 @@ disposeName = NULL; } - free(dirName); - free(baseName); - if (!hasErrors) { if (log->pre && !(log->flags & LOG_FLAG_SHAREDSCRIPTS)) { message(MESS_DEBUG, "running prerotate script\n"); @@ -744,6 +883,8 @@ } } #endif + free(dirName); + free(baseName); return hasErrors; } --- logrotate-3.7.1/config.c.dateext 2003-08-07 13:13:14.000000000 +0200 +++ logrotate-3.7.1/config.c 2005-05-03 14:17:28.734913850 +0200 @@ -511,6 +511,14 @@ newlog->flags &= ~LOG_FLAG_IFEMPTY; *endtag = oldchar, start = endtag; + } else if (!strcmp(start, "dateext")) { + newlog->flags |= LOG_FLAG_DATEEXT; + + *endtag = oldchar, start = endtag; + } else if (!strcmp(start, "nodateext")) { + newlog->flags &= ~LOG_FLAG_DATEEXT; + + *endtag = oldchar, start = endtag; } else if (!strcmp(start, "noolddir")) { newlog->oldDir = NULL; --- logrotate-3.7.1/logrotate.8.dateext 2005-05-03 14:17:28.722915545 +0200 +++ logrotate-3.7.1/logrotate.8 2005-05-03 14:17:28.735913708 +0200 @@ -205,6 +205,11 @@ Log files are rotated every day. .TP +\fBdateext\fR +Archive old versions of log files adding a daily extension like YYYYMMDD +instead of simply adding a number. + +.TP \fBdelaycompress\fR Postpone compression of the previous log file to the next rotation cycle. This has only effect when used in combination with \fBcompress\fR. logrotate-3.7.1-maxage.patch: config.c | 15 +++++++++++++++ logrotate.8 | 6 ++++++ logrotate.c | 25 +++++++++++++++++++++++-- logrotate.h | 1 + 4 files changed, 45 insertions(+), 2 deletions(-) --- NEW FILE logrotate-3.7.1-maxage.patch --- --- logrotate-3.7.1/logrotate.c.maxage 2005-05-03 14:46:08.154599467 +0200 +++ logrotate-3.7.1/logrotate.c 2005-05-03 14:50:43.707983574 +0200 @@ -617,7 +617,10 @@ * the date in their name */ for (i = 0; i < globResult.gl_pathc; i++) { if( !stat((globResult.gl_pathv)[i],&fst_buf) ) { - if (i <= ((int)globResult.gl_pathc - rotateCount)) { + if ((i <= ((int)globResult.gl_pathc - rotateCount)) + || ((log->rotateAge > 0) + && (((nowSecs - fst_buf.st_mtime)/60/60/24) + > log->rotateAge))) { if ( mail_out != -1 ) { if (!hasErrors && log->logAddress) { char * mailFilename = (globResult.gl_pathv)[mail_out]; @@ -647,6 +650,22 @@ globfree(&globResult); free(glob_pattern); } else { + if ( log->rotateAge ) { + struct stat fst_buf; + for (i=1; i <= rotateCount; i++) { + sprintf(oldName, "%s/%s.%d%s%s", dirName, baseName, + rotateCount + 1, fileext, compext); + if(!stat(oldName,&fst_buf) + && (((nowSecs - fst_buf.st_mtime)/60/60/24) + > log->rotateAge)) { + char * mailFilename = (globResult.gl_pathv)[i]; + if (!hasErrors && log->logAddress) + hasErrors = mailLogWrapper(mailFilename, mailCommand, logNum, log); + if (!hasErrors) + hasErrors = removeLogFile(mailFilename); + } + } + } sprintf(oldName, "%s/%s.%d%s%s", dirName, baseName, logStart + rotateCount, fileext, compext); @@ -1171,7 +1190,9 @@ int main(int argc, const char ** argv) { logInfo defConfig = { NULL, NULL, 0, NULL, ROT_SIZE, - /* threshHold */ 1024 * 1024, 0, + /* threshHold */ 1024 * 1024, + /* rotateCount */ 0, + /* rotateAge */ 0, /* log start */ -1, /* pre, post */ NULL, NULL, /* first, last */ NULL, NULL, --- logrotate-3.7.1/logrotate.h.maxage 2005-05-03 14:51:33.207865757 +0200 +++ logrotate-3.7.1/logrotate.h 2005-05-03 14:51:58.732195346 +0200 @@ -35,6 +35,7 @@ enum { ROT_DAYS, ROT_WEEKLY, ROT_MONTHLY, ROT_SIZE, ROT_FORCE } criterium; unsigned int threshhold; int rotateCount; + int rotateAge; int logStart; char * pre, * post, * first, * last; char * logAddress; --- logrotate-3.7.1/config.c.maxage 2005-05-03 14:39:35.690020183 +0200 +++ logrotate-3.7.1/config.c 2005-05-03 14:40:33.429711948 +0200 @@ -678,6 +678,21 @@ } *endtag = oldchar, start = endtag; } + } else if (!strcmp(start, "maxage")) { + *endtag = oldchar, start = endtag; + + if (!isolateValue(configFile, lineNum, "maxage count", &start, + &endtag)) { + oldchar = *endtag, *endtag = '\0'; + + newlog->rotateAge = strtoul(start, &chptr, 0); + if (*chptr || newlog->rotateAge < 0) { + message(MESS_ERROR, "%s:%d bad maximum age '%s'\n", + configFile, lineNum, start); + return 1; + } + *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); --- logrotate-3.7.1/logrotate.8.maxage 2005-05-03 14:41:55.736877916 +0200 +++ logrotate-3.7.1/logrotate.8 2005-05-03 14:45:33.464585908 +0200 @@ -256,6 +256,12 @@ instead of the just-rotated file (this is the default). .TP +\fBmaxage\fR \fIcount\fR +Remove rotated logs older than days. The age is only checked +if the logfile is to be rotated. The files are mailed to the +configured address if \fBmaillast\fR and \fBmail\fR are configured. + +.TP \fBmissingok\fR If the log file is missing, go on to the next one without issuing an error message. See also \fBnomissingok\fR.