From 6e511e8e8bdf55dd34ae27906e40aadf9b9a7a54 Mon Sep 17 00:00:00 2001
From: Rainer Gerhards <rgerhards@adiscon.com>
Date: Mon, 23 Mar 2015 15:14:40 +0100
Subject: [PATCH] imfile: preliminary patch for msg loss in readmode != 0

This is a preliminary patch, which needs to undergo further testing
in cases where rsyslogd is shut down with partial messages read.

see also https://github.com/rsyslog/rsyslog/issues/144

From 714a914c25144bba5ad8a668618364bd89e040fb Mon Sep 17 00:00:00 2001
From: Nirmoy Das <ndas@suse.de>
Date: Mon, 7 Aug 2017 17:19:02 +0200
Subject: [PATCH] remove debug prints from upstream patch

---
 runtime/stream.c | 39 ++++++++++++++++++++++++---------------
 runtime/stream.h |  1 +
 2 files changed, 25 insertions(+), 15 deletions(-)

diff --git a/runtime/stream.c b/runtime/stream.c
index 9974531..1ee872c 100644
--- a/runtime/stream.c
+++ b/runtime/stream.c
@@ -692,7 +692,6 @@ strmReadLine(strm_t *pThis, cstr_t **ppCStr, uint8_t mode, sbool bEscapeLF)
         uchar c;
 	uchar finished;
 	rsRetVal readCharRet;
-	sbool bPrevWasNL;
         DEFiRet;
 
         ASSERT(pThis != NULL);
@@ -701,12 +700,12 @@ strmReadLine(strm_t *pThis, cstr_t **ppCStr, uint8_t mode, sbool bEscapeLF)
         CHKiRet(cstrConstruct(ppCStr));
         CHKiRet(strmReadChar(pThis, &c));
 
