xymon

Check-in [9ad9c6d449]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:logfetch should send final lines in low-maxbytes, multi-trigger situations, not first lines after last trigger. Add more verbose debugging. git-svn-id: http://svn.code.sf.net/p/xymon/code/branches/4.3.19@7614 44351d6e-118b-4698-b696-ce33095ecaa4
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk | origin/4.3.19
Files: files | file ages | folders
SHA3-256: 9ad9c6d449d3f0ceaeaccbe3aa13342e2143746f224dd096cb4841b90487400f
User & Date: jccleaver@users.sf.net 2015-03-24 01:34:07
Context
2015-03-25
03:47
Ensure status text in Windows svcs test updated git-svn-id: http://svn.code.sf.net/p/xymon/code/branches/4.3.19@7616 44351d6e-118b-4698-b696-ce33095ecaa4 check-in: dba9139eba user: jccleaver@users.sf.net tags: trunk, origin/4.3.19
2015-03-24
01:34
logfetch should send final lines in low-maxbytes, multi-trigger situations, not first lines after last trigger. Add more verbose debugging. git-svn-id: http://svn.code.sf.net/p/xymon/code/branches/4.3.19@7614 44351d6e-118b-4698-b696-ce33095ecaa4 check-in: 9ad9c6d449 user: jccleaver@users.sf.net tags: trunk, origin/4.3.19
00:49
logfetch: Make skip/current location text override-able via ENV git-svn-id: http://svn.code.sf.net/p/xymon/code/branches/4.3.19@7613 44351d6e-118b-4698-b696-ce33095ecaa4 check-in: 82738771cf user: jccleaver@users.sf.net tags: trunk, origin/4.3.19
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to client/logfetch.c.

42
43
44
45
46
47
48




49
50
51
52
53
54
55
...
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162


163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
...
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
...
219
220
221
222
223
224
225
226

227


228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
...
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478


479
480

481
482
483
484
485
486
487
...
496
497
498
499
500
501
502


503
504
505




















506
507
508
509

510
511


512
513
514
515
516
517
518
...
568
569
570
571
572
573
574

575
576
577
578
579
580
581
....
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
....
1227
1228
1229
1230
1231
1232
1233
























1234
1235
1236
1237
1238
1239
1240

/* Is it ok for these to be hardcoded ? */
#define MAXCHECK   102400   /* When starting, dont look at more than 100 KB of data */
#define MAXMINUTES 30
#define POSCOUNT ((MAXMINUTES / 5) + 1)		/* 0 = current run */
#define DEFAULTSCROLLBACK (POSCOUNT - 1)	/* How far back to begin processing data, in runs */
#define LINES_AROUND_TRIGGER 5





typedef enum { C_NONE, C_LOG, C_FILE, C_DIR, C_COUNT } checktype_t;

typedef struct logdef_t {
#ifdef _LARGEFILE_SOURCE
	off_t lastpos[POSCOUNT];
	off_t maxbytes;
................................................................................
	char *startpos, *fillpos, *triggerstartpos, *triggerendpos, *curpos = NULL;
	FILE *fd;
	struct stat st;
	size_t bytesread, bytesleft;
	int openerr, i, status, triggerlinecount, done;
	char *linepos[2*LINES_AROUND_TRIGGER+1];
	int lpidx;
	int scrollback = DEFAULTSCROLLBACK;
	size_t byteslast, bytestocurrent, bytesin = 0;
	regex_t *deltaexpr = NULL, *ignexpr = NULL, *trigexpr = NULL;
#ifdef _LARGEFILE_SOURCE
	off_t bufsz;
#else
	long bufsz;
#endif

	char *(*triggerptrs)[2] = NULL;
	unsigned int triggerptrs_count = 0;



	if (buf) free(buf);
	buf = NULL;

	if (replacement) free(replacement);
	replacement = NULL;

	if (getenv("LOGFETCH_SCROLLBACK") != NULL) {
		int scroll;
		scroll = atoi(getenv("LOGFETCH_SCROLLBACK"));
		/* Don't use DEFAULTSCROLLBACK here in case we change/lower it in the future */
		if (scroll > (POSCOUNT-1)) errprintf("Scrollback requested is greater than maximum saved\n");
		else { dbgprintf("logfetch: setting scrollback to %d\n", scroll); scrollback = scroll; }
	}

	fd = fileopen(filename, &openerr);
	if (fd == NULL) {
		buf = (char *)malloc(1024 + strlen(filename));
		sprintf(buf, "Cannot open logfile %s : %s\n", filename, strerror(openerr));
		return buf;
	}

................................................................................
		 * Logfile shrank - probably it was rotated.
		 * Start from beginning of file.
		 */
		errprintf("logfetch: File %s shrank from >=%zu to %zu bytes in size. Probably rotated; clearing position state\n", filename, logdef->lastpos[POSCOUNT-1], st.st_size);
		for (i=0; (i < POSCOUNT); i++) logdef->lastpos[i] = 0;
	}

	/* Go to the position we were at $SCROLLBACK times ago (default corresponds to 6 (30 minutes when 5m/run)) */
