Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/zabbix/zabbix.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndris Mednis <Andris.Mednis@zabbix.com>2014-03-05 19:40:17 +0400
committerAndris Mednis <Andris.Mednis@zabbix.com>2014-03-05 19:40:17 +0400
commit785119d15160b9c28afa616bc28812a31998e37f (patch)
tree074d8bb336808f08a9e5c8f28e31d96b808f424e
parentc53f714dba773e6c37ba69f5fbc6be294c39dd22 (diff)
...G...... [ZBX-7740] fixed agent crash if logrt and log items are not supported
...G...PS. [ZBX-6731] faster processing of log files by agent on Unix ...G...PS. [ZBX-6729] faster processing of log files by agent on Microsoft Windows The main goal of the change is to improve performance (less time, fewer system calls, less CPU usage) of active checks for log[] and logrt[] items. Platform specific methods like GNU/Linux "i-notify" are not used. Please note that this change does not modify limits on maximum number of log file records checked in one check and number of matching records sent to server in one check. Before this change: - new log file records were read record-by-record, reading of each record involved opening, seeking, reading and closing of the log file. - zbx_regexp() and regexp_sub() compiled a regular expression every time, - for logrt[] items file mask regular expression was compiled for every file in a check. The log files were selected first by regular expression (file mask), then by last mtime. - due to a bug the log[] item does not go into NOTSUPPORTED state when the log file is not accessible. After this change: - new log file records are read into 256 kB buffer in one operation, then a regular expression is applied to every record in the buffer and matching records are sent to Zabbix server. No repetitive file opening, seeking for every record. - zbx_regexp() and regexp_sub() compile a regular expresion and rememeber it. This increases performance if the same regular expression is used several times in a row (e.g. for matching log file records). This affects also server and proxy. - for logrt[] items file mask regular expression is compiled only once in a check. The log files are selected first by the last mtime, then by regular expression (file mask) to improve performance. - if the log file is not accessible, the log[] item goes into NOTSUPPORTED state. - Zabbix agent detects oversized log file records (longer than 256 kB). Only the first 256 kB are matched against the regular expression and the rest of the record is ignored. - a new Zabbix datatype "zbx_offset_t" and a new function "zbx_lseek()" is added to encapsulate "lseek()" differences between UNIX/GNU/Linux and Microsoft Windows, - The Make files for "zabbix_get" and "zabbix_sender" on Microsoft Windows do not include src/libs/zbxcommon/file.c anymore.
-rw-r--r--build/win32/project/Makefile_get.inc4
-rw-r--r--build/win32/project/Makefile_sender.inc4
-rw-r--r--include/common.h5
-rw-r--r--include/zbxtypes.h6
-rw-r--r--src/libs/zbxcommon/file.c139
-rw-r--r--src/libs/zbxregexp/zbxregexp.c74
-rw-r--r--src/zabbix_agent/active.c165
-rw-r--r--src/zabbix_agent/active.h1
-rw-r--r--src/zabbix_agent/logfiles.c627
-rw-r--r--src/zabbix_agent/logfiles.h15
10 files changed, 670 insertions, 370 deletions
diff --git a/build/win32/project/Makefile_get.inc b/build/win32/project/Makefile_get.inc
index 3172f7d8eaa..c136db6c04b 100644
--- a/build/win32/project/Makefile_get.inc
+++ b/build/win32/project/Makefile_get.inc
@@ -13,7 +13,6 @@ OBJS = ..\..\..\src\libs\zbxcommon\comms.o \
..\..\..\src\libs\zbxcommon\str.o \
..\..\..\src\libs\zbxcommon\xml.o \
..\..\..\src\libs\zbxcommon\zbxgetopt.o \
- ..\..\..\src\libs\zbxcommon\file.o \
..\..\..\src\libs\zbxcomms\comms.o \
..\..\..\src\libs\zbxconf\cfg.o \
..\..\..\src\libs\zbxcrypto\base64.o \
@@ -55,9 +54,6 @@ $(RESOURCE_RES): $(RESOURCE_RC) $(RESOURCE_H) $(DESC_H)
..\..\..\src\libs\zbxcommon\zbxgetopt.o: ..\..\..\src\libs\zbxcommon\zbxgetopt.c
$(CC) ..\..\..\src\libs\zbxcommon\zbxgetopt.c /Fo"..\..\..\src\libs\zbxcommon\zbxgetopt.o" $(CFLAGS)
-..\..\..\src\libs\zbxcommon\file.o: ..\..\..\src\libs\zbxcommon\file.c
- $(CC) ..\..\..\src\libs\zbxcommon\file.c /Fo"..\..\..\src\libs\zbxcommon\file.o" $(CFLAGS)
-
..\..\..\src\libs\zbxcomms\comms.o: ..\..\..\src\libs\zbxcomms\comms.c
$(CC) ..\..\..\src\libs\zbxcomms\comms.c /Fo"..\..\..\src\libs\zbxcomms\comms.o" $(CFLAGS)
diff --git a/build/win32/project/Makefile_sender.inc b/build/win32/project/Makefile_sender.inc
index 2a699db7bab..836ee40de41 100644
--- a/build/win32/project/Makefile_sender.inc
+++ b/build/win32/project/Makefile_sender.inc
@@ -13,7 +13,6 @@ OBJS = ..\..\..\src\libs\zbxcommon\comms.o \
..\..\..\src\libs\zbxcommon\str.o \
..\..\..\src\libs\zbxcommon\xml.o \
..\..\..\src\libs\zbxcommon\zbxgetopt.o \
- ..\..\..\src\libs\zbxcommon\file.o \
..\..\..\src\libs\zbxcomms\comms.o \
..\..\..\src\libs\zbxconf\cfg.o \
..\..\..\src\libs\zbxcrypto\base64.o \
@@ -60,9 +59,6 @@ $(RESOURCE_RES): $(RESOURCE_RC) $(RESOURCE_H) $(DESC_H)
..\..\..\src\libs\zbxcommon\zbxgetopt.o: ..\..\..\src\libs\zbxcommon\zbxgetopt.c
$(CC) ..\..\..\src\libs\zbxcommon\zbxgetopt.c /Fo"..\..\..\src\libs\zbxcommon\zbxgetopt.o" $(CFLAGS)
-..\..\..\src\libs\zbxcommon\file.o: ..\..\..\src\libs\zbxcommon\file.c
- $(CC) ..\..\..\src\libs\zbxcommon\file.c /Fo"..\..\..\src\libs\zbxcommon\file.o" $(CFLAGS)
-
..\..\..\src\libs\zbxcomms\comms.o: ..\..\..\src\libs\zbxcomms\comms.c
$(CC) ..\..\..\src\libs\zbxcomms\comms.c /Fo"..\..\..\src\libs\zbxcomms\comms.o" $(CFLAGS)
diff --git a/include/common.h b/include/common.h
index 54237db53b2..172ca462df9 100644
--- a/include/common.h
+++ b/include/common.h
@@ -1028,6 +1028,11 @@ double str2double(const char *str);
int __zbx_stat(const char *path, struct stat *buf);
int __zbx_open(const char *pathname, int flags);
#endif /* _WINDOWS && _UNICODE */
+
+typedef int (*zbx_process_value_func_t)(const char *, unsigned short, const char *, const char *, const char *,
+ zbx_uint64_t *, int *, unsigned long *, const char *, unsigned short *, unsigned long *, unsigned char);
+
+void find_cr_lf_szbyte(const char *encoding, const char **cr, const char **lf, size_t *szbyte);
int zbx_read(int fd, char *buf, size_t count, const char *encoding);
int zbx_is_regular_file(const char *path);
diff --git a/include/zbxtypes.h b/include/zbxtypes.h
index b3aaa71fe60..fa300cb9508 100644
--- a/include/zbxtypes.h
+++ b/include/zbxtypes.h
@@ -74,6 +74,9 @@
# define strcasecmp lstrcmpiA
+typedef __int64 zbx_offset_t;
+#define zbx_lseek(fd, offset, whence) _lseeki64(fd, (zbx_offset_t)(offset), whence)
+
#else /* _WINDOWS */
# define zbx_stat(path, buf) stat(path, buf)
@@ -110,6 +113,9 @@
# define PATH_SEPARATOR '/'
#endif
+typedef off_t zbx_offset_t;
+#define zbx_lseek(fd, offset, whence) lseek(fd, (zbx_offset_t)(offset), whence)
+
#endif /* _WINDOWS */
#define ZBX_FS_SIZE_T ZBX_FS_UI64
diff --git a/src/libs/zbxcommon/file.c b/src/libs/zbxcommon/file.c
index 22f45afe418..5f7aee9125d 100644
--- a/src/libs/zbxcommon/file.c
+++ b/src/libs/zbxcommon/file.c
@@ -45,69 +45,93 @@ int __zbx_open(const char *pathname, int flags)
}
#endif
-/*
- * Reads in at most one less than size characters from a file descriptor and stores them into the buffer pointed to by s.
- * Reading stops after a newline. If a newline is read, it is stored into the buffer.
- *
- * On success, the number of bytes read is returned (zero indicates end of file).
- * On error, -1 is returned, and errno is set appropriately.
- */
+void find_cr_lf_szbyte(const char *encoding, const char **cr, const char **lf, size_t *szbyte)
+{
+ /* default is single-byte character set */
+ *cr = "\r";
+ *lf = "\n";
+ *szbyte = 1;
+
+ if ('\0' != *encoding)
+ {
+ if (0 == strcasecmp(encoding, "UNICODE") || 0 == strcasecmp(encoding, "UNICODELITTLE") ||
+ 0 == strcasecmp(encoding, "UTF-16") || 0 == strcasecmp(encoding, "UTF-16LE") ||
+ 0 == strcasecmp(encoding, "UTF16") || 0 == strcasecmp(encoding, "UTF16LE"))
+ {
+ *cr = "\r\0";
+ *lf = "\n\0";
+ *szbyte = 2;
+ }
+ else if (0 == strcasecmp(encoding, "UNICODEBIG") || 0 == strcasecmp(encoding, "UNICODEFFFE") ||
+ 0 == strcasecmp(encoding, "UTF-16BE") || 0 == strcasecmp(encoding, "UTF16BE"))
+ {
+ *cr = "\0\r";
+ *lf = "\0\n";
+ *szbyte = 2;
+ }
+ else if (0 == strcasecmp(encoding, "UTF-32") || 0 == strcasecmp(encoding, "UTF-32LE") ||
+ 0 == strcasecmp(encoding, "UTF32") || 0 == strcasecmp(encoding, "UTF32LE"))
+ {
+ *cr = "\r\0\0\0";
+ *lf = "\n\0\0\0";
+ *szbyte = 4;
+ }
+ else if (0 == strcasecmp(encoding, "UTF-32BE") || 0 == strcasecmp(encoding, "UTF32BE"))
+ {
+ *cr = "\0\0\0\r";
+ *lf = "\0\0\0\n";
+ *szbyte = 4;
+ }
+ }
+}
+
+/******************************************************************************
+ * *
+ * Function: zbx_read *
+ * *
+ * Purpose: Read one text line from a file descriptor into buffer *
+ * *
+ * Parameters: fd - [IN] file descriptor to read from *
+ * buf - [IN] buffer to read into *
+ * count - [IN] buffer size in bytes *
+ * encoding - [IN] pointer to a text string describing encoding. *
+ * The following encodings are recognized: *
+ * "UNICODE" *
+ * "UNICODEBIG" *
+ * "UNICODEFFFE" *
+ * "UNICODELITTLE" *
+ * "UTF-16" "UTF16" *
+ * "UTF-16BE" "UTF16BE" *
+ * "UTF-16LE" "UTF16LE" *
+ * "UTF-32" "UTF32" *
+ * "UTF-32BE" "UTF32BE" *
+ * "UTF-32LE" "UTF32LE". *
+ * "" (empty string) means a single-byte character set.*
+ * *
+ * Return value: On success, the number of bytes read is returned (0 (zero) *
+ * indicates end of file). *
+ * On error, -1 is returned and errno is set appropriately. *
+ * *
+ * Comments: Reading stops after a newline. If the newline is read, it is *
+ * stored into the buffer. *
+ * *
+ ******************************************************************************/
int zbx_read(int fd, char *buf, size_t count, const char *encoding)
{
size_t i, szbyte;
const char *cr, *lf;
int nbytes;
-#ifdef _WINDOWS
- __int64 offset;
-#else
- off_t offset;
-#endif
+ zbx_offset_t offset;
-#ifdef _WINDOWS
- offset = _lseeki64(fd, 0, SEEK_CUR);
-#else
- offset = lseek(fd, 0, SEEK_CUR);
-#endif
+ if ((zbx_offset_t)-1 == (offset = zbx_lseek(fd, 0, SEEK_CUR)))
+ return -1;
if (0 >= (nbytes = (int)read(fd, buf, count)))
return nbytes;
- if (0 == strcasecmp(encoding, "UNICODE") || 0 == strcasecmp(encoding, "UNICODELITTLE") ||
- 0 == strcasecmp(encoding, "UTF-16") || 0 == strcasecmp(encoding, "UTF-16LE") ||
- 0 == strcasecmp(encoding, "UTF16") || 0 == strcasecmp(encoding, "UTF16LE"))
- {
- cr = "\r\0";
- lf = "\n\0";
- szbyte = 2;
- }
- else if (0 == strcasecmp(encoding, "UNICODEBIG") || 0 == strcasecmp(encoding, "UNICODEFFFE") ||
- 0 == strcasecmp(encoding, "UTF-16BE") || 0 == strcasecmp(encoding, "UTF16BE"))
- {
- cr = "\0\r";
- lf = "\0\n";
- szbyte = 2;
- }
- else if (0 == strcasecmp(encoding, "UTF-32") || 0 == strcasecmp(encoding, "UTF-32LE") ||
- 0 == strcasecmp(encoding, "UTF32") || 0 == strcasecmp(encoding, "UTF32LE"))
- {
- cr = "\r\0\0\0";
- lf = "\n\0\0\0";
- szbyte = 4;
- }
- else if (0 == strcasecmp(encoding, "UTF-32BE") || 0 == strcasecmp(encoding, "UTF32BE"))
- {
- cr = "\0\0\0\r";
- lf = "\0\0\0\n";
- szbyte = 4;
- }
- else /* Single or Multi Byte Character Sets */
- {
- cr = "\r";
- lf = "\n";
- szbyte = 1;
- }
+ find_cr_lf_szbyte(encoding, &cr, &lf, &szbyte);
- for (i = 0; i + szbyte <= (size_t)nbytes; i += szbyte)
+ for (i = 0; i <= (size_t)nbytes - szbyte; i += szbyte)
{
if (0 == memcmp(&buf[i], lf, szbyte)) /* LF (Unix) */
{
@@ -117,18 +141,17 @@ int zbx_read(int fd, char *buf, size_t count, const char *encoding)
if (0 == memcmp(&buf[i], cr, szbyte)) /* CR (Mac) */
{
- if (i + szbyte < (size_t)nbytes && 0 == memcmp(&buf[i + szbyte], lf, szbyte)) /* CR+LF (Windows) */
+ /* CR+LF (Windows) ? */
+ if (i < (size_t)nbytes - szbyte && 0 == memcmp(&buf[i + szbyte], lf, szbyte))
i += szbyte;
+
i += szbyte;
break;
}
}
-#ifdef _WINDOWS
- _lseeki64(fd, offset + i, SEEK_SET);
-#else
- lseek(fd, offset + i, SEEK_SET);
-#endif
+ if ((zbx_offset_t)-1 == zbx_lseek(fd, offset + (zbx_offset_t)i, SEEK_SET))
+ return -1;
return (int)i;
}
diff --git a/src/libs/zbxregexp/zbxregexp.c b/src/libs/zbxregexp/zbxregexp.c
index ec457f48a04..4964c45f3c0 100644
--- a/src/libs/zbxregexp/zbxregexp.c
+++ b/src/libs/zbxregexp/zbxregexp.c
@@ -27,28 +27,46 @@
static char *zbx_regexp(const char *string, const char *pattern, int *len, int flags)
{
char *c = NULL;
- regex_t re;
+ static char *old_pattern = NULL;
+ static int old_flags;
+ static regex_t re;
regmatch_t match;
if (NULL != len)
*len = 0;
- if (NULL != string)
- {
- if (0 == regcomp(&re, pattern, flags))
- {
- if (0 == regexec(&re, string, (size_t)1, &match, 0)) /* matched */
- {
- c = (char *)string + match.rm_so;
+ if (NULL == string)
+ goto out;
- if (NULL != len)
- *len = match.rm_eo - match.rm_so;
- }
+ /* performance optimization: if possible then reuse the last compiled regexp */
- regfree(&re);
- }
+ if (NULL == old_pattern)
+ goto compile;
+
+ if (0 != strcmp(old_pattern, pattern) || old_flags != flags)
+ regfree(&re);
+ else
+ goto execute;
+compile:
+ if (0 == regcomp(&re, pattern, flags))
+ {
+ old_pattern = zbx_strdup(old_pattern, pattern);
+ old_flags = flags;
+ }
+ else
+ {
+ zbx_free(old_pattern);
+ goto out;
}
+execute:
+ if (0 == regexec(&re, string, (size_t)1, &match, 0)) /* matched */
+ {
+ c = (char *)string + match.rm_so;
+ if (NULL != len)
+ *len = match.rm_eo - match.rm_so;
+ }
+out:
return c;
}
@@ -174,9 +192,11 @@ out:
*********************************************************************************/
static char *regexp_sub(const char *string, const char *pattern, const char *output_template, int flags)
{
- regex_t re;
- regmatch_t match[10];
+ static regex_t re;
+ regmatch_t match[10]; /* up to 10 capture groups in regexp */
char *ptr = NULL;
+ static char *old_pattern = NULL;
+ static int old_flags;
if (NULL == string)
return NULL;
@@ -184,14 +204,30 @@ static char *regexp_sub(const char *string, const char *pattern, const char *out
if (NULL == output_template || '\0' == *output_template)
flags |= REG_NOSUB;
- if (0 != regcomp(&re, pattern, flags))
- return NULL;
+ /* performance optimization: if possible then reuse the last compiled regexp */
+ if (NULL == old_pattern)
+ goto compile;
+
+ if (0 != strcmp(old_pattern, pattern) || old_flags != flags)
+ regfree(&re);
+ else
+ goto execute;
+compile:
+ if (0 == regcomp(&re, pattern, flags))
+ {
+ old_pattern = zbx_strdup(old_pattern, pattern);
+ old_flags = flags;
+ }
+ else
+ {
+ zbx_free(old_pattern);
+ return NULL;
+ }
+execute:
if (0 == regexec(&re, string, ARRSIZE(match), match, 0))
ptr = regexp_sub_replace(string, output_template, match, ARRSIZE(match));
- regfree(&re);
-
return ptr;
}
diff --git a/src/zabbix_agent/active.c b/src/zabbix_agent/active.c
index 8003bfebd4c..167d664e3e0 100644
--- a/src/zabbix_agent/active.c
+++ b/src/zabbix_agent/active.c
@@ -32,7 +32,6 @@
#include "comms.h"
#include "threads.h"
#include "zbxjson.h"
-#include "zbxregexp.h"
#if defined(ZABBIX_SERVICE)
# include "service.h"
@@ -126,7 +125,10 @@ static void free_active_metrics(void)
zabbix_log(LOG_LEVEL_DEBUG, "In free_active_metrics()");
for (i = 0; NULL != active_metrics[i].key; i++)
+ {
zbx_free(active_metrics[i].key);
+ zbx_free(active_metrics[i].key_orig);
+ }
zbx_free(active_metrics);
@@ -175,6 +177,7 @@ static void add_check(const char *key, const char *key_orig, int refresh, zbx_ui
active_metrics[i].key = strdup(key);
active_metrics[i].lastlogsize = lastlogsize;
active_metrics[i].mtime = mtime;
+ active_metrics[i].big_rec = 0;
}
/* replace metric */
@@ -198,6 +201,7 @@ static void add_check(const char *key, const char *key_orig, int refresh, zbx_ui
active_metrics[i].mtime = mtime;
/* can skip existing log[] and eventlog[] data */
active_metrics[i].skip_old_data = active_metrics[i].lastlogsize ? 0 : 1;
+ active_metrics[i].big_rec = 0;
/* move to the last metric */
i++;
@@ -720,7 +724,13 @@ ret:
* *
* Author: Alexei Vladishev *
* *
- * Comments: *
+ * Comments: ATTENTION! This function's address and pointers to arguments *
+ * are described in Zabbix defined type "zbx_process_value_func_t" *
+ * and used when calling process_log(), process_logrt() and *
+ * zbx_read2(). If you ever change this process_value() arguments *
+ * or return value do not forget to synchronize changes with the *
+ * defined type "zbx_process_value_func_t" and implementations of *
+ * process_log(), process_logrt(), zbx_read2() and their callers. *
* *
******************************************************************************/
static int process_value(
@@ -799,7 +809,7 @@ static int process_value(
memset(el, 0, sizeof(ZBX_ACTIVE_BUFFER_ELEMENT));
el->host = zbx_strdup(NULL, host);
el->key = zbx_strdup(NULL, key);
- el->value = (NULL == value) ? NULL : zbx_strdup(NULL, value);
+ el->value = zbx_strdup(NULL, value);
if (NULL != source)
el->source = strdup(source);
@@ -830,18 +840,17 @@ out:
static void process_active_checks(char *server, unsigned short port)
{
const char *__function_name = "process_active_checks";
- register int i, s_count, p_count;
+ int i, s_count, p_count;
char **pvalue;
int now, send_err = SUCCEED, ret;
- char *value = NULL, *item_value = NULL;
- zbx_uint64_t lastlogsize;
- int mtime;
char params[MAX_STRING_LEN], filename[MAX_STRING_LEN];
char pattern[MAX_STRING_LEN], output_template[MAX_STRING_LEN];
/* checks `log', `eventlog', `logrt' may contain parameter, which overrides CONFIG_MAX_LINES_PER_SECOND */
char maxlines_persec_str[16];
int maxlines_persec;
#ifdef _WINDOWS
+ char *value = NULL;
+ zbx_uint64_t lastlogsize;
unsigned long timestamp, logeventid;
unsigned short severity;
char key_severity[MAX_STRING_LEN], str_severity[32] /* for `regex_match_ex' */;
@@ -880,7 +889,6 @@ static void process_active_checks(char *server, unsigned short port)
do
{
- /* simple try realization */
if (ZBX_COMMAND_WITH_PARAMS != parse_command(active_metrics[i].key, NULL, 0,
params, sizeof(params)))
{
@@ -919,58 +927,18 @@ static void process_active_checks(char *server, unsigned short port)
if (0 != get_param(params, 6, output_template, sizeof(output_template)))
*output_template = '\0';
- s_count = 0;
- p_count = 0;
- lastlogsize = active_metrics[i].lastlogsize;
-
- while (SUCCEED == (ret = process_log(filename, &lastlogsize, &value, encoding,
- active_metrics[i].skip_old_data)))
- {
- active_metrics[i].skip_old_data = 0;
-
- /* End of file. The file could become empty, must save `lastlogsize'. */
- if (NULL == value)
- {
- active_metrics[i].lastlogsize = lastlogsize;
- break;
- }
-
- if (SUCCEED == regexp_sub_ex(&regexps, value, pattern, ZBX_CASE_SENSITIVE,
- output_template, &item_value))
- {
- send_err = process_value(server, port, CONFIG_HOSTNAME,
- active_metrics[i].key_orig, item_value, &lastlogsize,
- NULL, NULL, NULL, NULL, NULL, 1);
-
- zbx_free(item_value);
-
- s_count++;
- }
- p_count++;
-
- zbx_free(value);
-
- if (SUCCEED == send_err)
- active_metrics[i].lastlogsize = lastlogsize;
- else
- {
- /* buffer is full, stop processing active checks */
- /* till the buffer is cleared */
- lastlogsize = active_metrics[i].lastlogsize;
- goto ret;
- }
+ /* do not flood Zabbix server if file grows too fast */
+ s_count = maxlines_persec * active_metrics[i].refresh;
- /* do not flood Zabbix server if file grows too fast */
- if (s_count >= (maxlines_persec * active_metrics[i].refresh))
- break;
-
- /* do not flood local system if file grows too fast */
- if (p_count >= (4 * maxlines_persec * active_metrics[i].refresh))
- break;
- } /* while processing a log */
+ /* do not flood local system if file grows too fast */
+ p_count = 4 * maxlines_persec * active_metrics[i].refresh;
+ ret = process_log(filename, &active_metrics[i].lastlogsize, NULL,
+ &active_metrics[i].skip_old_data, &active_metrics[i].big_rec, encoding,
+ &regexps, pattern, output_template, &p_count, &s_count, process_value,
+ server, port, CONFIG_HOSTNAME, active_metrics[i].key_orig);
}
- while (0); /* simple try realization */
+ while (0);
if (FAIL == ret)
{
@@ -978,8 +946,9 @@ static void process_active_checks(char *server, unsigned short port)
zabbix_log(LOG_LEVEL_WARNING, "active check \"%s\" is not supported",
active_metrics[i].key);
- process_value(server, port, CONFIG_HOSTNAME, active_metrics[i].key_orig, NULL,
- &active_metrics[i].lastlogsize, NULL, NULL, NULL, NULL, NULL, 0);
+ process_value(server, port, CONFIG_HOSTNAME, active_metrics[i].key_orig,
+ ZBX_NOTSUPPORTED, &active_metrics[i].lastlogsize, NULL, NULL, NULL,
+ NULL, NULL, 0);
}
}
/* special processing for log files with rotation */
@@ -989,7 +958,6 @@ static void process_active_checks(char *server, unsigned short port)
do
{
- /* simple try realization */
if (ZBX_COMMAND_WITH_PARAMS != parse_command(active_metrics[i].key, NULL, 0,
params, sizeof(params)))
{
@@ -1028,64 +996,18 @@ static void process_active_checks(char *server, unsigned short port)
if (0 != get_param(params, 6, output_template, sizeof(output_template)))
*output_template = '\0';
- s_count = 0;
- p_count = 0;
- lastlogsize = active_metrics[i].lastlogsize;
- mtime = active_metrics[i].mtime;
-
- while (SUCCEED == (ret = process_logrt(filename, &lastlogsize, &mtime, &value,
- encoding, active_metrics[i].skip_old_data)))
- {
- active_metrics[i].skip_old_data = 0;
-
- /* End of file. The file could become empty,*/
- /* must save `lastlogsize' and `mtime'. */
- if (NULL == value)
- {
- active_metrics[i].lastlogsize = lastlogsize;
- active_metrics[i].mtime = mtime;
- break;
- }
-
- if (SUCCEED == regexp_sub_ex(&regexps, value, pattern, ZBX_CASE_SENSITIVE,
- output_template, &item_value))
- {
- send_err = process_value(server, port, CONFIG_HOSTNAME,
- active_metrics[i].key_orig, item_value, &lastlogsize,
- &mtime, NULL, NULL, NULL, NULL, 1);
-
- zbx_free(item_value);
-
- s_count++;
- }
- p_count++;
-
- zbx_free(value);
-
- if (SUCCEED == send_err)
- {
- active_metrics[i].lastlogsize = lastlogsize;
- active_metrics[i].mtime = mtime;
- }
- else
- {
- /* buffer is full, stop processing active checks */
- /* till the buffer is cleared */
- lastlogsize = active_metrics[i].lastlogsize;
- mtime = active_metrics[i].mtime;
- goto ret;
- }
+ /* do not flood Zabbix server if files grow too fast */
+ s_count = maxlines_persec * active_metrics[i].refresh;
- /* do not flood Zabbix server if file grows too fast */
- if (s_count >= (maxlines_persec * active_metrics[i].refresh))
- break;
+ /* do not flood local system if files grow too fast */
+ p_count = 4 * maxlines_persec * active_metrics[i].refresh;
- /* do not flood local system if file grows too fast */
- if (p_count >= (4 * maxlines_persec * active_metrics[i].refresh))
- break;
- } /* while processing a log */
+ ret = process_logrt(filename, &active_metrics[i].lastlogsize, &active_metrics[i].mtime,
+ &active_metrics[i].skip_old_data, &active_metrics[i].big_rec, encoding,
+ &regexps, pattern, output_template, &p_count, &s_count, process_value,
+ server, port, CONFIG_HOSTNAME, active_metrics[i].key_orig);
}
- while (0); /* simple try realization */
+ while (0);
if (FAIL == ret)
{
@@ -1093,9 +1015,9 @@ static void process_active_checks(char *server, unsigned short port)
zabbix_log(LOG_LEVEL_WARNING, "active check \"%s\" is not supported",
active_metrics[i].key);
- process_value(server, port, CONFIG_HOSTNAME, active_metrics[i].key_orig, NULL,
- &active_metrics[i].lastlogsize, &active_metrics[i].mtime,
- NULL, NULL, NULL, NULL, 0);
+ process_value(server, port, CONFIG_HOSTNAME, active_metrics[i].key_orig,
+ ZBX_NOTSUPPORTED, &active_metrics[i].lastlogsize,
+ &active_metrics[i].mtime, NULL, NULL, NULL, NULL, 0);
}
}
/* special processing for eventlog */
@@ -1361,8 +1283,9 @@ static void process_active_checks(char *server, unsigned short port)
zabbix_log(LOG_LEVEL_WARNING, "active check \"%s\" is not supported",
active_metrics[i].key);
- process_value(server, port, CONFIG_HOSTNAME, active_metrics[i].key_orig, NULL,
- &active_metrics[i].lastlogsize, NULL, NULL, NULL, NULL, NULL, 0);
+ process_value(server, port, CONFIG_HOSTNAME, active_metrics[i].key_orig,
+ ZBX_NOTSUPPORTED, &active_metrics[i].lastlogsize, NULL, NULL, NULL,
+ NULL, NULL, 0);
}
}
else
@@ -1391,7 +1314,9 @@ static void process_active_checks(char *server, unsigned short port)
}
active_metrics[i].nextcheck = (int)time(NULL) + active_metrics[i].refresh;
}
+#ifdef _WINDOWS
ret:
+#endif
zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __function_name);
}
diff --git a/src/zabbix_agent/active.h b/src/zabbix_agent/active.h
index a561903ae01..ef41954ea82 100644
--- a/src/zabbix_agent/active.h
+++ b/src/zabbix_agent/active.h
@@ -75,6 +75,7 @@ typedef struct
int mtime;
unsigned char skip_old_data; /* for processing [event]log metrics */
unsigned char state;
+ int big_rec; /* for reading logfiles: 0 - normal record, 1 - long unfinished record */
}
ZBX_ACTIVE_METRIC;
diff --git a/src/zabbix_agent/logfiles.c b/src/zabbix_agent/logfiles.c
index baf331ba06b..30da8928d9e 100644
--- a/src/zabbix_agent/logfiles.c
+++ b/src/zabbix_agent/logfiles.c
@@ -20,7 +20,10 @@
#include "common.h"
#include "logfiles.h"
#include "log.h"
-#include "zbxregexp.h"
+
+#if defined(_WINDOWS)
+# include "gnuregex.h"
+#endif /* _WINDOWS */
/******************************************************************************
* *
@@ -349,28 +352,55 @@ out:
* *
* Function: process_logrt *
* *
- * Purpose: Get message from logfile with rotation *
- * *
- * Parameters: filename - logfile name (regular expression with a path) *
- * lastlogsize - offset for message *
- * mtime - last modification time of the file *
- * value - pointer for logged message *
+ * Purpose: Find new records in logfiles with rotation *
+ * *
+ * Parameters: *
+ * filename - [IN] logfile name (regular expression with a path) *
+ * lastlogsize - [IN/OUT] offset from the beginning of the file *
+ * mtime - [IN/OUT] last modification time of the file *
+ * skip_old_data - [IN/OUT] start from the beginning of the file or *
+ * jump to the end *
+ * big_rec - [IN/OUT] state variable to remember whether a long *
+ * record is being processed *
+ * encoding - [IN] text string describing encoding. *
+ * The following encodings are recognized: *
+ * "UNICODE" *
+ * "UNICODEBIG" *
+ * "UNICODEFFFE" *
+ * "UNICODELITTLE" *
+ * "UTF-16" "UTF16" *
+ * "UTF-16BE" "UTF16BE" *
+ * "UTF-16LE" "UTF16LE" *
+ * "UTF-32" "UTF32" *
+ * "UTF-32BE" "UTF32BE" *
+ * "UTF-32LE" "UTF32LE". *
+ * "" (empty string) means a single-byte character *
+ * set (e.g. ASCII). *
+ * regexps - [IN] array of regexps *
+ * pattern - [IN] pattern to match *
+ * output_template - [IN] output formatting template *
+ * p_count - [IN/OUT] limit of records to be processed *
+ * s_count - [IN/OUT] limit of records to be sent to server *
+ * process_value - [IN] pointer to function process_value() *
+ * server - [IN] server to send data to *
+ * port - [IN] port to send data to *
+ * hostname - [IN] hostname the data comes from *
+ * key - [IN] item key the data belongs to *
* *
* Return value: returns SUCCEED on successful reading, *
* FAIL on other cases *
* *
* Author: Dmitry Borovikov (logrotation) *
* *
- * Comments: This function allocates memory for 'value', so use zbx_free(). *
- * Return SUCCEED and NULL 'value' if end of file received. *
- * *
******************************************************************************/
-int process_logrt(char *filename, zbx_uint64_t *lastlogsize, int *mtime, char **value, const char *encoding,
- unsigned char skip_old_data)
+int process_logrt(char *filename, zbx_uint64_t *lastlogsize, int *mtime, unsigned char *skip_old_data,
+ int *big_rec, const char *encoding, zbx_vector_ptr_t *regexps, const char *pattern,
+ const char *output_template, int *p_count, int *s_count, zbx_process_value_func_t process_value,
+ const char *server, unsigned short port, const char *hostname, const char *key)
{
const char *__function_name = "process_logrt";
- int i = 0, nbytes, ret = FAIL, logfiles_num = 0, logfiles_alloc = 0, fd = 0, length = 0, j = 0;
- char buffer[MAX_BUFFER_LEN], *directory = NULL, *format = NULL, *logfile_candidate = NULL;
+ int i = 0, ret = FAIL, logfiles_num = 0, logfiles_alloc = 0, j = 0, reg_error;
+ char err_buf[MAX_STRING_LEN], *directory = NULL, *format = NULL, *logfile_candidate = NULL;
struct stat file_buf;
struct st_logfile *logfiles = NULL;
#ifdef _WINDOWS
@@ -382,6 +412,8 @@ int process_logrt(char *filename, zbx_uint64_t *lastlogsize, int *mtime, char **
DIR *dir = NULL;
struct dirent *d_ent = NULL;
#endif
+ regex_t re;
+ time_t now;
zabbix_log(LOG_LEVEL_DEBUG, "In %s() filename:'%s' lastlogsize:" ZBX_FS_UI64 " mtime:%d",
__function_name, filename, *lastlogsize, *mtime);
@@ -389,7 +421,37 @@ int process_logrt(char *filename, zbx_uint64_t *lastlogsize, int *mtime, char **
/* splitting filename */
if (SUCCEED != split_filename(filename, &directory, &format))
{
- zabbix_log(LOG_LEVEL_WARNING, "filename '%s' does not contain a valid directory and/or format", filename);
+ zabbix_log(LOG_LEVEL_WARNING, "filename '%s' does not contain a valid directory and/or format",
+ filename);
+ goto out;
+ }
+
+ if (0 != (reg_error = regcomp(&re, format, REG_EXTENDED | REG_NEWLINE | REG_NOSUB)))
+ {
+ regerror(reg_error, &re, err_buf, sizeof(err_buf));
+ zabbix_log(LOG_LEVEL_WARNING, "Cannot compile a regexp describing filename pattern '%s' for a logrt[] "
+ "item. Error: %s", format, err_buf);
+ goto out;
+ }
+
+ /* Minimize data loss if the system clock has been set back in time. */
+ /* Setting the clock ahead of time is harmless in our case. */
+ if ((time_t)-1 != (now = time(NULL)))
+ {
+ if (now < *mtime)
+ {
+ int old_mtime;
+
+ old_mtime = *mtime;
+ *mtime = (int)now;
+ zabbix_log(LOG_LEVEL_WARNING, "System clock has been set back in time. Setting agent mtime %d "
+ "seconds back.", (int)(old_mtime - now));
+ }
+ }
+ else
+ {
+ zabbix_log(LOG_LEVEL_WARNING, "cannot get system time");
+ regfree(&re);
goto out;
}
@@ -401,7 +463,9 @@ int process_logrt(char *filename, zbx_uint64_t *lastlogsize, int *mtime, char **
if (-1 == (find_handle = _wfindfirst(find_wpath, &find_data)))
{
- zabbix_log(LOG_LEVEL_DEBUG, "cannot get entries from '%s' directory: %s", directory, zbx_strerror(errno));
+ zabbix_log(LOG_LEVEL_WARNING, "cannot open directory \"%s\" for reading: %s", directory,
+ zbx_strerror(errno));
+ regfree(&re);
zbx_free(directory);
zbx_free(format);
zbx_free(find_wpath);
@@ -409,23 +473,24 @@ int process_logrt(char *filename, zbx_uint64_t *lastlogsize, int *mtime, char **
}
zbx_free(find_wpath);
- zabbix_log(LOG_LEVEL_DEBUG, "we are in the Windows directory reading cycle");
do
{
file_name_utf8 = zbx_unicode_to_utf8(find_data.name);
logfile_candidate = zbx_dsprintf(logfile_candidate, "%s%s", directory, file_name_utf8);
- if (-1 == zbx_stat(logfile_candidate, &file_buf) || !S_ISREG(file_buf.st_mode))
+ if (0 == zbx_stat(logfile_candidate, &file_buf))
{
- zabbix_log(LOG_LEVEL_DEBUG, "cannot process read entry '%s'", logfile_candidate);
- }
- else if (NULL != zbx_regexp_match(file_name_utf8, format, &length))
- {
- zabbix_log(LOG_LEVEL_DEBUG, "adding file '%s' to logfiles", logfile_candidate);
- add_logfile(&logfiles, &logfiles_alloc, &logfiles_num, file_name_utf8, (int)file_buf.st_mtime);
+ if (S_ISREG(file_buf.st_mode) &&
+ *mtime <= file_buf.st_mtime &&
+ 0 == regexec(&re, file_name_utf8, (size_t)0, NULL, 0))
+ {
+ zabbix_log(LOG_LEVEL_DEBUG, "adding file '%s' to logfiles", logfile_candidate);
+ add_logfile(&logfiles, &logfiles_alloc, &logfiles_num, file_name_utf8,
+ (int)file_buf.st_mtime);
+ }
}
else
- zabbix_log(LOG_LEVEL_DEBUG, "'%s' does not match '%s'", logfile_candidate, format);
+ zabbix_log(LOG_LEVEL_DEBUG, "cannot process entry '%s'", logfile_candidate);
zbx_free(logfile_candidate);
zbx_free(file_name_utf8);
@@ -437,45 +502,37 @@ int process_logrt(char *filename, zbx_uint64_t *lastlogsize, int *mtime, char **
if (NULL == (dir = opendir(directory)))
{
zabbix_log(LOG_LEVEL_WARNING, "cannot open directory '%s' for reading: %s", directory, zbx_strerror(errno));
+ regfree(&re);
zbx_free(directory);
zbx_free(format);
goto out;
}
- zabbix_log(LOG_LEVEL_DEBUG, "we are in the *nix directory reading cycle");
while (NULL != (d_ent = readdir(dir)))
{
logfile_candidate = zbx_dsprintf(logfile_candidate, "%s%s", directory, d_ent->d_name);
- if (-1 == zbx_stat(logfile_candidate, &file_buf) || !S_ISREG(file_buf.st_mode))
- {
- zabbix_log(LOG_LEVEL_DEBUG, "cannot process read entry '%s'", logfile_candidate);
- }
- else if (NULL != zbx_regexp_match(d_ent->d_name, format, &length))
+ if (0 == zbx_stat(logfile_candidate, &file_buf))
{
- zabbix_log(LOG_LEVEL_DEBUG, "adding file '%s' to logfiles", logfile_candidate);
- add_logfile(&logfiles, &logfiles_alloc, &logfiles_num, d_ent->d_name, (int)file_buf.st_mtime);
+ if (S_ISREG(file_buf.st_mode) &&
+ *mtime <= file_buf.st_mtime &&
+ 0 == regexec(&re, d_ent->d_name, (size_t)0, NULL, 0))
+ {
+ zabbix_log(LOG_LEVEL_DEBUG, "adding file '%s' to logfiles", logfile_candidate);
+ add_logfile(&logfiles, &logfiles_alloc, &logfiles_num, d_ent->d_name,
+ (int)file_buf.st_mtime);
+ }
}
else
- zabbix_log(LOG_LEVEL_DEBUG, "'%s' does not match '%s'", logfile_candidate, format);
+ zabbix_log(LOG_LEVEL_DEBUG, "cannot process entry '%s'", logfile_candidate);
zbx_free(logfile_candidate);
}
#endif /*_WINDOWS*/
- if (1 == skip_old_data)
- i = logfiles_num ? logfiles_num - 1 : 0;
- else
- i = 0;
+ regfree(&re);
- /* find the oldest file that match */
- for ( ; i < logfiles_num; i++)
- {
- if (logfiles[i].mtime < *mtime)
- continue; /* not interested in mtimes less than the given mtime */
- else
- break; /* the first occurrence is found */
- }
+ i = (1 == *skip_old_data && 0 < logfiles_num) ? logfiles_num - 1 : 0;
/* escaping those with the same mtime, taking the latest one (without exceptions!) */
for (j = i + 1; j < logfiles_num; j++)
@@ -486,193 +543,441 @@ int process_logrt(char *filename, zbx_uint64_t *lastlogsize, int *mtime, char **
break; /* all next mtimes are bigger */
}
- /* if all mtimes are less than the given one, take the latest file from existing ones */
- if (0 < logfiles_num && i == logfiles_num)
- i = logfiles_num - 1; /* i cannot be bigger than logfiles_num */
-
- /* processing matched or moving to the newer one and repeating the cycle */
+ /* processing matched logfiles starting from the older one to the newer one */
for (; i < logfiles_num; i++)
{
logfile_candidate = zbx_dsprintf(logfile_candidate, "%s%s", directory, logfiles[i].filename);
- if (0 != zbx_stat(logfile_candidate, &file_buf))/* situation could have changed */
+
+ if (SUCCEED != (ret = process_log(logfile_candidate, lastlogsize, mtime, skip_old_data, big_rec,
+ encoding, regexps, pattern, output_template, p_count, s_count, process_value, server,
+ port, hostname, key)) || 0 >= *p_count || 0 >= *s_count)
{
- zabbix_log(LOG_LEVEL_WARNING, "cannot stat '%s': %s", logfile_candidate, zbx_strerror(errno));
- break; /* must return, situation could have changed */
+ /* Do not make a logrt[] item NOTSUPPORTED if one of selected files is not accessible */
+ /* (can happen during a rotation). Maybe during the next check all will be well. */
+ ret = SUCCEED;
+ break;
}
- if (1 == skip_old_data)
+ if (i != logfiles_num - 1)
{
- *lastlogsize = (zbx_uint64_t)file_buf.st_size;
- zabbix_log(LOG_LEVEL_DEBUG, "skipping existing filename:'%s' lastlogsize:" ZBX_FS_UI64,
- logfile_candidate, *lastlogsize);
+ zbx_free(logfile_candidate);
+ *lastlogsize = 0;
}
+ }
- *mtime = (int)file_buf.st_mtime; /* must contain the latest mtime as possible */
+ if (0 == logfiles_num)
+ {
+ zabbix_log(LOG_LEVEL_WARNING, "there are no files matching '%s' in '%s'", format, directory);
- if (file_buf.st_size < *lastlogsize)
- *lastlogsize = 0; /* maintain backward compatibility */
+ /* do not make a logrt[] item NOTSUPPORTED if there are no matching files in the directory */
+ ret = SUCCEED;
+ }
- if (-1 == (fd = zbx_open(logfile_candidate, O_RDONLY)))
- {
- zabbix_log(LOG_LEVEL_WARNING, "cannot open '%s': %s", logfile_candidate, zbx_strerror(errno));
- break; /* must return, situation could have changed */
- }
+ free_logfiles(&logfiles, &logfiles_alloc, &logfiles_num);
#ifdef _WINDOWS
- if (-1L != _lseeki64(fd, (__int64)*lastlogsize, SEEK_SET))
+ if (0 != find_handle && -1 == _findclose(find_handle))
+ zabbix_log(LOG_LEVEL_WARNING, "cannot close the find directory handle: %s", zbx_strerror(errno));
#else
- if ((off_t)-1 != lseek(fd, (off_t)*lastlogsize, SEEK_SET))
+ if (NULL != dir && -1 == closedir(dir))
+ zabbix_log(LOG_LEVEL_WARNING, "cannot close directory '%s': %s", directory, zbx_strerror(errno));
#endif
+
+ zbx_free(logfile_candidate);
+ zbx_free(directory);
+ zbx_free(format);
+out:
+ zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __function_name, zbx_result_string(ret));
+
+ return ret;
+}
+
+static char *buf_find_newline(char *p, char **p_next, const char *p_end, const char *cr, const char *lf,
+ size_t szbyte)
+{
+ if (1 == szbyte) /* single-byte character set */
+ {
+ for (; p < p_end; p++)
{
- if (-1 != (nbytes = zbx_read(fd, buffer, sizeof(buffer), encoding)))
+ if (0xd < *p || 0xa > *p)
+ continue;
+
+ if (0xa == *p) /* LF (Unix) */
{
- if (0 != nbytes)
+ *p_next = p + 1;
+ return p;
+ }
+
+ if (0xd == *p) /* CR (Mac) */
+ {
+ if (p < p_end - 1 && 0xa == *(p + 1)) /* CR+LF (Windows) */
{
- *lastlogsize += nbytes;
- *value = convert_to_utf8(buffer, nbytes, encoding);
- zbx_rtrim(*value, "\r\n ");
- ret = SUCCEED;
- break; /* return at this point */
+ *p_next = p + 2;
+ return p;
+ }
+
+ *p_next = p + 1;
+ return p;
+ }
+ }
+ return (char *)NULL;
+ }
+ else
+ {
+ while (p <= p_end - szbyte)
+ {
+ if (0 == memcmp(p, lf, szbyte)) /* LF (Unix) */
+ {
+ *p_next = p + szbyte;
+ return p;
+ }
+
+ if (0 == memcmp(p, cr, szbyte)) /* CR (Mac) */
+ {
+ if (p <= p_end - szbyte - szbyte && 0 == memcmp(p + szbyte, lf, szbyte))
+ {
+ /* CR+LF (Windows) */
+ *p_next = p + szbyte + szbyte;
+ return p;
}
- else /* EOF is reached, but there can be other files to try reading from */
+
+ *p_next = p + szbyte;
+ return p;
+ }
+
+ p += szbyte;
+ }
+ return (char *)NULL;
+ }
+}
+
+static int zbx_read2(int fd, zbx_uint64_t *lastlogsize, int *mtime, int *big_rec, const char *encoding,
+ zbx_vector_ptr_t *regexps, const char *pattern, const char *output_template, int *p_count, int *s_count,
+ zbx_process_value_func_t process_value, const char *server, unsigned short port, const char *hostname,
+ const char *key)
+{
+ int ret, nbytes;
+ const char *cr, *lf, *p_end;
+ char *p_start, *p, *p_nl, *p_next, *item_value = NULL;
+ size_t szbyte;
+ zbx_offset_t offset;
+ static char *buf = NULL;
+ int send_err;
+ zbx_uint64_t lastlogsize1;
+
+#define BUF_SIZE (256 * ZBX_KIBIBYTE) /* The longest encodings use 4-bytes for every character. To send */
+ /* up to 64 k characters to the Zabbix server a 256 kB buffer might */
+ /* be required. */
+ if (NULL == buf)
+ {
+ buf = zbx_malloc(buf, (size_t)(BUF_SIZE + 1));
+ }
+
+ find_cr_lf_szbyte(encoding, &cr, &lf, &szbyte);
+
+ for (;;)
+ {
+ if (0 >= *p_count || 0 >= *s_count)
+ {
+ /* limit on number of processed or sent-to-server lines reached */
+ ret = SUCCEED;
+ goto out;
+ }
+
+ if ((zbx_offset_t)-1 == (offset = zbx_lseek(fd, 0, SEEK_CUR)))
+ {
+ *big_rec = 0;
+ ret = FAIL;
+ goto out;
+ }
+
+ nbytes = (int)read(fd, buf, (size_t)BUF_SIZE);
+
+ if (-1 == nbytes)
+ {
+ /* error on read */
+ *big_rec = 0;
+ ret = FAIL;
+ goto out;
+ }
+
+ if (0 == nbytes)
+ {
+ /* end of file reached */
+ ret = SUCCEED;
+ goto out;
+ }
+
+ p_start = buf; /* beginning of current line */
+ p = buf; /* current byte */
+ p_end = buf + (size_t)nbytes; /* no data from this position */
+
+ if (NULL == (p_nl = buf_find_newline(p, &p_next, p_end, cr, lf, szbyte)))
+ {
+ if (BUF_SIZE > nbytes)
+ {
+ /* Buffer is not full (no more data available) and there is no "newline" in it. */
+ /* Do not analyze it now, keep the same position in the file and wait the next check, */
+ /* maybe more data will come. */
+
+ *lastlogsize = (zbx_uint64_t)offset;
+ ret = SUCCEED;
+ goto out;
+ }
+ else
+ {
+ /* buffer is full and there is no "newline" in it */
+
+ if (0 == *big_rec)
{
- if (i == logfiles_num - 1)
+ /* It is the first, beginning part of a long record. Match it against the */
+ /* regexp now (our buffer length corresponds to what we can save in the */
+ /* database). */
+
+ char *value = NULL;
+
+ buf[BUF_SIZE] = '\0';
+
+ if ('\0' != *encoding)
+ value = convert_to_utf8(buf, (size_t)BUF_SIZE, encoding);
+ else
+ value = buf;
+
+ zabbix_log(LOG_LEVEL_WARNING, "Logfile contains a large record: \"%.64s\""
+ " (showing only the first 64 characters). Only the first 64 kB"
+ " will be analyzed, the rest will be ignored while Zabbix agent"
+ " is running", value);
+
+ lastlogsize1 = (size_t)offset + (size_t)nbytes;
+ send_err = SUCCEED;
+
+ if (SUCCEED == regexp_sub_ex(regexps, value, pattern, ZBX_CASE_SENSITIVE,
+ output_template, &item_value))
{
- ret = SUCCEED; /* EOF of the most current file is reached */
- break;
+ send_err = process_value(server, port, hostname, key, item_value,
+ &lastlogsize1, mtime, NULL, NULL, NULL, NULL, 1);
+
+ zbx_free(item_value);
+
+ if (SUCCEED == send_err)
+ (*s_count)--;
}
- else
+ (*p_count)--;
+
+ if (SUCCEED == send_err)
{
- zbx_free(logfile_candidate);
- *lastlogsize = 0;
- close(fd);
- continue; /* try to read from a more current file */
+ *lastlogsize = lastlogsize1;
+ *big_rec = 1; /* ignore the rest of this record */
}
+
+ if ('\0' != *encoding)
+ zbx_free(value);
+ }
+ else
+ {
+ /* It is a middle part of a long record. Ignore it. We have already */
+ /* checked the first part against the regexp. */
+
+ *lastlogsize = (size_t)offset + (size_t)nbytes;
}
- }
- else /* cannot read from the file */
- {
- zabbix_log(LOG_LEVEL_WARNING, "cannot read from '%s': %s", logfile_candidate,
- zbx_strerror(errno));
- break; /* must return, situation could have changed */
}
}
- else /* cannot position in the file */
+ else
{
- zabbix_log(LOG_LEVEL_WARNING, "cannot set position to " ZBX_FS_UI64 " for file '%s': %s",
- *lastlogsize, logfile_candidate, zbx_strerror(errno));
- break; /* must return, situation could have changed */
- }
- } /* trying to read from logfiles */
+ /* the "newline" was found, so there is at least one complete record */
+ /* (or trailing part of a large record) in the buffer */
- if (0 == logfiles_num)
- zabbix_log(LOG_LEVEL_WARNING, "there are no files matching '%s' in '%s'", format, directory);
+ for (;;)
+ {
+ if (0 >= *p_count || 0 >= *s_count)
+ {
+ /* limit on number of processed or sent-to-server lines reached */
+ ret = SUCCEED;
+ goto out;
+ }
- free_logfiles(&logfiles, &logfiles_alloc, &logfiles_num);
- if (0 < fd && -1 == close(fd))
- zabbix_log(LOG_LEVEL_WARNING, "cannot close file '%s': %s", logfile_candidate, zbx_strerror(errno));
+ if (0 == *big_rec)
+ {
+ char *value = NULL;
-#ifdef _WINDOWS
- if (0 != find_handle && -1 == _findclose(find_handle))
- zabbix_log(LOG_LEVEL_WARNING, "cannot close the find directory handle: %s", zbx_strerror(errno));
-#else
- if (NULL != dir && -1 == closedir(dir))
- zabbix_log(LOG_LEVEL_WARNING, "cannot close directory '%s': %s", directory, zbx_strerror(errno));
-#endif
+ *p_nl = '\0';
- zbx_free(logfile_candidate);
- zbx_free(directory);
- zbx_free(format);
-out:
- zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __function_name, zbx_result_string(ret));
+ if ('\0' != *encoding)
+ value = convert_to_utf8(p_start, (size_t)(p_nl - p_start), encoding);
+ else
+ value = p_start;
+
+ lastlogsize1 = (size_t)offset + (size_t)(p_next - buf);
+ send_err = SUCCEED;
+
+ if (SUCCEED == regexp_sub_ex(regexps, value, pattern, ZBX_CASE_SENSITIVE,
+ output_template, &item_value))
+ {
+ send_err = process_value(server, port, hostname, key, item_value,
+ &lastlogsize1, mtime, NULL, NULL, NULL, NULL, 1);
+
+ zbx_free(item_value);
+
+ if (SUCCEED == send_err)
+ (*s_count)--;
+ }
+ (*p_count)--;
+
+ if (SUCCEED == send_err)
+ *lastlogsize = lastlogsize1;
+
+ if ('\0' != *encoding)
+ zbx_free(value);
+ }
+ else
+ {
+ /* skip the trailing part of a long record */
+ *lastlogsize = (size_t)offset + (size_t)(p_next - buf);
+ *big_rec = 0;
+ }
+
+ /* move to the next record in the buffer */
+ p_start = p_next;
+ p = p_next;
+ if (NULL == (p_nl = buf_find_newline(p, &p_next, p_end, cr, lf, szbyte)))
+ {
+ /* There are no complete records in the buffer. */
+ /* Try to read more data from this position if available. */
+ if ((zbx_offset_t)-1 == zbx_lseek(fd, *lastlogsize, SEEK_SET))
+ {
+ ret = FAIL;
+ goto out;
+ }
+ else
+ break;
+ }
+ }
+ }
+ }
+out:
return ret;
+
+#undef BUF_SIZE
}
/******************************************************************************
* *
* Function: process_log *
* *
- * Purpose: Get message from logfile WITHOUT rotation *
- * *
- * Parameters: filename - logfile name *
- * lastlogsize - offset for message *
- * value - pointer for logged message *
+ * Purpose: Match new records in logfile with regexp, transmit matching *
+ * records to Zabbix server *
+ * *
+ * Parameters: *
+ * filename - [IN] logfile name *
+ * lastlogsize - [IN/OUT] offset from the beginning of the file *
+ * mtime - [IN] file modification time for reporting to server *
+ * skip_old_data - [IN/OUT] start from the beginning of the file or *
+ * jump to the end *
+ * big_rec - [IN/OUT] state variable to remember whether a long *
+ * record is being processed *
+ * encoding - [IN] text string describing encoding. *
+ * The following encodings are recognized: *
+ * "UNICODE" *
+ * "UNICODEBIG" *
+ * "UNICODEFFFE" *
+ * "UNICODELITTLE" *
+ * "UTF-16" "UTF16" *
+ * "UTF-16BE" "UTF16BE" *
+ * "UTF-16LE" "UTF16LE" *
+ * "UTF-32" "UTF32" *
+ * "UTF-32BE" "UTF32BE" *
+ * "UTF-32LE" "UTF32LE". *
+ * "" (empty string) means a single-byte character *
+ * set (e.g. ASCII). *
+ * regexps - [IN] array of regexps *
+ * pattern - [IN] pattern to match *
+ * output_template - [IN] output formatting template *
+ * p_count - [IN/OUT] limit of records to be processed *
+ * s_count - [IN/OUT] limit of records to be sent to server *
+ * process_value - [IN] pointer to function process_value() *
+ * server - [IN] server to send data to *
+ * port - [IN] port to send data to *
+ * hostname - [IN] hostname the data comes from *
+ * key - [IN] item key the data belongs to *
* *
* Return value: returns SUCCEED on successful reading, *
* FAIL on other cases *
* *
* Author: Eugene Grigorjev *
* *
- * Comments: This function allocates memory for 'value', so use zbx_free(). *
- * Return SUCCEED and NULL 'value' if end of file received. *
+ * Comments: *
+ * This function does not deal with log file rotation. *
* *
******************************************************************************/
-int process_log(char *filename, zbx_uint64_t *lastlogsize, char **value, const char *encoding,
- unsigned char skip_old_data)
+int process_log(char *filename, zbx_uint64_t *lastlogsize, int *mtime, unsigned char *skip_old_data, int *big_rec,
+ const char *encoding, zbx_vector_ptr_t *regexps, const char *pattern, const char *output_template,
+ int *p_count, int *s_count, zbx_process_value_func_t process_value, const char *server,
+ unsigned short port, const char *hostname, const char *key)
{
const char *__function_name = "process_log";
- int f;
+ int f, ret = FAIL;
struct stat buf;
- int nbytes, ret = FAIL;
- char buffer[MAX_BUFFER_LEN];
-
- assert(NULL != filename);
- assert(NULL != lastlogsize);
- assert(NULL != value);
- assert(NULL != encoding);
+ zbx_uint64_t l_size;
- zabbix_log(LOG_LEVEL_DEBUG, "In %s() filename:'%s' lastlogsize:" ZBX_FS_UI64,
- __function_name, filename, *lastlogsize);
+ zabbix_log(LOG_LEVEL_DEBUG, "In %s() filename:'%s' lastlogsize:" ZBX_FS_UI64 " mtime: %d",
+ __function_name, filename, *lastlogsize, NULL != mtime ? *mtime : 0);
- /* handling of file shrinking */
if (0 != zbx_stat(filename, &buf))
{
zabbix_log(LOG_LEVEL_WARNING, "cannot stat '%s': %s", filename, zbx_strerror(errno));
- return ret;
+ goto out;
}
- if (1 == skip_old_data)
+ if ((zbx_uint64_t)buf.st_size == *lastlogsize)
{
- *lastlogsize = (zbx_uint64_t)buf.st_size;
- zabbix_log(LOG_LEVEL_DEBUG, "skipping existing filename:'%s' lastlogsize:" ZBX_FS_UI64,
- filename, *lastlogsize);
+ /* The file size has not changed. Nothing to do. Here we do not deal with a case of changing */
+ /* a logfile's content while keeping the same length. */
+ ret = SUCCEED;
+ goto out;
}
- if (buf.st_size < *lastlogsize)
- *lastlogsize = 0;
-
if (-1 == (f = zbx_open(filename, O_RDONLY)))
{
zabbix_log(LOG_LEVEL_WARNING, "cannot open '%s': %s", filename, zbx_strerror(errno));
- return ret;
+ goto out;
}
-#ifdef _WINDOWS
- if (-1L != _lseeki64(f, (__int64)*lastlogsize, SEEK_SET))
-#else
- if ((off_t)-1 != lseek(f, (off_t)*lastlogsize, SEEK_SET))
-#endif
+ l_size = *lastlogsize;
+
+ if (1 == *skip_old_data)
{
- if (-1 != (nbytes = zbx_read(f, buffer, sizeof(buffer), encoding)))
- {
- if (0 != nbytes)
- {
- *lastlogsize += nbytes;
- *value = convert_to_utf8(buffer, nbytes, encoding);
- zbx_rtrim(*value, "\r\n ");
- }
- ret = SUCCEED;
- }
- else
- zabbix_log(LOG_LEVEL_WARNING, "cannot read from '%s': %s", filename, zbx_strerror(errno));
+ l_size = (zbx_uint64_t)buf.st_size;
+ zabbix_log(LOG_LEVEL_DEBUG, "skipping old data in filename:'%s' to lastlogsize:" ZBX_FS_UI64,
+ filename, l_size);
+ }
+
+ if ((zbx_uint64_t)buf.st_size < l_size) /* handle file truncation */
+ l_size = 0;
+
+ if ((zbx_offset_t)-1 != zbx_lseek(f, l_size, SEEK_SET))
+ {
+ *lastlogsize = l_size;
+ *skip_old_data = 0;
+
+ if (NULL != mtime)
+ *mtime = (int)buf.st_mtime;
+
+ ret = zbx_read2(f, lastlogsize, mtime, big_rec, encoding, regexps, pattern, output_template, p_count,
+ s_count, process_value, server, port, hostname, key);
}
else
+ {
zabbix_log(LOG_LEVEL_WARNING, "cannot set position to " ZBX_FS_UI64 " for '%s': %s",
- *lastlogsize, filename, zbx_strerror(errno));
+ l_size, filename, zbx_strerror(errno));
+ }
- close(f);
+ if (0 != close(f))
+ zabbix_log(LOG_LEVEL_WARNING, "cannot close file '%s': %s", filename, zbx_strerror(errno));
+out:
+ zabbix_log(LOG_LEVEL_DEBUG, "End of %s() filename:'%s' lastlogsize:" ZBX_FS_UI64 " mtime: %d ret:%s",
+ __function_name, filename, *lastlogsize, NULL != mtime ? *mtime : 0, zbx_result_string(ret));
return ret;
}
diff --git a/src/zabbix_agent/logfiles.h b/src/zabbix_agent/logfiles.h
index 674fa3c750a..b28dec98fdb 100644
--- a/src/zabbix_agent/logfiles.h
+++ b/src/zabbix_agent/logfiles.h
@@ -20,9 +20,16 @@
#ifndef ZABBIX_LOGFILES_H
#define ZABBIX_LOGFILES_H
-int process_log(char *filename, zbx_uint64_t *lastlogsize, char **value, const char *encoding,
- unsigned char skip_old_data);
-int process_logrt(char *fileformat, zbx_uint64_t *lastlogsize, int *mtime, char **value, const char *encoding,
- unsigned char skip_old_data);
+#include "zbxregexp.h"
+
+int process_log(char *filename, zbx_uint64_t *lastlogsize, int *mtime, unsigned char *skip_old_data, int *big_rec,
+ const char *encoding, zbx_vector_ptr_t *regexps, const char *pattern, const char *output_template,
+ int *p_count, int *s_count, zbx_process_value_func_t process_value, const char *server,
+ unsigned short port, const char *hostname, const char *key);
+
+int process_logrt(char *filename, zbx_uint64_t *lastlogsize, int *mtime, unsigned char *skip_old_data,
+ int *big_rec, const char *encoding, zbx_vector_ptr_t *regexps, const char *pattern,
+ const char *output_template, int *p_count, int *s_count, zbx_process_value_func_t process_value,
+ const char *server, unsigned short port, const char *hostname, const char *key);
#endif