|
|
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 |
+}
|