File logrotate-weekly-rotation.patch of Package logrotate.10250

From bd2638856dbbb6c0a47beb85fe8a8a628160772e Mon Sep 17 00:00:00 2001
From: Kamil Dudka <kdudka@redhat.com>
Date: Wed, 7 Dec 2016 16:34:13 +0100
Subject: [PATCH] weekly: trigger the rotation more predictably

... by ignoring the exact time.  If the (absolute) day counter
advances by 7+ days since the last rotation, a new rotation is
triggered.

Additionally, introduce an optional argument of the 'weekly' directive
to trigger the rotation on a selected day of the week.  If the argument
is omitted, default to Sunday to preserve backward compatibility.

Closes #93
---
 config.c       | 19 +++++++++++++++++++
 logrotate.8.in | 12 ++++++------
 logrotate.c    | 39 +++++++++++++++++++++++++++++----------
 logrotate.h    |  1 +
 4 files changed, 55 insertions(+), 16 deletions(-)

Index: logrotate-3.11.0/config.c
===================================================================
--- logrotate-3.11.0.orig/config.c
+++ logrotate-3.11.0/config.c
@@ -421,6 +421,7 @@ static void copyLogInfo(struct logInfo *
     if (from->oldDir)
 	to->oldDir = strdup(from->oldDir);
     to->criterium = from->criterium;
+    to->weekday = from->weekday;
     to->threshhold = from->threshhold;
     to->minsize = from->minsize;
     to->maxsize = from->maxsize;
@@ -1081,7 +1082,25 @@ static int readConfigFile(const char *co
 				} else if (!strcmp(key, "monthly")) {
 					newlog->criterium = ROT_MONTHLY;
 				} else if (!strcmp(key, "weekly")) {
+					unsigned weekday;
+					char tmp;
 					newlog->criterium = ROT_WEEKLY;
+					free(key);
+					key = isolateLine(&start, &buf, length);
+					if (key == NULL || key[0] == '\0') {
+						/* default to Sunday if no argument was given */
+						newlog->weekday = 0;
+						continue;
+					}
+
+					if (1 == sscanf(key, "%u%c", &weekday, &tmp) && weekday <= 7) {
+						/* use the selected weekday, 7 means "once per week" */
+						newlog->weekday = weekday;
+						continue;
+					}
+					message(MESS_ERROR, "%s:%d bad weekly directive '%s'\n",
+							configFile, lineNum, key);
+					goto error;
 				} else if (!strcmp(key, "yearly")) {
 					newlog->criterium = ROT_YEARLY;
 				} else if (!strcmp(key, "rotate")) {
Index: logrotate-3.11.0/logrotate.8.in
===================================================================
--- logrotate-3.11.0.orig/logrotate.8.in
+++ logrotate-3.11.0/logrotate.8.in
@@ -568,12 +568,12 @@ patterns, the current taboo pattern list
 is replaced. At startup, the taboo pattern list is empty.
 
 .TP
-\fBweekly\fR
-Log files are rotated if the current weekday is less than the weekday
-of the last rotation or if more than a week has passed since the last
-rotation. This is normally the same as rotating logs on the first day
-of the week, but it works better if \fIlogrotate\fR is not run every
-night.
+\fBweekly\fR [\fIweekday\fR]
+Log files are rotated once each \fIweekday\fR, or if the date is advanced by at
+least 7 days since the last rotation (while ignoring the exact time).  The
+\fIweekday\fR intepretation is following:  0 means Sunday, 1 means Monday, ...,
+6 means Saturday; the special value 7 means each 7 days, irrespectively of
+weekday.  Defaults to 0 if the \fIweekday\fR argument is omitted.
 
 .TP
 \fByearly\fR
Index: logrotate-3.11.0/logrotate.c
===================================================================
--- logrotate-3.11.0.orig/logrotate.c
+++ logrotate-3.11.0/logrotate.c
@@ -1075,6 +1075,27 @@ static int copyTruncate(char *currLog, c
     return 0;
 }
 
+/* return value similar to mktime() but the exact time is ignored */
+static time_t mktimeFromDateOnly(const struct tm *src)
+{
+    /* explicit struct copy to retain C89 compatibility */
+    struct tm tmp;
+    memcpy(&tmp, src, sizeof tmp);
+
+    /* abstract out (nullify) fields expressing the exact time */
+    tmp.tm_hour = 0;
+    tmp.tm_min  = 0;
+    tmp.tm_sec  = 0;
+    return mktime(&tmp);
+}
+
+/* return by how many days the date was advanced but ignore exact time */
+static int daysElapsed(const struct tm *now, const struct tm *last)
+{
+    const time_t diff = mktimeFromDateOnly(now) - mktimeFromDateOnly(last);
+    return diff / (24 * 3600);
+}
+
 int findNeedRotating(struct logInfo *log, int logNum, int force)
 {
     struct stat sb;
@@ -1169,18 +1190,16 @@ int findNeedRotating(struct logInfo *log
 	       state->lastRotated.tm_mon != now.tm_mon ||
 	       state->lastRotated.tm_mday != now.tm_mday ||
 	       state->lastRotated.tm_hour != now.tm_hour) {
+	int days;
 	switch (log->criterium) {
 	case ROT_WEEKLY:
-	    /* rotate if:
-	       1) the current weekday is before the weekday of the
-	       last rotation
-	       2) more then a week has passed since the last
-	       rotation */
-	    state->doRotate = ((now.tm_wday < state->lastRotated.tm_wday)
-			       ||
-			       ((mktime(&now) -
-				 mktime(&state->lastRotated)) >
-				(7 * 24 * 3600)));
+	    days = daysElapsed(&now, &state->lastRotated);
+	    /* rotate if date is advanced by 7+ days (exact time is ignored) */
+	    state->doRotate = (days >= 7)
+		/* ... or if we have not yet rotated today */
+		|| (days >= 1
+			/* ... and the selected weekday is today */
+			&& now.tm_wday == log->weekday);
 	    if (!state->doRotate) {
 	    message(MESS_DEBUG, "  log does not need rotating "
 		    "(log has been rotated at %d-%d-%d %d:%d, "
Index: logrotate-3.11.0/logrotate.h
===================================================================
--- logrotate-3.11.0.orig/logrotate.h
+++ logrotate-3.11.0/logrotate.h
@@ -45,6 +45,7 @@ struct logInfo {
     char *oldDir;
     enum { ROT_HOURLY, ROT_DAYS, ROT_WEEKLY, ROT_MONTHLY, ROT_YEARLY, ROT_SIZE
             } criterium;
+    int weekday; /* used by ROT_WEEKLY only */
     unsigned long long threshhold;
     unsigned long long maxsize;
     unsigned long long minsize;
openSUSE Build Service is sponsored by