#ifdef _LARGEFILE_SOURCE
	fseeko(fd, logdef->lastpos[scrollback], SEEK_SET);
	bufsz = st.st_size - ftello(fd);
	if (bufsz > MAXCHECK) {
		/*
		 * Too much data for us. We have to skip some of the old data.
		 */
................................................................................
		errprintf("logfetch: %s delta %zu bytes exceeds max buffer size %zu; skipping some data\n", filename, bufsz, MAXCHECK);
		logdef->lastpos[scrollback] = st.st_size - MAXCHECK;
		fseek(fd, logdef->lastpos[scrollback], SEEK_SET);
		bufsz = st.st_size - ftell(fd);
	}
#endif

	/* Calculate delta between scrollback (where we're starting) and end of last run */

	byteslast = logdef->lastpos[scrollback];



	/* Shift position markers one down for the next round */
	/* lastpos[1] is the previous end location, lastpos[0] will be the end of this run */
	for (i=POSCOUNT-1; (i > 0); i--) logdef->lastpos[i] = logdef->lastpos[i-1];
	logdef->lastpos[0] = st.st_size;

	/* This is where we place our "CURRENT" marker -- lastpos[1] should only be 0 if we're starting from the beginning anyway */
	bytestocurrent = logdef->lastpos[1] - byteslast;
	dbgprintf("logfetch: last position count was %zu, current is %zu, scrollback is %d, bytestocurrent: %zu\n", byteslast, logdef->lastpos[1], scrollback, bytestocurrent);

	/*
	 * Get our read buffer.
	 *
	 * NB: fgets() need some extra room in the input buffer.
	 *     If it is missing, we will never detect end-of-file 
	 *     because fgets() will read 0 bytes, but having read that
................................................................................

	bytesread = (fillpos - startpos);
	*(buf + bytesread) = '\0';

	if (bytesread > logdef->maxbytes) {

	        if (triggerptrs != NULL) {
		       size_t triggerbytes, nontriggerbytes, skiptxtbytes;
		       char *pos;
		       size_t size;

		       /* Sum the number of bytes required to hold all the trigger content (start -> end anchors) */
		       for (i = 0, triggerbytes = 0, skiptxtbytes = 0; i < triggerptrs_count; i++) {
		           triggerbytes += strlen(triggerptrs[i][0]) - strlen(triggerptrs[i][1]);
		           skiptxtbytes += strlen(skiptxt) * 2;
		       }
	  
		       /* Find the remaining bytes allowed for non-trigger content (and prevent size_t underflow wrap ) */
			nontriggerbytes = (logdef->maxbytes < triggerbytes) ? 0 : (logdef->maxbytes - triggerbytes);



		       dbgprintf("Found %zu bytes of trigger data, %zu bytes remaining of non-trigger data. Max allowed is %zu.\n", triggerbytes, nontriggerbytes, logdef->maxbytes);


		       /* Allocate a new buffer, reduced to what we actually can hold */
		       replacement = malloc(sizeof(char) * (triggerbytes + skiptxtbytes + nontriggerbytes));
		       if (replacement == NULL) return "Out of memory";

		       dbgprintf("Staging replacement buffer, %zu bytes.\n", (triggerbytes + skiptxtbytes + nontriggerbytes));

