Blame SOURCES/logrotate-3.8.6-state-clean.patch

eab4a5
diff --git a/logrotate.c b/logrotate.c
eab4a5
index 9faf341..06b7100 100644
eab4a5
--- a/logrotate.c
eab4a5
+++ b/logrotate.c
eab4a5
@@ -62,11 +62,12 @@ extern int asprintf(char **str, const char *fmt, ...);
eab4a5
 #endif
eab4a5
 
eab4a5
 struct logState {
eab4a5
-    char *fn;
eab4a5
-    struct tm lastRotated;	/* only tm_hour, tm_mday, tm_mon, tm_year are good! */
eab4a5
-    struct stat sb;
eab4a5
-    int doRotate;
eab4a5
-    LIST_ENTRY(logState) list;
eab4a5
+	char *fn;
eab4a5
+	struct tm lastRotated;	/* only tm_hour, tm_mday, tm_mon, tm_year are good! */
eab4a5
+	struct stat sb;
eab4a5
+	int doRotate;
eab4a5
+	int isUsed;	/* True if there is real log file in system for this state. */
eab4a5
+	LIST_ENTRY(logState) list;
eab4a5
 };
eab4a5
 
eab4a5
 struct logNames {
eab4a5
@@ -204,23 +205,17 @@ static void unescape(char *arg)
eab4a5
 }
eab4a5
 
eab4a5
 #define HASH_SIZE_MIN 64
