Skip to content

Commit

Permalink
Fix checkresult output processing.
Browse files Browse the repository at this point in the history
Checkresult output submitted through files is stored as a single line
with newlines (and backslashes) escaped so Core can read the complete
output as one line. The corresponding unescaping was not performed when
reading back from the files. These changes correct this by unescaping
the output after it is read from the checkresult file, and before any
further processing. We now use one newline-delimited format internally
for output data from all sources (checkresults spool, command file,
workers).

Perfdata from the first line was not being concatenated correctly to
following lines. A space is now concatenated before additional lines,
and a terminal space is not added to only be trimmed off later.

In support of these changes a new unescape_newlines() functions was
added to base/utils.c that performs the converse of the existing
escape_newlines(). Both of these functions allocate exactly the memory
needed for the new string they return, and not assume a worst case.
Using the improved escape_newlines() to escape the long_output at the
end of parse_check_output() eliminates an unnecessary malloc()/free()
pair.

The line processing code in parse_check_output() was strdup()ing each
line it processed, and then memmove()ing the remaining input to the
beginning of its buffer. As the memove() and earlier processing modify
the input buffer anyway, this alloc/copy/free/move is entirely unneeded:
the per-line processing can use the input buf directly. Modifying the
line processing loop to update buf to point at the next line to process
eliminates the extra memory movement.

Additionally, the checkresults spool directory is now installed with the
same ownership and permissions as the external command directory. This
allows members of the Nagios Command group (nagcmd by default) to submit
checkresults by default, and also keeps reinstalls or upgrades from
resetting the ownership and permissions.

All checkresult output, single or multiple line, and from all sources,
should now be parsed correctly (and more efficiently) by Core.
  • Loading branch information
emislivec committed May 28, 2014
1 parent fefc694 commit 0834195
Show file tree
Hide file tree
Showing 3 changed files with 165 additions and 154 deletions.
7 changes: 4 additions & 3 deletions Makefile.in
Expand Up @@ -234,21 +234,22 @@ install:
cd $(SRC_BASE) && $(MAKE) $@
cd $(SRC_CGI) && $(MAKE) $@
cd $(SRC_HTM) && $(MAKE) $@
$(MAKE) install-exfoliation
$(MAKE) install-exfoliation
$(MAKE) install-basic

install-unstripped:
cd $(SRC_BASE) && $(MAKE) $@
cd $(SRC_CGI) && $(MAKE) $@
cd $(SRC_HTM) && $(MAKE) $@
$(MAKE) install-exfoliation
$(MAKE) install-exfoliation
$(MAKE) install-basic

install-basic:
$(INSTALL) -m 775 $(INSTALL_OPTS) -d $(DESTDIR)$(LIBEXECDIR)
$(INSTALL) -m 775 $(INSTALL_OPTS) -d $(DESTDIR)$(LOGDIR)
$(INSTALL) -m 775 $(INSTALL_OPTS) -d $(DESTDIR)$(LOGDIR)/archives
$(INSTALL) -m 775 $(INSTALL_OPTS) -d $(DESTDIR)$(CHECKRESULTDIR)
$(INSTALL) -m 775 $(COMMAND_OPTS) -d $(DESTDIR)$(CHECKRESULTDIR)
chmod g+s $(DESTDIR)$(CHECKRESULTDIR)

@echo ""
@echo "*** Main program, CGIs and HTML files installed ***"
Expand Down
224 changes: 85 additions & 139 deletions base/checks.c
Expand Up @@ -2960,193 +2960,139 @@ int handle_host_state(host *hst) {
}