................................................................................
		               strncpy(pos, triggerptrs[i][0], size);
		               pos += size;
		       }

		       /* At this point, all the required trigger lines are present */

		       if (nontriggerbytes > 0) {


		               /* Append non-trigger, up to the allowed byte size remaining, of remaining log content */
		               dbgprintf("Copying %zu bytes of non-trigger content.\n", nontriggerbytes);





















		               /* Add the final skip for completeness */
		               strncpy(pos, skiptxt, strlen(skiptxt));
		               pos += strlen(skiptxt);


		               /* And copy the the rest of the original buffer content, starting at the last trigger END */
		               strncpy(pos, triggerptrs[triggerptrs_count - 1][1], nontriggerbytes);


		       }

		       /* Prune out the last line to prevent sending a partial */
		       if (*(pos = &replacement[strlen(replacement) - 1]) != '\n') {
		               while (*pos != '\n') {
		                       pos -= 1;
		               }
................................................................................
			}
			xfree(trigexpr);
		}

		if (triggerptrs) xfree(triggerptrs);
	}


	return startpos;
}

char *ftypestr(unsigned int mode, char *symlink)
{
	static char *result = NULL;
	char *s = "unknown";
................................................................................
#ifdef BIG_SECURITY_HOLE
	drop_root();
#else
	drop_root_and_removesuid(argv[0]);
#endif

	for (i=1; (i<argc); i++) {
		if (strcmp(argv[i], "--debug") == 0) {
			char *delim = strchr(argv[i], '=');
			debug = 1;
			if (delim) set_debugfile(delim+1, 0);
		}
		else if (strcmp(argv[i], "--clock") == 0) {
			struct timeval tv;
			struct timezone tz;
................................................................................
		else {
			/* Not an option -- should have two arguments left: our config and status file */
			if (cfgfn == NULL) cfgfn = argv[i];
			else if (statfn == NULL) statfn = argv[i];
			else fprintf(stderr, "Unknown argument '%s'\n", argv[i]);
		}
	}

























	if ((cfgfn == NULL) || (statfn == NULL)) {
		fprintf(stderr, "Missing config or status file arguments\n");
		return 1;
	}

	if (loadconfig(cfgfn) != 0) return 1;







>
>
>
>







 







<











>
>






<
<
<
<
<
<
<
<







 







|







 







|
>

>
>






|
|
<







 







|
|










>
>

<
>







 







>
>
|
<

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
|
|
|
>
|
<
>
>







 







>







 







|







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
...
148
149
150
151
152
153
154

155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173








174
175
176
177
178
179
180
...
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
...
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235

236
237
238
239
240
241
242
...
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480

481
482
483
484
485
486
487
488
...
497
498
499
500
501
502
503
504
505
506

507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533

534
535
536
537
538
539
540
541
542
...
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
....
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
....
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289

/* Is it ok for these to be hardcoded ? */
#define MAXCHECK   102400   /* When starting, dont look at more than 100 KB of data */
#define MAXMINUTES 30
#define POSCOUNT ((MAXMINUTES / 5) + 1)		/* 0 = current run */
#define DEFAULTSCROLLBACK (POSCOUNT - 1)	/* How far back to begin processing data, in runs */
#define LINES_AROUND_TRIGGER 5

/* Default = use default */
int scrollback = -1;


typedef enum { C_NONE, C_LOG, C_FILE, C_DIR, C_COUNT } checktype_t;

typedef struct logdef_t {
#ifdef _LARGEFILE_SOURCE
	off_t lastpos[POSCOUNT];
	off_t maxbytes;
................................................................................
	char *startpos, *fillpos, *triggerstartpos, *triggerendpos, *curpos = NULL;
	FILE *fd;
	struct stat st;
	size_t bytesread, bytesleft;
	int openerr, i, status, triggerlinecount, done;
	char *linepos[2*LINES_AROUND_TRIGGER+1];
	int lpidx;

	size_t byteslast, bytestocurrent, bytesin = 0;
	regex_t *deltaexpr = NULL, *ignexpr = NULL, *trigexpr = NULL;
#ifdef _LARGEFILE_SOURCE
	off_t bufsz;
#else
	long bufsz;
#endif

	char *(*triggerptrs)[2] = NULL;
	unsigned int triggerptrs_count = 0;

	dbgprintf("logfetch: -> logdata (%s)\n", filename);

	if (buf) free(buf);
	buf = NULL;

	if (replacement) free(replacement);
	replacement = NULL;









	fd = fileopen(filename, &openerr);
	if (fd == NULL) {
		buf = (char *)malloc(1024 + strlen(filename));
		sprintf(buf, "Cannot open logfile %s : %s\n", filename, strerror(openerr));
		return buf;
	}

................................................................................
		 * Logfile shrank - probably it was rotated.
		 * Start from beginning of file.
		 */
		errprintf("logfetch: File %s shrank from >=%zu to %zu bytes in size. Probably rotated; clearing position state\n", filename, logdef->lastpos[POSCOUNT-1], st.st_size);
		for (i=0; (i < POSCOUNT); i++) logdef->lastpos[i] = 0;
	}

	/* Go to the position we were at scrollback times ago (default corresponds to 6 -- 30 minutes when 5m per run) */
#ifdef _LARGEFILE_SOURCE
	fseeko(fd, logdef->lastpos[scrollback], SEEK_SET);
	bufsz = st.st_size - ftello(fd);
	if (bufsz > MAXCHECK) {
		/*
		 * Too much data for us. We have to skip some of the old data.
		 */
................................................................................
		errprintf("logfetch: %s delta %zu bytes exceeds max buffer size %zu; skipping some data\n", filename, bufsz, MAXCHECK);
		logdef->lastpos[scrollback] = st.st_size - MAXCHECK;
		fseek(fd, logdef->lastpos[scrollback], SEEK_SET);
		bufsz = st.st_size - ftell(fd);
	}
#endif

	/* Calculate delta between scrollback (where going to start looking at data) and end of the most recent run.
	 * This is where we place our "CURRENT" marker */
	byteslast = logdef->lastpos[scrollback];
	/* If lastpos[0] is 0, then all of the positions are 0 because we rotated above */
	bytestocurrent = logdef->lastpos[0] - byteslast;

	/* Shift position markers one down for the next round */
	/* lastpos[1] is the previous end location, lastpos[0] will be the end of this run */
	for (i=POSCOUNT-1; (i > 0); i--) logdef->lastpos[i] = logdef->lastpos[i-1];
	logdef->lastpos[0] = st.st_size;

	dbgprintf("logfetch: Current size (ending): %zu bytes. Last end was %zu. Looking %d spots before that, which is %zu. bytestocurrent: %zu\n",
		logdef->lastpos[0], logdef->lastpos[1], scrollback, byteslast, bytestocurrent);


	/*
	 * Get our read buffer.
	 *
	 * NB: fgets() need some extra room in the input buffer.
	 *     If it is missing, we will never detect end-of-file 
	 *     because fgets() will read 0 bytes, but having read that
................................................................................

	bytesread = (fillpos - startpos);
	*(buf + bytesread) = '\0';

	if (bytesread > logdef->maxbytes) {

	        if (triggerptrs != NULL) {
		       size_t triggerbytes, nontriggerbytes, skiptxtbytes, lasttriggeroffset;
		       char *pos, *lasttriggerptr;
		       size_t size;

		       /* Sum the number of bytes required to hold all the trigger content (start -> end anchors) */
		       for (i = 0, triggerbytes = 0, skiptxtbytes = 0; i < triggerptrs_count; i++) {
		           triggerbytes += strlen(triggerptrs[i][0]) - strlen(triggerptrs[i][1]);
		           skiptxtbytes += strlen(skiptxt) * 2;
		       }
	  
		       /* Find the remaining bytes allowed for non-trigger content (and prevent size_t underflow wrap ) */
			nontriggerbytes = (logdef->maxbytes < triggerbytes) ? 0 : (logdef->maxbytes - triggerbytes);
			lasttriggerptr = triggerptrs[triggerptrs_count - 1][1];
			lasttriggeroffset = (fillpos - lasttriggerptr);


		       dbgprintf("Found %zu bytes of trigger data, %zu bytes available space for non-trigger data; last trigger ended %zu bytes from end. Max bytes allowed is %zu.\n", triggerbytes, nontriggerbytes, lasttriggeroffset, logdef->maxbytes);

		       /* Allocate a new buffer, reduced to what we actually can hold */
		       replacement = malloc(sizeof(char) * (triggerbytes + skiptxtbytes + nontriggerbytes));
		       if (replacement == NULL) return "Out of memory";

		       dbgprintf("Staging replacement buffer, %zu bytes.\n", (triggerbytes + skiptxtbytes + nontriggerbytes));

................................................................................
		               strncpy(pos, triggerptrs[i][0], size);
		               pos += size;
		       }

		       /* At this point, all the required trigger lines are present */

		       if (nontriggerbytes > 0) {
				char *finalstartptr;

				/* Append non-trigger, up to the allowed byte size remaining, of remaining log content, starting backwards */


				/* Figure out where to start copying from */
				finalstartptr = (fillpos - nontriggerbytes);
				if (finalstartptr < lasttriggerptr) {
					/* 
					 * FIXME: We could have included inter-trigger data here too.
					 * At the moment, duplicate lines are the worse of two evils, so just start from there.
					 */
					finalstartptr = lasttriggerptr;
					nontriggerbytes = (fillpos - finalstartptr);
					dbgprintf("logfetch: More space was available than between last trigger and eof; reducing to %zu bytes\n", nontriggerbytes);
				}

				/* We're already skipping content; so don't send a partial line. Be sure to decrement the subsequent length we feed to strncpy */
				while (*(finalstartptr-1) != '\n') {
					finalstartptr += 1; nontriggerbytes -= 1;
				}

				dbgprintf("logfetch: Delta from end of final trigger to beginning of final section: %zu bytes\n", (finalstartptr - lasttriggerptr) );

				if (finalstartptr > lasttriggerptr) {
					/* Add the final skip for completeness */
					strncpy(pos, skiptxt, strlen(skiptxt));
					pos += strlen(skiptxt);
				}

				/* And copy the the rest of the original buffer content */

				dbgprintf("Copying %zu final bytes of non-trigger content\n", nontriggerbytes);
				strncpy(pos, finalstartptr, nontriggerbytes);
		       }

		       /* Prune out the last line to prevent sending a partial */
		       if (*(pos = &replacement[strlen(replacement) - 1]) != '\n') {
		               while (*pos != '\n') {
		                       pos -= 1;
		               }
................................................................................
			}
			xfree(trigexpr);
		}

		if (triggerptrs) xfree(triggerptrs);
	}

	dbgprintf("logfetch: <- logdata (%s)\n", filename);
	return startpos;
}