eab4a5
-static int allocateHash(void)
eab4a5
+static int allocateHash(unsigned int hs)
eab4a5
 {
eab4a5
-	struct logInfo *log;
eab4a5
-	unsigned int hs;
eab4a5
 	int i;
eab4a5
 
eab4a5
-	hs = 0;
eab4a5
-
eab4a5
-	for (log = logs.tqh_first; log != NULL; log = log->list.tqe_next)
eab4a5
-		hs += log->numFiles;
eab4a5
-
eab4a5
-	hs *= 2;
eab4a5
-
eab4a5
 	/* Enforce some reasonable minimum hash size */
eab4a5
 	if (hs < HASH_SIZE_MIN)
eab4a5
 		hs = HASH_SIZE_MIN;
eab4a5
 
eab4a5
+	message(MESS_DEBUG, "Allocating hash table for state file, size %lu B\n",
eab4a5
+			hs * (sizeof(struct logStates *) + sizeof(struct logState) ) );
eab4a5
+
eab4a5
 	states = calloc(hs, sizeof(struct logStates *));
eab4a5
 	if (states == NULL) {
eab4a5
 		message(MESS_ERROR, "could not allocate memory for "
eab4a5
@@ -271,6 +266,7 @@ static struct logState *newState(const char *fn)
eab4a5
 	}
eab4a5
 
eab4a5
 	new->doRotate = 0;
eab4a5
+	new->isUsed = 0;
eab4a5
 
eab4a5
 	memset(&new->lastRotated, 0, sizeof(new->lastRotated));
eab4a5
 	new->lastRotated.tm_hour = now.tm_hour;
eab4a5
@@ -850,9 +846,10 @@ int findNeedRotating(struct logInfo *log, int logNum, int force)
eab4a5
 	return 1;
eab4a5
     }
eab4a5
 
eab4a5
-    state = findState(log->files[logNum]);
eab4a5
-    state->doRotate = 0;
eab4a5
-    state->sb = sb;
eab4a5
+	state = findState(log->files[logNum]);
eab4a5
+	state->doRotate = 0;
eab4a5
+	state->sb = sb;
eab4a5
+	state->isUsed = 1;
eab4a5
 
eab4a5
 	if ((sb.st_mode & S_IFMT) == S_IFLNK) {
eab4a5
 	    message(MESS_DEBUG, "  log %s is symbolic link. Rotation of symbolic"
eab4a5
@@ -1820,6 +1817,8 @@ static int writeState(char *stateFilename)
eab4a5
 	int fdsave;
eab4a5
 	struct stat sb;
eab4a5
 	char *tmpFilename = NULL;
eab4a5
+	struct tm now = *localtime(&nowSecs);
eab4a5
+	time_t now_time, last_time;
eab4a5
 
eab4a5
 	tmpFilename = malloc(strlen(stateFilename) + 5 );
eab4a5
 	if (tmpFilename == NULL) {
eab4a5
@@ -1924,9 +1923,22 @@ static int writeState(char *stateFilename)
eab4a5
 	if (bytes < 0)
eab4a5
 		error = bytes;
eab4a5
 
eab4a5
+#define SECONDS_IN_YEAR 31556926
eab4a5
+
eab4a5
 	for (i = 0; i < hashSize && error == 0; i++) {
eab4a5
 		for (p = states[i]->head.lh_first; p != NULL && error == 0;
eab4a5
 				p = p->list.le_next) {
eab4a5
+
eab4a5
+			/* Skip states which are not used for more than a year. */
eab4a5
+			now_time = mktime(&now;;
eab4a5
+			last_time = mktime(&p->lastRotated);
eab4a5
+			if (!p->isUsed && difftime(now_time, last_time) > SECONDS_IN_YEAR) {
eab4a5
+				message(MESS_DEBUG, "Removing %s from state file, "
eab4a5
+					"because it does not exist and has not been rotated for one year\n",
eab4a5
+					p->fn);
eab4a5
+				continue;
eab4a5
+			}
eab4a5
+
eab4a5
 			error = fputc('"', f) == EOF;
eab4a5
 			for (chptr = p->fn; *chptr && error == 0; chptr++) {
eab4a5
 				switch (*chptr) {
eab4a5
@@ -2010,23 +2022,27 @@ static int readState(char *stateFilename)
eab4a5
 
eab4a5
     error = stat(stateFilename, &f_stat);
eab4a5
 
eab4a5
-    if ((error && errno == ENOENT) || (!error && f_stat.st_size == 0)) {
eab4a5
-	/* create the file before continuing to ensure we have write
eab4a5
-	   access to the file */
eab4a5
-	f = fopen(stateFilename, "w");
eab4a5
-	if (!f) {
eab4a5
-	    message(MESS_ERROR, "error creating state file %s: %s\n",
eab4a5
-		    stateFilename, strerror(errno));
eab4a5
-	    return 1;
eab4a5
+	if ((error && errno == ENOENT) || (!error && f_stat.st_size == 0)) {
eab4a5
+		/* create the file before continuing to ensure we have write
eab4a5
+		access to the file */
eab4a5
+		f = fopen(stateFilename, "w");
eab4a5
+		if (!f) {
eab4a5
+			message(MESS_ERROR, "error creating state file %s: %s\n",
eab4a5
+				stateFilename, strerror(errno));
eab4a5
+			return 1;
eab4a5
+		}
eab4a5
+		fprintf(f, "logrotate state -- version 2\n");
eab4a5
+		fclose(f);
eab4a5
+
eab4a5
+		if (allocateHash(64) != 0)
eab4a5
+			return 1;
eab4a5
+
eab4a5
+		return 0;
eab4a5
+	} else if (error) {
eab4a5
+		message(MESS_ERROR, "error stat()ing state file %s: %s\n",
eab4a5
+			stateFilename, strerror(errno));
eab4a5
+		return 1;
eab4a5
 	}
eab4a5
-	fprintf(f, "logrotate state -- version 2\n");
eab4a5
-	fclose(f);
eab4a5
-	return 0;
eab4a5
-    } else if (error) {
eab4a5
-	message(MESS_ERROR, "error stat()ing state file %s: %s\n",
eab4a5
-		stateFilename, strerror(errno));
eab4a5
-	return 1;
eab4a5
-    }
eab4a5
 
eab4a5
     f = fopen(stateFilename, "r");
eab4a5
     if (!f) {
eab4a5
@@ -2050,6 +2066,13 @@ static int readState(char *stateFilename)
eab4a5
 	return 1;
eab4a5
     }
eab4a5
 
eab4a5
+	/* Try to estimate how many state entries we have in the state file.
eab4a5
+	 * We expect single entry to have around 80 characters (Of course this is
eab4a5
+	 * just an estimation). During the testing I've found out that 200 entries
eab4a5
+	 * per single hash entry gives good mem/performance ratio. */
eab4a5
+	if (allocateHash(f_stat.st_size / 80 / 200) != 0)
eab4a5
+		return 1;
eab4a5
+
eab4a5
     line++;
eab4a5
 
eab4a5
     while (fgets(buf, sizeof(buf) - 1, f)) {
eab4a5
@@ -2244,9 +2267,6 @@ int main(int argc, const char **argv)
eab4a5
     poptFreeContext(optCon);
eab4a5
     nowSecs = time(NULL);
eab4a5
 
eab4a5
-	if (allocateHash() != 0)
eab4a5
-		return 1;
eab4a5
-
eab4a5
 	if (readState(stateFile))
eab4a5
 		exit(1);
eab4a5
 
eab4a5
diff --git a/test/test b/test/test
eab4a5
index f7f3cf4..4048197 100755
eab4a5
--- a/test/test
eab4a5
+++ b/test/test
eab4a5
@@ -1460,4 +1460,61 @@ EOF
eab4a5
 checkmail test.log-$DATESTRING zero
eab4a5
 
eab4a5
 
eab4a5
+cleanup 67
eab4a5
+
eab4a5
+# ------------------------------- Test 67 ------------------------------------
eab4a5
+# firstaction and lastaction scripts should be called if no file is rotated
eab4a5
+preptest test.log 67 1 0
eab4a5
+
eab4a5
+DATESTRING=$(/bin/date +%Y%m%d)
eab4a5
+TODAY=$(/bin/date "+%Y-%m-%d" 2>/dev/null)
eab4a5
+
eab4a5
+echo removed > "test.log$TODAY"
eab4a5
+
eab4a5
+$RLR test-config.67 --force
eab4a5
+
eab4a5
+cat scriptout|grep firstaction >/dev/null
eab4a5
+if [ $? != 0 ]; then
eab4a5
+	echo "scriptout should contain 'firstaction'"
eab4a5
+	exit 3
eab4a5
+fi
eab4a5
+
eab4a5
+cat scriptout|grep lastaction >/dev/null
eab4a5
+if [ $? != 0 ]; then
eab4a5
+	echo "scriptout should contain 'lastaction'"
eab4a5
+	exit 3
eab4a5
+fi
eab4a5
+
eab4a5
+cleanup 68
eab4a5
+
eab4a5
+# ------------------------------- Test 68 ------------------------------------
eab4a5
+# Old state file entries should be removed when not used. Logrotate should
eab4a5
+# not freeze on big state file.
eab4a5
+preptest test.log 68 1 0
eab4a5
+
eab4a5
+cat > state << EOF
eab4a5
+logrotate state -- version 1
eab4a5
+"$PWD/test.log" 2000-1-1
eab4a5
+EOF
eab4a5
+
eab4a5
+for i in {1..200000}
eab4a5
+do
eab4a5
+   echo "\"$PWD/removed.log$i\" 2000-1-1" >> state
eab4a5
+done
eab4a5
+
eab4a5
+
eab4a5
+$RLR test-config.68 --force
eab4a5
+
eab4a5
+cat state|grep test.log >/dev/null
eab4a5
+if [ $? != 0 ]; then
eab4a5
+	echo "state file should contain 'test.log'"
eab4a5
+	exit 3
eab4a5
+fi
eab4a5
+
eab4a5
+cat state|grep removed.log >/dev/null
eab4a5
+if [ $? == 0 ]; then
eab4a5
+	echo "state file should not contain 'removed.log'"
eab4a5
+	exit 3
eab4a5
+fi
eab4a5
+
eab4a5
 cleanup
eab4a5
diff --git a/test/test-config.67.in b/test/test-config.67.in
eab4a5
new file mode 100644
eab4a5
index 0000000..69b9fff
eab4a5
--- /dev/null
eab4a5
+++ b/test/test-config.67.in
eab4a5
@@ -0,0 +1,16 @@
eab4a5
+create
eab4a5
+
eab4a5
+&DIR&/test.log {
eab4a5
+    daily
eab4a5
+    dateext
eab4a5
+    dateformat %Y-%m-%d
eab4a5
+    rotate 1
eab4a5
+
eab4a5
+	firstaction
eab4a5
+		echo "firstaction" > scriptout
eab4a5
+	endscript
eab4a5
+
eab4a5
+	lastaction
eab4a5
+		echo "lastaction" >> scriptout
eab4a5
+	endscript
eab4a5
+}
eab4a5
diff --git a/test/test-config.68.in b/test/test-config.68.in
eab4a5
new file mode 100644
eab4a5
index 0000000..e8e1c79
eab4a5
--- /dev/null
eab4a5
+++ b/test/test-config.68.in
eab4a5
@@ -0,0 +1,6 @@
eab4a5
+create
eab4a5
+
eab4a5
+&DIR&/test.log {
eab4a5
+    daily
eab4a5
+    rotate 1
eab4a5
+}