/* parse raw plugin output and return: short and long output, perf data */
/* Parses raw plugin output and return: short and long output, perf data. */
int parse_check_output(char *buf, char **short_output, char **long_output, char **perf_data, int escape_newlines_please, int newlines_are_escaped) {
int current_line = 0;
int found_newline = FALSE;
int eof = FALSE;
int used_buf = 0;
int dbuf_chunk = 1024;
int in_perf_data = FALSE;
const int dbuf_chunk = 1024;
dbuf db1;
dbuf db2;
char *ptr = NULL;
int in_perf_data = FALSE;
char *tempbuf = NULL;
register int x = 0;
register int y = 0;
int x = 0;
int y = 0;

/* initialize values */
if(short_output)
/* Initialize output values. */
if (short_output)
*short_output = NULL;
if(long_output)
if (long_output)
*long_output = NULL;
if(perf_data)
if (perf_data)
*perf_data = NULL;

/* nothing to do */
if(buf == NULL || !strcmp(buf, ""))
/* No input provided or no output requested, nothing to do. */
if (!buf || !*buf || (!short_output && !long_output && !perf_data))
return OK;

used_buf = strlen(buf) + 1;

/* initialize dynamic buffers (1KB chunk size) */
/* Initialize dynamic buffers (1KB chunk size). */
dbuf_init(&db1, dbuf_chunk);
dbuf_init(&db2, dbuf_chunk);

/* unescape newlines and escaped backslashes first */
if(newlines_are_escaped == TRUE) {
for(x = 0, y = 0; buf[x] != '\x0'; x++) {
if(buf[x] == '\\' && buf[x + 1] == '\\') {
/* We should never need to worry about unescaping here again. We assume a
* common internal plugin output format that is newline delimited. */
if (newlines_are_escaped) {
for (x = 0, y = 0; buf[x]; x++) {
if (buf[x] == '\\' && buf[x + 1] == '\\') {
x++;
buf[y++] = buf[x];
}
else if(buf[x] == '\\' && buf[x + 1] == 'n') {
else if (buf[x] == '\\' && buf[x + 1] == 'n') {
x++;
buf[y++] = '\n';
}
else
buf[y++] = buf[x];
}
buf[y] = '\x0';
buf[y] = '\0';
}

/* process each line of input */
for(x = 0; eof == FALSE; x++) {
/* Process each line of input. */
for (x = 0; !eof; x++) {

/* we found the end of a line */
if(buf[x] == '\n')
found_newline = TRUE;
else if(buf[x] == '\\' && buf[x + 1] == 'n' && newlines_are_escaped == TRUE) {
found_newline = TRUE;
buf[x] = '\x0';
x++;
}
else if(buf[x] == '\x0') {
found_newline = TRUE;
/* Continue on until we reach the end of a line (or input). */
if (buf[x] == '\0')
eof = TRUE;
}
else
found_newline = FALSE;

if(found_newline == TRUE) {

current_line++;

/* handle this line of input */
buf[x] = '\x0';
if((tempbuf = (char *)strdup(buf))) {

/* first line contains short plugin output and optional perf data */
if(current_line == 1) {

/* get the short plugin output */
if((ptr = strtok(tempbuf, "|"))) {
if(short_output)
*short_output = (char *)strdup(ptr);

/* get the optional perf data */
if((ptr = strtok(NULL, "\n")))
dbuf_strcat(&db2, ptr);
}
}
else if (buf[x] != '\n')
continue;

/* additional lines contain long plugin output and optional perf data */
else {
/* Handle this line of input. */
buf[x] = '\0';
current_line++;

/* rest of the output is perf data */
if(in_perf_data == TRUE) {
dbuf_strcat(&db2, tempbuf);
dbuf_strcat(&db2, " ");
}
/* The first line contains short plugin output and optional perf data. */
if (current_line == 1) {

/* we're still in long output */
else {

/* perf data separator has been found */
if(strstr(tempbuf, "|")) {

/* NOTE: strtok() causes problems if first character of tempbuf='|', so use my_strtok() instead */
/* get the remaining long plugin output */
if((ptr = my_strtok(tempbuf, "|"))) {

if(current_line > 2)
dbuf_strcat(&db1, "\n");
dbuf_strcat(&db1, ptr);

/* get the perf data */
if((ptr = my_strtok(NULL, "\n"))) {
dbuf_strcat(&db2, ptr);
dbuf_strcat(&db2, " ");
}
}

/* set the perf data flag */
in_perf_data = TRUE;
}

/* just long output */
else {
if(current_line > 2)
dbuf_strcat(&db1, "\n");
dbuf_strcat(&db1, tempbuf);
}
}
/* Get the short plugin output. */
if ((ptr = strtok(buf, "|"))) {
if (short_output) {
strip(ptr); /* Remove leading and trailing whitespace. */
*short_output = strdup(ptr);
}

my_free(tempbuf);
tempbuf = NULL;
/* Get the optional perf data. */
if ((ptr = strtok(NULL, "\n")))
dbuf_strcat(&db2, ptr);
}


/* shift data back to front of buffer and adjust counters */
memmove((void *)&buf[0], (void *)&buf[x + 1], (size_t)((int)used_buf - x - 1));
used_buf -= (x + 1);
buf[used_buf] = '\x0';
x = -1;
}
}

/* save long output */
if(long_output && (db1.buf && strcmp(db1.buf, ""))) {
/* Additional lines contain long plugin output and optional perf data.
/* Once we've hit perf data, the rest of the output is perf data. */
else if (in_perf_data) {
if (*db2.buf)
dbuf_strcat(&db2, " ");
dbuf_strcat(&db2, buf);

if(escape_newlines_please == FALSE)
*long_output = (char *)strdup(db1.buf);

else {
}
/* Look for the perf data separator. */
else if (strstr(buf, "|")) {
in_perf_data = TRUE;

/* escape newlines (and backslashes) in long output */
if((tempbuf = (char *)malloc((strlen(db1.buf) * 2) + 1))) {
/* NOTE: strtok() causes problems if first character of buf='|', so use my_strtok() instead */
/* Get the remaining long plugin output. */
if ((ptr = my_strtok(buf, "|"))) {

for(x = 0, y = 0; db1.buf[x] != '\x0'; x++) {
if (current_line > 2)
dbuf_strcat(&db1, "\n");
dbuf_strcat(&db1, ptr);

if(db1.buf[x] == '\n') {
tempbuf[y++] = '\\';
tempbuf[y++] = 'n';
}
else if(db1.buf[x] == '\\') {
tempbuf[y++] = '\\';
tempbuf[y++] = '\\';
}
else
tempbuf[y++] = db1.buf[x];
/* get the perf data */
if ((ptr = my_strtok(NULL, "\n"))) {
if (*db2.buf)
dbuf_strcat(&db2, " ");
dbuf_strcat(&db2, ptr);
}

tempbuf[y] = '\x0';
*long_output = (char *)strdup(tempbuf);
my_free(tempbuf);
}

}
/* Otherwise it's still just long output. */
else {
if (current_line > 2)
dbuf_strcat(&db1, "\n");
dbuf_strcat(&db1, buf);
}

/* Point buf to the start of the next line. At this point buf[x] == '\0',
* and *(buf+x+1) will be a valid memory reference on our next iteration
* or we are at the end of input (eof == TRUE). */
buf += x + 1;
x = -1; /* x will be incremented to 0 by the loop update. */
}

/* save perf data */
if(perf_data && (db2.buf && strcmp(db2.buf, "")))
*perf_data = (char *)strdup(db2.buf);
/* Save long output. */
if (long_output && db1.buf && *db1.buf) {
/* Escape newlines (and backslashes) in long output if requested. */
if (escape_newlines_please)
*long_output = escape_newlines(db1.buf);
else
*long_output = strdup(db1.buf);
}

/* strip short output and perf data */
if(short_output)
strip(*short_output);
if(perf_data)
strip(*perf_data);
/* Save perf data. */
if (perf_data && db2.buf && *db2.buf) {
strip(db2.buf); /* Remove leading and trailing whitespace. */
*perf_data = strdup(db2.buf);
}

/* free dynamic buffers */
dbuf_free(&db1);
Expand Down

0 comments on commit 0834195

Please sign in to comment.