|
|
ee3a35 |
From 31350bc0b935920f9924317b4cb3602602420f83 Mon Sep 17 00:00:00 2001
|
|
|
ee3a35 |
From: Jiri Vymazal <jvymazal@redhat.com>
|
|
|
ee3a35 |
Date: Fri, 16 Nov 2018 13:16:13 +0100
|
|
|
ee3a35 |
Subject: [PATCH] bugfix imfile: file change was not reliably detected
|
|
|
ee3a35 |
|
|
|
ee3a35 |
A change in the inode was not detected under all circumstances,
|
|
|
ee3a35 |
most importantly not in some logrotate cases.
|
|
|
ee3a35 |
|
|
|
ee3a35 |
Previously, truncation was only detected at end of file. Especially with
|
|
|
ee3a35 |
busy files that could cause loss of data and possibly also stall imfile
|
|
|
ee3a35 |
reading. The new code now also checks during each read. Obviously, there
|
|
|
ee3a35 |
is some additional overhead associated with that, but this is unavoidable.
|
|
|
ee3a35 |
|
|
|
ee3a35 |
It still is highly recommended NOT to turn on "reopenOnTruncate" in imfile.
|
|
|
ee3a35 |
Note that there are also inherant reliability issues. There is no way to
|
|
|
ee3a35 |
"fix" these, as they are caused by races between the process(es) who truncate
|
|
|
ee3a35 |
and rsyslog reading the file. But with the new code, the "problem window"
|
|
|
ee3a35 |
should be much smaller and, more importantly, imfile should not stall.
|
|
|
ee3a35 |
---
|
|
|
ee3a35 |
plugins/imfile/imfile.c | 13 ++++++++++++-
|
|
|
ee3a35 |
runtime/rsyslog.h | 1 +
|
|
|
ee3a35 |
runtime/stream.c | 116 ++++++++-
|
|
|
ee3a35 |
runtime/stream.h | 7 +++++++
|
|
|
ee3a35 |
4 files changed, 125 insertions(+), 11 deletions(-)
|
|
|
ee3a35 |
|
|
|
ee3a35 |
diff --git a/plugins/imfile/imfile.c b/plugins/imfile/imfile.c
|
|
|
ee3a35 |
index f4a4ef9b7..6be8b2999 100644
|
|
|
ee3a35 |
--- a/plugins/imfile/imfile.c
|
|
|
ee3a35 |
+++ b/plugins/imfile/imfile.c
|
|
|
ee3a35 |
@@ -740,8 +740,19 @@ detect_updates(fs_edge_t *const edge)
|
|
|
ee3a35 |
act_obj_unlink(act);
|
|
|
ee3a35 |
restart = 1;
|
|
|
ee3a35 |
break;
|
|
|
ee3a35 |
+ } else if(fileInfo.st_ino != act->ino) {
|
|
|
ee3a35 |
+ DBGPRINTF("file '%s' inode changed from %llu to %llu, unlinking from "
|
|
|
ee3a35 |
+ "internal lists\n", act->name, (long long unsigned) act->ino,
|
|
|
ee3a35 |
+ (long long unsigned) fileInfo.st_ino);
|
|
|
ee3a35 |
+ if(act->pStrm != NULL) {
|
|
|
ee3a35 |
+ /* we do no need to re-set later, as act_obj_unlink
|
|
|
ee3a35 |
+ * will destroy the strm obj */
|
|
|
ee3a35 |
+ strmSet_checkRotation(act->pStrm, STRM_ROTATION_DO_NOT_CHECK);
|
|
|
ee3a35 |
+ }
|
|
|
ee3a35 |
+ act_obj_unlink(act);
|
|
|
ee3a35 |
+ restart = 1;
|
|
|
ee3a35 |
+ break;
|
|
|
ee3a35 |
}
|
|
|
ee3a35 |
- // TODO: add inode check for change notification!
|
|
|
ee3a35 |
|
|
|
ee3a35 |
/* Note: active nodes may get deleted, so we need to do the
|
|
|
ee3a35 |
* pointer advancement at the end of the for loop!
|
|
|
ee3a35 |
diff --git a/runtime/rsyslog.h b/runtime/rsyslog.h
|
|
|
ee3a35 |
index 61d0af623..22a1c46d1 100644
|
|
|
ee3a35 |
--- a/runtime/rsyslog.h
|
|
|
ee3a35 |
+++ b/runtime/rsyslog.h
|
|
|
ee3a35 |
@@ -183,6 +183,7 @@ enum rsRetVal_ /** return value. All methods return this if not specified otherwise */
|
|
|
ee3a35 |
RS_RET_NOT_IMPLEMENTED = -7, /**< implementation is missing (probably internal error or lazyness ;)) */
|
|
|
ee3a35 |
RS_RET_OUT_OF_MEMORY = -6, /**< memory allocation failed */
|
|
|
ee3a35 |
RS_RET_PROVIDED_BUFFER_TOO_SMALL = -50,/**< the caller provided a buffer, but the called function sees the size of this buffer is too small - operation not carried out */
|
|
|
ee3a35 |
+ RS_RET_FILE_TRUNCATED = -51, /**< (input) file was truncated, not an error but a status */
|
|
|
ee3a35 |
RS_RET_TRUE = -3, /**< to indicate a true state (can be used as TRUE, legacy) */
|
|
|
ee3a35 |
RS_RET_FALSE = -2, /**< to indicate a false state (can be used as FALSE, legacy) */
|
|
|
ee3a35 |
RS_RET_NO_IRET = -8, /**< This is a trick for the debuging system - it means no iRet is provided */
|
|
|
ee3a35 |
diff --git a/runtime/stream.c b/runtime/stream.c
|
|
|
ee3a35 |
index 2d494c612..5b52591ef 100644
|
|
|
ee3a35 |
--- a/runtime/stream.c
|
|
|
ee3a35 |
+++ b/runtime/stream.c
|
|
|
ee3a35 |
@@ -400,6 +400,7 @@ static rsRetVal strmOpenFile(strm_t *pThis)
|
|
|
ee3a35 |
CHKiRet(doPhysOpen(pThis));
|
|
|
ee3a35 |
|
|
|
ee3a35 |
pThis->iCurrOffs = 0;
|
|
|
ee3a35 |
+ pThis->iBufPtrMax = 0;
|
|
|
ee3a35 |
CHKiRet(getFileSize(pThis->pszCurrFName, &offset));
|
|
|
ee3a35 |
if(pThis->tOperationsMode == STREAMMODE_WRITE_APPEND) {
|
|
|
ee3a35 |
pThis->iCurrOffs = offset;
|
|
|
ee3a35 |
@@ -574,7 +574,7 @@ strmNextFile(strm_t *pThis)
|
|
|
ee3a35 |
* a file change is detected only if the inode changes. -- rgerhards, 2011-01-10
|
|
|
ee3a35 |
*/
|
|
|
ee3a35 |
static rsRetVal
|
|
|
ee3a35 |
-strmHandleEOFMonitor(strm_t *pThis)
|
|
|
ee3a35 |
+strmHandleEOFMonitor(strm_t *const pThis)
|
|
|
ee3a35 |
{
|
|
|
ee3a35 |
DEFiRet;
|
|
|
ee3a35 |
struct stat statName;
|
|
|
ee3a35 |
@@ -611,7 +611,7 @@ strmHandleEOFMonitor(strm_t *pThis)
|
|
|
ee3a35 |
* rgerhards, 2008-02-13
|
|
|
ee3a35 |
*/
|
|
|
ee3a35 |
static rsRetVal
|
|
|
ee3a35 |
-strmHandleEOF(strm_t *pThis)
|
|
|
ee3a35 |
+strmHandleEOF(strm_t *const pThis)
|
|
|
ee3a35 |
{
|
|
|
ee3a35 |
DEFiRet;
|
|
|
ee3a35 |
|
|
|
ee3a35 |
@@ -629,7 +629,13 @@ strmHandleEOF(strm_t *pThis)
|
|
|
ee3a35 |
CHKiRet(strmNextFile(pThis));
|
|
|
ee3a35 |
break;
|
|
|
ee3a35 |
case STREAMTYPE_FILE_MONITOR:
|
|
|
ee3a35 |
- CHKiRet(strmHandleEOFMonitor(pThis));
|
|
|
ee3a35 |
+ DBGOPRINT((obj_t*) pThis, "file '%s' (%d) EOF, rotationCheck %d\n",
|
|
|
ee3a35 |
+ pThis->pszCurrFName, pThis->fd, pThis->rotationCheck);
|
|
|
ee3a35 |
+ if(pThis->rotationCheck == STRM_ROTATION_DO_CHECK) {
|
|
|
ee3a35 |
+ CHKiRet(strmHandleEOFMonitor(pThis));
|
|
|
ee3a35 |
+ } else {
|
|
|
ee3a35 |
+ ABORT_FINALIZE(RS_RET_EOF);
|
|
|
ee3a35 |
+ }
|
|
|
ee3a35 |
break;
|
|
|
ee3a35 |
}
|
|
|
ee3a35 |
|
|
|
ee3a35 |
@@ -636,6 +637,75 @@ strmHandleEOF(strm_t *pThis)
|
|
|
ee3a35 |
RETiRet;
|
|
|
ee3a35 |
}
|
|
|
ee3a35 |
|
|
|
ee3a35 |
+
|
|
|
ee3a35 |
+/* helper to checkTruncation */
|
|
|
ee3a35 |
+static rsRetVal
|
|
|
ee3a35 |
+rereadTruncated(strm_t *const pThis, const char *const reason)
|
|
|
ee3a35 |
+{
|
|
|
ee3a35 |
+ DEFiRet;
|
|
|
ee3a35 |
+
|
|
|
ee3a35 |
+ LogMsg(errno, RS_RET_FILE_TRUNCATED, LOG_WARNING, "file '%s': truncation detected, "
|
|
|
ee3a35 |
+ "(%s) - re-start reading from beginning",
|
|
|
ee3a35 |
+ pThis->pszCurrFName, reason);
|
|
|
ee3a35 |
+ DBGPRINTF("checkTruncation, file %s last buffer CHANGED\n", pThis->pszCurrFName);
|
|
|
ee3a35 |
+ CHKiRet(strmCloseFile(pThis));
|
|
|
ee3a35 |
+ CHKiRet(strmOpenFile(pThis));
|
|
|
ee3a35 |
+ iRet = RS_RET_FILE_TRUNCATED;
|
|
|
ee3a35 |
+
|
|
|
ee3a35 |
+finalize_it:
|
|
|
ee3a35 |
+ RETiRet;
|
|
|
ee3a35 |
+}
|
|
|
ee3a35 |
+/* helper to read:
|
|
|
ee3a35 |
+ * Check if file has been truncated since last read and, if so, re-set reading
|
|
|
ee3a35 |
+ * to begin of file. To detect truncation, we try to re-read the last block.
|
|
|
ee3a35 |
+ * If that does not succeed or different data than from the original read is
|
|
|
ee3a35 |
+ * returned, truncation is assumed.
|
|
|
ee3a35 |
+ * NOTE: this function must be called only if truncation is enabled AND
|
|
|
ee3a35 |
+ * when the previous read buffer still is valid (aka "before the next read").
|
|
|
ee3a35 |
+ * It is ok to call with a 0-size buffer, which we than assume as begin of
|
|
|
ee3a35 |
+ * reading. In that case, no truncation will be detected.
|
|
|
ee3a35 |
+ * rgerhards, 2018-09-20
|
|
|
ee3a35 |
+ */
|
|
|
ee3a35 |
+static rsRetVal
|
|
|
ee3a35 |
+checkTruncation(strm_t *const pThis)
|
|
|
ee3a35 |
+{
|
|
|
ee3a35 |
+ DEFiRet;
|
|
|
ff08e8 |
+ off64_t ret;
|
|
|
ee3a35 |
+ off64_t backseek;
|
|
|
ee3a35 |
+ assert(pThis->bReopenOnTruncate);
|
|
|
ee3a35 |
+
|
|
|
ee3a35 |
+ DBGPRINTF("checkTruncation, file %s, iBufPtrMax %zd\n", pThis->pszCurrFName, pThis->iBufPtrMax);
|
|
|
ee3a35 |
+ if(pThis->iBufPtrMax == 0) {
|
|
|
ee3a35 |
+ FINALIZE;
|
|
|
ee3a35 |
+ }
|
|
|
ee3a35 |
+
|
|
|
ee3a35 |
+ int currpos = lseek64(pThis->fd, 0, SEEK_CUR);
|
|
|
ee3a35 |
+ backseek = -1 * (off64_t) pThis->iBufPtrMax;
|
|
|
ee3a35 |
+ dbgprintf("checkTruncation in actual processing, currpos %d, backseek is %d\n", (int)currpos, (int) backseek);
|
|
|
ee3a35 |
+ ret = lseek64(pThis->fd, backseek, SEEK_CUR);
|
|
|
ee3a35 |
+ if(ret < 0) {
|
|
|
ee3a35 |
+ iRet = rereadTruncated(pThis, "cannot seek backward to begin of last block");
|
|
|
ee3a35 |
+ FINALIZE;
|
|
|
ee3a35 |
+ }
|
|
|
ee3a35 |
+
|
|
|
ee3a35 |
+ const ssize_t lenRead = read(pThis->fd, pThis->pIOBuf_truncation, pThis->iBufPtrMax);
|
|
|
ee3a35 |
+ dbgprintf("checkTruncation proof-read: %d bytes\n", (int) lenRead);
|
|
|
ee3a35 |
+ if(lenRead < 0) {
|
|
|
ee3a35 |
+ iRet = rereadTruncated(pThis, "last block could not be re-read");
|
|
|
ee3a35 |
+ FINALIZE;
|
|
|
ee3a35 |
+ }
|
|
|
ee3a35 |
+
|
|
|
ee3a35 |
+ if(!memcmp(pThis->pIOBuf_truncation, pThis->pIOBuf, pThis->iBufPtrMax)) {
|
|
|
ee3a35 |
+ DBGPRINTF("checkTruncation, file %s last buffer unchanged\n", pThis->pszCurrFName);
|
|
|
ee3a35 |
+ } else {
|
|
|
ee3a35 |
+ iRet = rereadTruncated(pThis, "last block data different");
|
|
|
ee3a35 |
+ }
|
|
|
ee3a35 |
+
|
|
|
ee3a35 |
+finalize_it:
|
|
|
ee3a35 |
+ RETiRet;
|
|
|
ee3a35 |
+}
|
|
|
ee3a35 |
+
|
|
|
ee3a35 |
+
|
|
|
ee3a35 |
/* read the next buffer from disk
|
|
|
ee3a35 |
* rgerhards, 2008-02-13
|
|
|
ee3a35 |
*/
|
|
|
ee3a35 |
@@ -668,6 +741,13 @@ strmReadBuf(strm_t *pThis, int *padBytes)
|
|
|
ee3a35 |
toRead = (size_t) bytesLeft;
|
|
|
ee3a35 |
}
|
|
|
ee3a35 |
}
|
|
|
ee3a35 |
+ if(pThis->bReopenOnTruncate) {
|
|
|
ee3a35 |
+ rsRetVal localRet = checkTruncation(pThis);
|
|
|
ee3a35 |
+ if(localRet == RS_RET_FILE_TRUNCATED) {
|
|
|
ee3a35 |
+ continue;
|
|
|
ee3a35 |
+ }
|
|
|
ee3a35 |
+ CHKiRet(localRet);
|
|
|
ee3a35 |
+ }
|
|
|
ee3a35 |
iLenRead = read(pThis->fd, pThis->pIOBuf, toRead);
|
|
|
ee3a35 |
DBGOPRINT((obj_t*) pThis, "file %d read %ld bytes\n", pThis->fd, iLenRead);
|
|
|
ee3a35 |
/* end crypto */
|
|
|
ee3a35 |
@@ -854,7 +854,7 @@
|
|
|
ee3a35 |
* a line, but following lines that are indented are part of the same log entry
|
|
|
ee3a35 |
*/
|
|
|
ee3a35 |
static rsRetVal
|
|
|
ee3a35 |
-strmReadLine(strm_t *pThis, cstr_t **ppCStr, uint8_t mode, sbool bEscapeLF,
|
|
|
ee3a35 |
+strmReadLine(strm_t *const pThis, cstr_t **ppCStr, uint8_t mode, sbool bEscapeLF,
|
|
|
ee3a35 |
uint32_t trimLineOverBytes, int64 *const strtOffs)
|
|
|
ee3a35 |
{
|
|
|
ee3a35 |
uchar c;
|
|
|
ee3a35 |
@@ -1184,6 +1264,7 @@ static rsRetVal strmConstructFinalize(strm_t *pThis)
|
|
|
ee3a35 |
} else {
|
|
|
ee3a35 |
/* we work synchronously, so we need to alloc a fixed pIOBuf */
|
|
|
ee3a35 |
CHKmalloc(pThis->pIOBuf = (uchar*) MALLOC(pThis->sIOBufSize));
|
|
|
ee3a35 |
+ CHKmalloc(pThis->pIOBuf_truncation = (char*) MALLOC(pThis->sIOBufSize));
|
|
|
ee3a35 |
}
|
|
|
ee3a35 |
|
|
|
ee3a35 |
finalize_it:
|
|
|
ee3a35 |
@@ -1231,6 +1312,7 @@ CODESTARTobjDestruct(strm)
|
|
|
ee3a35 |
}
|
|
|
ee3a35 |
} else {
|
|
|
ee3a35 |
free(pThis->pIOBuf);
|
|
|
ee3a35 |
+ free(pThis->pIOBuf_truncation);
|
|
|
ee3a35 |
}
|
|
|
ee3a35 |
|
|
|
ee3a35 |
/* Finally, we can free the resources.
|
|
|
ee3a35 |
@@ -2147,11 +2150,22 @@ DEFpropSetMeth(strm, cryprov, cryprov_if_t*)
|
|
|
ee3a35 |
void
|
|
|
ee3a35 |
strmSetReadTimeout(strm_t *const __restrict__ pThis, const int val)
|
|
|
ee3a35 |
{
|
|
|
ee3a35 |
+ ISOBJ_TYPE_assert(pThis, strm);
|
|
|
ee3a35 |
pThis->readTimeout = val;
|
|
|
ee3a35 |
}
|
|
|
ee3a35 |
|
|
|
ee3a35 |
-static rsRetVal strmSetbDeleteOnClose(strm_t *pThis, int val)
|
|
|
ee3a35 |
+void
|
|
|
ee3a35 |
+strmSet_checkRotation(strm_t *const pThis, const int val) {
|
|
|
ee3a35 |
+ ISOBJ_TYPE_assert(pThis, strm);
|
|
|
ee3a35 |
+ assert(val == STRM_ROTATION_DO_CHECK || val == STRM_ROTATION_DO_NOT_CHECK);
|
|
|
ee3a35 |
+ pThis->rotationCheck = val;
|
|
|
ee3a35 |
+}
|
|
|
ee3a35 |
+
|
|
|
ee3a35 |
+
|
|
|
ee3a35 |
+static rsRetVal
|
|
|
ee3a35 |
+strmSetbDeleteOnClose(strm_t *const pThis, const int val)
|
|
|
ee3a35 |
{
|
|
|
ee3a35 |
+ ISOBJ_TYPE_assert(pThis, strm);
|
|
|
ee3a35 |
pThis->bDeleteOnClose = val;
|
|
|
ee3a35 |
if(pThis->cryprov != NULL) {
|
|
|
ee3a35 |
pThis->cryprov->SetDeleteOnClose(pThis->cryprovFileData, pThis->bDeleteOnClose);
|
|
|
ee3a35 |
@@ -2162,15 +2176,19 @@ static rsRetVal strmSetbDeleteOnClose(strm_t *pThis, int val)
|
|
|
ee3a35 |
return RS_RET_OK;
|
|
|
ee3a35 |
}
|
|
|
ee3a35 |
|
|
|
ee3a35 |
-static rsRetVal strmSetiMaxFiles(strm_t *pThis, int iNewVal)
|
|
|
ee3a35 |
+static rsRetVal
|
|
|
ee3a35 |
+strmSetiMaxFiles(strm_t *const pThis, const int iNewVal)
|
|
|
ee3a35 |
{
|
|
|
ee3a35 |
+ ISOBJ_TYPE_assert(pThis, strm);
|
|
|
ee3a35 |
pThis->iMaxFiles = iNewVal;
|
|
|
ee3a35 |
pThis->iFileNumDigits = getNumberDigits(iNewVal);
|
|
|
ee3a35 |
return RS_RET_OK;
|
|
|
ee3a35 |
}
|
|
|
ee3a35 |
|
|
|
ee3a35 |
-static rsRetVal strmSetFileNotFoundError(strm_t *pThis, int pFileNotFoundError)
|
|
|
ee3a35 |
+static rsRetVal
|
|
|
ee3a35 |
+strmSetFileNotFoundError(strm_t *const pThis, const int pFileNotFoundError)
|
|
|
ee3a35 |
{
|
|
|
ee3a35 |
+ ISOBJ_TYPE_assert(pThis, strm);
|
|
|
ee3a35 |
pThis->fileNotFoundError = pFileNotFoundError;
|
|
|
ee3a35 |
return RS_RET_OK;
|
|
|
ee3a35 |
}
|
|
|
ee3a35 |
diff --git a/runtime/stream.h b/runtime/stream.h
|
|
|
ee3a35 |
index e3d6c2372..f6f48378a 100644
|
|
|
ee3a35 |
--- a/runtime/stream.h
|
|
|
ee3a35 |
+++ b/runtime/stream.h
|
|
|
ee3a35 |
@@ -91,6 +91,10 @@ typedef enum { /* when extending, do NOT change existing modes! */
|
|
|
ee3a35 |
STREAMMODE_WRITE_APPEND = 4
|
|
|
ee3a35 |
} strmMode_t;
|
|
|
ee3a35 |
|
|
|
ee3a35 |
+/* settings for stream rotation (applies not to all processing modes!) */
|
|
|
ee3a35 |
+#define STRM_ROTATION_DO_CHECK 0
|
|
|
ee3a35 |
+#define STRM_ROTATION_DO_NOT_CHECK 1
|
|
|
ee3a35 |
+
|
|
|
ee3a35 |
#define STREAM_ASYNC_NUMBUFS 2 /* must be a power of 2 -- TODO: make configurable */
|
|
|
ee3a35 |
/* The strm_t data structure */
|
|
|
ee3a35 |
typedef struct strm_s {
|
|
|
ee3a35 |
@@ -114,6 +118,7 @@ typedef struct strm_s {
|
|
|
ee3a35 |
sbool bDisabled; /* should file no longer be written to? (currently set only if omfile file size limit fails) */
|
|
|
ee3a35 |
sbool bSync; /* sync this file after every write? */
|
|
|
ee3a35 |
sbool bReopenOnTruncate;
|
|
|
ee3a35 |
+ int rotationCheck; /* rotation check mode */
|
|
|
ee3a35 |
size_t sIOBufSize;/* size of IO buffer */
|
|
|
ee3a35 |
uchar *pszDir; /* Directory */
|
|
|
ee3a35 |
int lenDir;
|
|
|
ee3a35 |
@@ -124,6 +124,7 @@ typedef struct strm_s {
|
|
|
ee3a35 |
ino_t inode; /* current inode for files being monitored (undefined else) */
|
|
|
ee3a35 |
uchar *pszCurrFName; /* name of current file (if open) */
|
|
|
ee3a35 |
uchar *pIOBuf; /* the iobuffer currently in use to gather data */
|
|
|
ee3a35 |
+ char *pIOBuf_truncation; /* iobuffer used during trucation detection block re-reads */
|
|
|
ee3a35 |
size_t iBufPtrMax; /* current max Ptr in Buffer (if partial read!) */
|
|
|
ee3a35 |
size_t iBufPtr; /* pointer into current buffer */
|
|
|
ee3a35 |
int iUngetC; /* char set via UngetChar() call or -1 if none set */
|
|
|
ee3a35 |
@@ -238,5 +238,6 @@
|
|
|
ee3a35 |
const uchar * strmGetPrevLineSegment(strm_t *const pThis);
|
|
|
ee3a35 |
const uchar * strmGetPrevMsgSegment(strm_t *const pThis);
|
|
|
ee3a35 |
int strmGetPrevWasNL(const strm_t *const pThis);
|
|
|
ee3a35 |
+void strmSet_checkRotation(strm_t *const pThis, const int val);
|
|
|
ee3a35 |
|
|
|
ee3a35 |
#endif /* #ifndef STREAM_H_INCLUDED */
|