char *ftypestr(unsigned int mode, char *symlink)
{
	static char *result = NULL;
	char *s = "unknown";
................................................................................
#ifdef BIG_SECURITY_HOLE
	drop_root();
#else
	drop_root_and_removesuid(argv[0]);
#endif

	for (i=1; (i<argc); i++) {
		if (strncmp(argv[i], "--debug", 7) == 0) {
			char *delim = strchr(argv[i], '=');
			debug = 1;
			if (delim) set_debugfile(delim+1, 0);
		}
		else if (strcmp(argv[i], "--clock") == 0) {
			struct timeval tv;
			struct timezone tz;
................................................................................
		else {
			/* Not an option -- should have two arguments left: our config and status file */
			if (cfgfn == NULL) cfgfn = argv[i];
			else if (statfn == NULL) statfn = argv[i];
			else fprintf(stderr, "Unknown argument '%s'\n", argv[i]);
		}
	}


	if (scrollback == -1) {
		char *p;

		p = getenv("LOGFETCHSCROLLBACK");
		if (!p) p = getenv("LOGFETCH_SCROLLBACK"); /* compat */
		if (!p || (strlen(p) == 0) ) scrollback = DEFAULTSCROLLBACK;
		else {
			int scroll;
			scroll = atoi(p);

			/* Don't use DEFAULTSCROLLBACK here in case we change or lower the default in the future */
			if ((scroll < 0) || (scroll > (POSCOUNT-1)) ) {
				errprintf("Scrollback requested (%d) is greater than max saved (%d)\n", scroll, (POSCOUNT - 1) );
				scrollback = DEFAULTSCROLLBACK;
			}
			else {
				dbgprintf("logfetch: setting scrollback to %d\n", scroll);
				scrollback = scroll;
			}
		}
	}


	if ((cfgfn == NULL) || (statfn == NULL)) {
		fprintf(stderr, "Missing config or status file arguments\n");
		return 1;
	}

	if (loadconfig(cfgfn) != 0) return 1;