diff options
-rw-r--r-- | build/win32/project/Makefile_get.inc | 4 | ||||
-rw-r--r-- | build/win32/project/Makefile_sender.inc | 4 | ||||
-rw-r--r-- | include/common.h | 5 | ||||
-rw-r--r-- | include/zbxtypes.h | 6 | ||||
-rw-r--r-- | src/libs/zbxcommon/file.c | 139 | ||||
-rw-r--r-- | src/libs/zbxregexp/zbxregexp.c | 74 | ||||
-rw-r--r-- | src/zabbix_agent/active.c | 165 | ||||
-rw-r--r-- | src/zabbix_agent/active.h | 1 | ||||
-rw-r--r-- | src/zabbix_agent/logfiles.c | 627 | ||||
-rw-r--r-- | src/zabbix_agent/logfiles.h | 15 |
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(®exps, 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, + ®exps, 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(®exps, 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, + ®exps, 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 |