+	/* append previous message to current message if necessary */
+	if(pThis->prevLineSegment != NULL) {
+		CHKiRet(cstrAppendCStr(*ppCStr, pThis->prevLineSegment));
+		cstrDestruct(&pThis->prevLineSegment);
+	}
         if(mode == 0) {
-		/* append previous message to current message if necessary */
-		if(pThis->prevLineSegment != NULL) {
-			CHKiRet(cstrAppendCStr(*ppCStr, pThis->prevLineSegment));
-			cstrDestruct(&pThis->prevLineSegment);
-		}
 		while(c != '\n') {
                 	CHKiRet(cstrAppendChar(*ppCStr, c));
                 	readCharRet = strmReadChar(pThis, &c);
@@ -718,15 +717,14 @@ strmReadLine(strm_t *pThis, cstr_t **ppCStr, uint8_t mode, sbool bEscapeLF)
         	CHKiRet(cstrFinalize(*ppCStr));
 	} else if(mode == 1) {
 		finished=0;
-		bPrevWasNL = 0;
 		while(finished == 0){
         		if(c != '\n') {
                 		CHKiRet(cstrAppendChar(*ppCStr, c));
                 		CHKiRet(strmReadChar(pThis, &c));
-				bPrevWasNL = 0;
+				pThis->bPrevWasNL = 0;
 			} else {
 				if ((((*ppCStr)->iStrLen) > 0) ){
-					if(bPrevWasNL) {
+					if(pThis->bPrevWasNL) {
 						rsCStrTruncate(*ppCStr, (bEscapeLF) ? 4 : 1); /* remove the prior newline */
 						finished=1;
 					} else {
@@ -736,7 +734,7 @@ strmReadLine(strm_t *pThis, cstr_t **ppCStr, uint8_t mode, sbool bEscapeLF)
 							CHKiRet(cstrAppendChar(*ppCStr, c));
 						}
                					CHKiRet(strmReadChar(pThis, &c));
-						bPrevWasNL = 1;
+						pThis->bPrevWasNL = 1;
 					}
 				} else {
 					finished=1;  /* this is a blank line, a \n with nothing since the last complete record */
@@ -744,10 +742,10 @@ strmReadLine(strm_t *pThis, cstr_t **ppCStr, uint8_t mode, sbool bEscapeLF)
 			}
 		}
         	CHKiRet(cstrFinalize(*ppCStr));
+		pThis->bPrevWasNL = 0;
 	} else if(mode == 2) {
 		/* indented follow-up lines */
 		finished=0;
-		bPrevWasNL = 0;
 		while(finished == 0){
 			if ((*ppCStr)->iStrLen == 0){
         			if(c != '\n') {
@@ -758,11 +756,11 @@ strmReadLine(strm_t *pThis, cstr_t **ppCStr, uint8_t mode, sbool bEscapeLF)
 					finished=1;  /* this is a blank line, a \n with nothing since the last complete record */
 				}
 			} else {
-				if(bPrevWasNL) {
+				if(pThis->bPrevWasNL) {
 					if ((c == ' ') || (c == '\t')){
                					CHKiRet(cstrAppendChar(*ppCStr, c));
                					CHKiRet(strmReadChar(pThis, &c));
-						bPrevWasNL = 0;
+						pThis->bPrevWasNL = 0;
 					} else {
 						/* clean things up by putting the character we just read back into
 						 * the input buffer and removing the LF character that is currently at the
@@ -773,7 +771,7 @@ strmReadLine(strm_t *pThis, cstr_t **ppCStr, uint8_t mode, sbool bEscapeLF)
 					}
 				} else { /* not the first character after a newline, add it to the buffer */
 					if(c == '\n') {
-						bPrevWasNL = 1;
+						pThis->bPrevWasNL = 1;
 						if(bEscapeLF) {
 							CHKiRet(rsCStrAppendStrWithLen(*ppCStr, (uchar*)"#012", sizeof("#012")-1));
 						} else {
@@ -787,11 +785,14 @@ strmReadLine(strm_t *pThis, cstr_t **ppCStr, uint8_t mode, sbool bEscapeLF)
 			}
 		}
        		CHKiRet(cstrFinalize(*ppCStr));
+		pThis->bPrevWasNL = 0;
 	}
 
 finalize_it:
-        if(iRet != RS_RET_OK && *ppCStr != NULL)
+        if(iRet != RS_RET_OK && *ppCStr != NULL) {
+				rsCStrConstructFromCStr(&pThis->prevLineSegment, *ppCStr);
                 cstrDestruct(ppCStr);
+	}
 
         RETiRet;
 }
@@ -810,6 +811,7 @@ BEGINobjConstruct(strm) /* be sure to specify the object type also in END macro!
 	pThis->tOpenMode = 0600;
 	pThis->pszSizeLimitCmd = NULL;
 	pThis->prevLineSegment = NULL;
+	pThis->bPrevWasNL = 0;
 ENDobjConstruct(strm)
 
 
@@ -936,6 +938,8 @@ CODESTARTobjDestruct(strm)
 	 * IMPORTANT: we MUST free this only AFTER the ansyncWriter has been stopped, else
 	 * we get random errors...
 	 */
+	if(pThis->prevLineSegment)
+		cstrDestruct(&pThis->prevLineSegment);
 	free(pThis->pszDir);
 	free(pThis->pZipBuf);
 	free(pThis->pszCurrFName);
@@ -1852,6 +1856,9 @@ static rsRetVal strmSerialize(strm_t *pThis, strm_t *pStrm)
 
 	objSerializePTR(pStrm, prevLineSegment, PSZ);
 
+	i = pThis->bPrevWasNL;
+	objSerializeSCALAR_VAR(pStrm, bPrevWasNL, INT, i);
+
 	CHKiRet(obj.EndSerialize(pStrm));
 
 finalize_it:
@@ -1961,6 +1968,8 @@ static rsRetVal strmSetProperty(strm_t *pThis, var_t *pProp)
 		CHKiRet(strmSetbDeleteOnClose(pThis, pProp->val.num));
  	} else if(isProp("prevLineSegment")) {
 		CHKiRet(rsCStrConstructFromCStr(&pThis->prevLineSegment, pProp->val.pStr));
+ 	} else if(isProp("bPrevWasNL")) {
+		pThis->bPrevWasNL = (sbool) pProp->val.num;
 	}
 
 finalize_it:
diff --git a/runtime/stream.h b/runtime/stream.h
index 8f26bdc..cbd0fee 100644
--- a/runtime/stream.h
+++ b/runtime/stream.h
@@ -106,6 +106,7 @@ typedef struct strm_s {
 	sbool bDeleteOnClose; /* set to 1 to auto-delete on close -- be careful with that setting! */
 	int64 iCurrOffs;/* current offset */
 	int64 *pUsrWCntr; /* NULL or a user-provided counter that receives the nbr of bytes written since the last CntrSet() */
+	sbool bPrevWasNL; /* used for readLine() when reading multi-line messages */
 	/* dynamic properties, valid only during file open, not to be persistet */
 	sbool bDisabled; /* should file no longer be written to? (currently set only if omfile file size limit fails) */
 	sbool bSync;	/* sync this file after every write? */
-- 
2.13.2

