1 /*
2 ** Zabbix
3 ** Copyright (C) 2001-2021 Zabbix SIA
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
14 **
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 **/
19
20 #include "common.h"
21 #include "log.h"
22 #include "logfiles/logfiles.h"
23 #include "sysinfo.h"
24 #include "zbxregexp.h"
25 #include "winmeta.h"
26 #include <strsafe.h>
27 #include "eventlog.h"
28 #include <delayimp.h>
29 #include <sddl.h>
30
31 #define MAX_NAME 256
32
33 static const wchar_t *RENDER_ITEMS[] = {
34 L"/Event/System/Provider/@Name",
35 L"/Event/System/Provider/@EventSourceName",
36 L"/Event/System/EventRecordID",
37 L"/Event/System/EventID",
38 L"/Event/System/Level",
39 L"/Event/System/Keywords",
40 L"/Event/System/TimeCreated/@SystemTime",
41 L"/Event/EventData/Data"
42 };
43
44 #define RENDER_ITEMS_COUNT (sizeof(RENDER_ITEMS) / sizeof(const wchar_t *))
45
46 #define VAR_PROVIDER_NAME(p) (p[0].StringVal)
47 #define VAR_SOURCE_NAME(p) (p[1].StringVal)
48 #define VAR_RECORD_NUMBER(p) (p[2].UInt64Val)
49 #define VAR_EVENT_ID(p) (p[3].UInt16Val)
50 #define VAR_LEVEL(p) (p[4].ByteVal)
51 #define VAR_KEYWORDS(p) (p[5].UInt64Val)
52 #define VAR_TIME_CREATED(p) (p[6].FileTimeVal)
53 #define VAR_EVENT_DATA_STRING(p) (p[7].StringVal)
54 #define VAR_EVENT_DATA_STRING_ARRAY(p, i) (p[7].StringArr[i])
55 #define VAR_EVENT_DATA_TYPE(p) (p[7].Type)
56 #define VAR_EVENT_DATA_COUNT(p) (p[7].Count)
57
58 #define EVENTLOG_REG_PATH TEXT("SYSTEM\\CurrentControlSet\\Services\\EventLog\\")
59
60 #ifndef INFORMATION_TYPE
61 # define INFORMATION_TYPE "Information"
62 #endif
63 #ifndef WARNING_TYPE
64 # define WARNING_TYPE "Warning"
65 #endif
66 #ifndef ERROR_TYPE
67 # define ERROR_TYPE "Error"
68 #endif
69 #ifndef AUDIT_FAILURE
70 # define AUDIT_FAILURE "Failure Audit"
71 #endif
72 #ifndef AUDIT_SUCCESS
73 # define AUDIT_SUCCESS "Success Audit"
74 #endif
75 #ifndef CRITICAL_TYPE
76 # define CRITICAL_TYPE "Critical"
77 #endif
78 #ifndef VERBOSE_TYPE
79 # define VERBOSE_TYPE "Verbose"
80 #endif
81
82 extern int CONFIG_EVENTLOG_MAX_LINES_PER_SECOND;
83
DelayLoadDllExceptionFilter(PEXCEPTION_POINTERS excpointers)84 LONG WINAPI DelayLoadDllExceptionFilter(PEXCEPTION_POINTERS excpointers)
85 {
86 LONG disposition = EXCEPTION_EXECUTE_HANDLER;
87 PDelayLoadInfo delayloadinfo = (PDelayLoadInfo)(excpointers->ExceptionRecord->ExceptionInformation[0]);
88
89 switch (excpointers->ExceptionRecord->ExceptionCode)
90 {
91 case VcppException(ERROR_SEVERITY_ERROR, ERROR_MOD_NOT_FOUND):
92 zabbix_log(LOG_LEVEL_DEBUG, "function %s was not found in %s",
93 delayloadinfo->dlp.szProcName, delayloadinfo->szDll);
94 break;
95 case VcppException(ERROR_SEVERITY_ERROR, ERROR_PROC_NOT_FOUND):
96 if (delayloadinfo->dlp.fImportByName)
97 {
98 zabbix_log(LOG_LEVEL_DEBUG, "function %s was not found in %s",
99 delayloadinfo->dlp.szProcName, delayloadinfo->szDll);
100 }
101 else
102 {
103 zabbix_log(LOG_LEVEL_DEBUG, "function ordinal %d was not found in %s",
104 delayloadinfo->dlp.dwOrdinal, delayloadinfo->szDll);
105 }
106 break;
107 default:
108 disposition = EXCEPTION_CONTINUE_SEARCH;
109 break;
110 }
111
112 return disposition;
113 }
114
115 /* open event logger and return number of records */
zbx_open_eventlog(LPCTSTR wsource,HANDLE * eventlog_handle,zbx_uint64_t * FirstID,zbx_uint64_t * LastID,DWORD * error_code)116 static int zbx_open_eventlog(LPCTSTR wsource, HANDLE *eventlog_handle, zbx_uint64_t *FirstID,
117 zbx_uint64_t *LastID, DWORD *error_code)
118 {
119 wchar_t reg_path[MAX_PATH];
120 HKEY hk = NULL;
121 DWORD dwNumRecords, dwOldestRecord;
122 int ret = FAIL;
123
124 zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
125
126 *eventlog_handle = NULL;
127
128 /* Get path to eventlog */
129 StringCchPrintf(reg_path, MAX_PATH, EVENTLOG_REG_PATH TEXT("%s"), wsource);
130
131 if (ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE, reg_path, 0, KEY_READ, &hk))
132 {
133 *error_code = GetLastError();
134 goto out;
135 }
136
137 RegCloseKey(hk);
138
139 if (NULL == (*eventlog_handle = OpenEventLog(NULL, wsource))) /* open log file */
140 {
141 *error_code = GetLastError();
142 goto out;
143 }
144
145 if (0 == GetNumberOfEventLogRecords(*eventlog_handle, &dwNumRecords) ||
146 0 == GetOldestEventLogRecord(*eventlog_handle, &dwOldestRecord))
147 {
148 *error_code = GetLastError();
149 CloseEventLog(*eventlog_handle);
150 *eventlog_handle = NULL;
151 goto out;
152 }
153
154 *FirstID = dwOldestRecord;
155 *LastID = dwOldestRecord + dwNumRecords - 1;
156
157 zabbix_log(LOG_LEVEL_DEBUG, "FirstID:" ZBX_FS_UI64 " LastID:" ZBX_FS_UI64 " numIDs:%lu",
158 *FirstID, *LastID, dwNumRecords);
159
160 ret = SUCCEED;
161 out:
162 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
163
164 return ret;
165 }
166
167 /* close event logger */
zbx_close_eventlog(HANDLE eventlog_handle)168 static void zbx_close_eventlog(HANDLE eventlog_handle)
169 {
170 if (NULL != eventlog_handle)
171 CloseEventLog(eventlog_handle);
172 }
173
174 /******************************************************************************
175 * *
176 * Function: zbx_get_message_files *
177 * *
178 * Purpose: gets event message and parameter translation files from registry *
179 * *
180 * Parameters: szLogName - [IN] the log name *
181 * szSourceName - [IN] the log source name *
182 * pEventMessageFile - [OUT] the event message file *
183 * pParamMessageFile - [OUT] the parameter message file *
184 * *
185 ******************************************************************************/
zbx_get_message_files(const wchar_t * szLogName,const wchar_t * szSourceName,wchar_t ** pEventMessageFile,wchar_t ** pParamMessageFile)186 static void zbx_get_message_files(const wchar_t *szLogName, const wchar_t *szSourceName, wchar_t **pEventMessageFile,
187 wchar_t **pParamMessageFile)
188 {
189 wchar_t buf[MAX_PATH];
190 HKEY hKey = NULL;
191 DWORD szData;
192
193 /* Get path to message dll */
194 StringCchPrintf(buf, MAX_PATH, EVENTLOG_REG_PATH TEXT("%s\\%s"), szLogName, szSourceName);
195
196 if (ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE, buf, 0, KEY_READ, &hKey))
197 return;
198
199 if (ERROR_SUCCESS == RegQueryValueEx(hKey, TEXT("EventMessageFile"), NULL, NULL, NULL, &szData))
200 {
201 *pEventMessageFile = zbx_malloc(*pEventMessageFile, szData);
202 if (ERROR_SUCCESS != RegQueryValueEx(hKey, TEXT("EventMessageFile"), NULL, NULL,
203 (unsigned char *)*pEventMessageFile, &szData))
204 {
205 zbx_free(*pEventMessageFile);
206 }
207 }
208
209 if (ERROR_SUCCESS == RegQueryValueEx(hKey, TEXT("ParameterMessageFile"), NULL, NULL, NULL, &szData))
210 {
211 *pParamMessageFile = zbx_malloc(*pParamMessageFile, szData);
212 if (ERROR_SUCCESS != RegQueryValueEx(hKey, TEXT("ParameterMessageFile"), NULL, NULL,
213 (unsigned char *)*pParamMessageFile, &szData))
214 {
215 zbx_free(*pParamMessageFile);
216 }
217 }
218
219 RegCloseKey(hKey);
220 }
221
222 /******************************************************************************
223 * *
224 * Function: zbx_load_message_file *
225 * *
226 * Purpose: load the specified message file, expanding environment variables *
227 * in the file name if necessary *
228 * *
229 * Parameters: szFileName - [IN] the message file name *
230 * *
231 * Return value: Handle to the loaded library or NULL otherwise *
232 * *
233 ******************************************************************************/
zbx_load_message_file(const wchar_t * szFileName)234 static HINSTANCE zbx_load_message_file(const wchar_t *szFileName)
235 {
236 wchar_t *dll_name = NULL;
237 long int sz, len = 0;
238 HINSTANCE res = NULL;
239
240 if (NULL == szFileName)
241 return NULL;
242
243 do
244 {
245 if (0 != (sz = len))
246 dll_name = zbx_realloc(dll_name, sz * sizeof(wchar_t));
247
248 len = ExpandEnvironmentStrings(szFileName, dll_name, sz);
249 }
250 while (0 != len && sz < len);
251
252 if (0 != len)
253 res = LoadLibraryEx(dll_name, NULL, LOAD_LIBRARY_AS_DATAFILE);
254
255 zbx_free(dll_name);
256
257 return res;
258 }
259
260 /******************************************************************************
261 * *
262 * Function: zbx_format_message *
263 * *
264 * Purpose: extracts the specified message from a message file *
265 * *
266 * Parameters: hLib - [IN] the message file handle *
267 * dwMessageId - [IN] the message identifier *
268 * pInsertStrings - [IN] a list of insert strings, optional *
269 * *
270 * Return value: The formatted message converted to utf8 or NULL *
271 * *
272 * Comments: This function allocates memory for the returned message, which *
273 * must be freed by the caller later. *
274 * *
275 ******************************************************************************/
zbx_format_message(HINSTANCE hLib,DWORD dwMessageId,wchar_t ** pInsertStrings)276 static char *zbx_format_message(HINSTANCE hLib, DWORD dwMessageId, wchar_t **pInsertStrings)
277 {
278 wchar_t *pMsgBuf = NULL;
279 char *message;
280
281 if (0 == FormatMessage(FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ALLOCATE_BUFFER |
282 FORMAT_MESSAGE_ARGUMENT_ARRAY | FORMAT_MESSAGE_MAX_WIDTH_MASK,
283 hLib, dwMessageId, MAKELANGID(LANG_NEUTRAL, SUBLANG_ENGLISH_US), (wchar_t *)&pMsgBuf, 0,
284 (va_list *)pInsertStrings))
285 {
286 return NULL;
287 }
288
289 message = zbx_unicode_to_utf8(pMsgBuf);
290 zbx_rtrim(message, "\r\n ");
291
292 LocalFree((HLOCAL)pMsgBuf);
293
294 return message;
295 }
296
297 /******************************************************************************
298 * *
299 * Function: zbx_translate_message_params *
300 * *
301 * Purpose: translates message by replacing parameters %%<id> with translated *
302 * values *
303 * *
304 * Parameters: message - [IN/OUT] the message to translate *
305 * hLib - [IN] the parameter message file handle *
306 * *
307 ******************************************************************************/
zbx_translate_message_params(char ** message,HINSTANCE hLib)308 static void zbx_translate_message_params(char **message, HINSTANCE hLib)
309 {
310 char *param, *pstart, *pend;
311 int dwMessageId;
312 size_t offset = 0;
313
314 while (1)
315 {
316 if (NULL == (pstart = strstr(*message + offset, "%%")))
317 break;
318
319 pend = pstart + 2;
320
321 dwMessageId = atoi(pend);
322
323 while ('\0' != *pend && 0 != isdigit(*pend))
324 pend++;
325
326 offset = pend - *message - 1;
327
328 if (NULL != (param = zbx_format_message(hLib, dwMessageId, NULL)))
329 {
330 zbx_replace_string(message, pstart - *message, &offset, param);
331
332 zbx_free(param);
333 }
334 }
335 }
336
get_eventlog6_id(EVT_HANDLE * event_query,EVT_HANDLE * render_context,zbx_uint64_t * id,char ** error)337 static int get_eventlog6_id(EVT_HANDLE *event_query, EVT_HANDLE *render_context, zbx_uint64_t *id, char **error)
338 {
339 int ret = FAIL;
340 DWORD size_required_next = 0, size_required = 0, size = 0, status = 0, bookmarkedCount = 0;
341 EVT_VARIANT* renderedContent = NULL;
342 EVT_HANDLE event_bookmark = NULL;
343
344 zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
345
346 if (TRUE != EvtNext(*event_query, 1, &event_bookmark, INFINITE, 0, &size_required_next))
347 {
348 /* no data in eventlog */
349 zabbix_log(LOG_LEVEL_DEBUG, "%s() EvtNext failed:%s", __func__, strerror_from_system(GetLastError()));
350 *id = 0;
351 ret = SUCCEED;
352 goto out;
353 }
354
355 /* obtain the information from selected events */
356 if (TRUE != EvtRender(*render_context, event_bookmark, EvtRenderEventValues, size, renderedContent,
357 &size_required, &bookmarkedCount))
358 {
359 /* information exceeds the allocated space */
360 if (ERROR_INSUFFICIENT_BUFFER != (status = GetLastError()))
361 {
362 *error = zbx_dsprintf(*error, "EvtRender failed:%s", strerror_from_system(status));
363 goto out;
364 }
365
366 size = size_required;
367 renderedContent = (EVT_VARIANT*)zbx_malloc(NULL, size);
368
369 if (TRUE != EvtRender(*render_context, event_bookmark, EvtRenderEventValues, size, renderedContent,
370 &size_required, &bookmarkedCount))
371 {
372 *error = zbx_dsprintf(*error, "EvtRender failed:%s", strerror_from_system(GetLastError()));
373 goto out;
374 }
375 }
376
377 *id = VAR_RECORD_NUMBER(renderedContent);
378 ret = SUCCEED;
379 out:
380 if (NULL != event_bookmark)
381 EvtClose(event_bookmark);
382
383 zbx_free(renderedContent);
384
385 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s id:" ZBX_FS_UI64, __func__, zbx_result_string(ret), id);
386
387 return ret;
388 }
389
390 /* open eventlog using API 6 and return the number of records */
zbx_open_eventlog6(const wchar_t * wsource,zbx_uint64_t * lastlogsize,EVT_HANDLE * render_context,zbx_uint64_t * FirstID,zbx_uint64_t * LastID,char ** error)391 static int zbx_open_eventlog6(const wchar_t *wsource, zbx_uint64_t *lastlogsize, EVT_HANDLE *render_context,
392 zbx_uint64_t *FirstID, zbx_uint64_t *LastID, char **error)
393 {
394 EVT_HANDLE tmp_first_event_query = NULL;
395 EVT_HANDLE tmp_last_event_query = NULL;
396 DWORD status = 0;
397 int ret = FAIL;
398
399 zabbix_log(LOG_LEVEL_DEBUG, "In %s() lastlogsize:" ZBX_FS_UI64, __func__, *lastlogsize);
400
401 *FirstID = 0;
402 *LastID = 0;
403
404 /* get the number of the oldest record in the log */
405 /* "EvtGetLogInfo()" does not work properly with "EvtLogOldestRecordNumber" */
406 /* we have to get it from the first EventRecordID */
407
408 /* create the system render */
409 if (NULL == (*render_context = EvtCreateRenderContext(RENDER_ITEMS_COUNT, RENDER_ITEMS, EvtRenderContextValues)))
410 {
411 *error = zbx_dsprintf(*error, "EvtCreateRenderContext failed:%s", strerror_from_system(GetLastError()));
412 goto out;
413 }
414
415 /* get all eventlog */
416 if (NULL == (tmp_first_event_query = EvtQuery(NULL, wsource, NULL, EvtQueryChannelPath)))
417 {
418 if (ERROR_EVT_CHANNEL_NOT_FOUND == (status = GetLastError()))
419 *error = zbx_dsprintf(*error, "EvtQuery channel missed:%s", strerror_from_system(status));
420 else
421 *error = zbx_dsprintf(*error, "EvtQuery failed:%s", strerror_from_system(status));
422
423 goto out;
424 }
425
426 if (SUCCEED != get_eventlog6_id(&tmp_first_event_query, render_context, FirstID, error))
427 goto out;
428
429 if (0 == *FirstID)
430 {
431 /* no data in eventlog */
432 zabbix_log(LOG_LEVEL_DEBUG, "%s() first EvtNext failed", __func__);
433 *FirstID = 1;
434 *LastID = 1;
435 *lastlogsize = 0;
436 ret = SUCCEED;
437 goto out;
438 }
439
440 if (NULL == (tmp_last_event_query = EvtQuery(NULL, wsource, NULL,
441 EvtQueryChannelPath | EvtQueryReverseDirection)))
442 {
443 if (ERROR_EVT_CHANNEL_NOT_FOUND == (status = GetLastError()))
444 *error = zbx_dsprintf(*error, "EvtQuery channel missed:%s", strerror_from_system(status));
445 else
446 *error = zbx_dsprintf(*error, "EvtQuery failed:%s", strerror_from_system(status));
447
448 goto out;
449 }
450
451 if (SUCCEED != get_eventlog6_id(&tmp_last_event_query, render_context, LastID, error) || 0 == *LastID)
452 {
453 /* no data in eventlog */
454 zabbix_log(LOG_LEVEL_DEBUG, "%s() last EvtNext failed", __func__);
455 *LastID = 1;
456 }
457 else
458 *LastID += 1; /* we should read the last record */
459
460 if (*lastlogsize >= *LastID)
461 {
462 *lastlogsize = *FirstID - 1;
463 zabbix_log(LOG_LEVEL_WARNING, "lastlogsize is too big. It is set to:" ZBX_FS_UI64, *lastlogsize);
464 }
465
466 ret = SUCCEED;
467 out:
468 if (NULL != tmp_first_event_query)
469 EvtClose(tmp_first_event_query);
470 if (NULL != tmp_last_event_query)
471 EvtClose(tmp_last_event_query);
472
473 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s FirstID:" ZBX_FS_UI64 " LastID:" ZBX_FS_UI64,
474 __func__, zbx_result_string(ret), *FirstID, *LastID);
475
476 return ret;
477 }
478
479 /* get handles of eventlog */
zbx_get_handle_eventlog6(const wchar_t * wsource,zbx_uint64_t * lastlogsize,EVT_HANDLE * query,char ** error)480 static int zbx_get_handle_eventlog6(const wchar_t *wsource, zbx_uint64_t *lastlogsize, EVT_HANDLE *query,
481 char **error)
482 {
483 wchar_t *event_query = NULL;
484 char *tmp_str = NULL;
485 int ret = FAIL;
486
487 zabbix_log(LOG_LEVEL_DEBUG, "In %s(), previous lastlogsize:" ZBX_FS_UI64, __func__, *lastlogsize);
488
489 /* start building the query */
490 tmp_str = zbx_dsprintf(NULL, "Event/System[EventRecordID>" ZBX_FS_UI64 "]", *lastlogsize);
491 event_query = zbx_utf8_to_unicode(tmp_str);
492
493 /* create massive query for an event on a local computer*/
494 *query = EvtQuery(NULL, wsource, event_query, EvtQueryChannelPath);
495 if (NULL == *query)
496 {
497 DWORD status;
498
499 if (ERROR_EVT_CHANNEL_NOT_FOUND == (status = GetLastError()))
500 *error = zbx_dsprintf(*error, "EvtQuery channel missed:%s", strerror_from_system(status));
501 else
502 *error = zbx_dsprintf(*error, "EvtQuery failed:%s", strerror_from_system(status));
503
504 goto out;
505 }
506
507 ret = SUCCEED;
508 out:
509 zbx_free(tmp_str);
510 zbx_free(event_query);
511 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
512
513 return ret;
514 }
515
516 /* initialize event logs with Windows API version 6 */
initialize_eventlog6(const char * source,zbx_uint64_t * lastlogsize,zbx_uint64_t * FirstID,zbx_uint64_t * LastID,EVT_HANDLE * render_context,EVT_HANDLE * query,char ** error)517 int initialize_eventlog6(const char *source, zbx_uint64_t *lastlogsize, zbx_uint64_t *FirstID,
518 zbx_uint64_t *LastID, EVT_HANDLE *render_context, EVT_HANDLE *query, char **error)
519 {
520 wchar_t *wsource = NULL;
521 int ret = FAIL;
522
523 zabbix_log(LOG_LEVEL_DEBUG, "In %s() source:'%s' previous lastlogsize:" ZBX_FS_UI64,
524 __func__, source, *lastlogsize);
525
526 if (NULL == source || '\0' == *source)
527 {
528 *error = zbx_dsprintf(*error, "cannot open eventlog with empty name.");
529 goto out;
530 }
531
532 wsource = zbx_utf8_to_unicode(source);
533
534 if (SUCCEED != zbx_open_eventlog6(wsource, lastlogsize, render_context, FirstID, LastID, error))
535 {
536 zabbix_log(LOG_LEVEL_ERR, "cannot open eventlog '%s'", source);
537 goto out;
538 }
539
540 if (SUCCEED != zbx_get_handle_eventlog6(wsource, lastlogsize, query, error))
541 {
542 zabbix_log(LOG_LEVEL_ERR, "cannot get eventlog handle '%s'", source);
543 goto out;
544 }
545
546 ret = SUCCEED;
547 out:
548 zbx_free(wsource);
549 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
550
551 return ret;
552 }
553
554 /* expand the string message from a specific event handler */
expand_message6(const wchar_t * pname,EVT_HANDLE event)555 static char *expand_message6(const wchar_t *pname, EVT_HANDLE event)
556 {
557 wchar_t *pmessage = NULL;
558 EVT_HANDLE provider = NULL;
559 DWORD require = 0;
560 char *out_message = NULL;
561 char *tmp_pname = NULL;
562
563 zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
564
565 if (NULL == (provider = EvtOpenPublisherMetadata(NULL, pname, NULL, 0, 0)))
566 {
567 tmp_pname = zbx_unicode_to_utf8(pname);
568 zabbix_log(LOG_LEVEL_DEBUG, "provider '%s' could not be opened: %s",
569 tmp_pname, strerror_from_system(GetLastError()));
570 zbx_free(tmp_pname);
571 goto out;
572 }
573
574 if (TRUE != EvtFormatMessage(provider, event, 0, 0, NULL, EvtFormatMessageEvent, 0, NULL, &require))
575 {
576 if (ERROR_INSUFFICIENT_BUFFER == GetLastError())
577 {
578 DWORD error = ERROR_SUCCESS;
579
580 pmessage = zbx_malloc(pmessage, sizeof(WCHAR) * require);
581
582 if (TRUE != EvtFormatMessage(provider, event, 0, 0, NULL, EvtFormatMessageEvent, require,
583 pmessage, &require))
584 {
585 error = GetLastError();
586 }
587
588 if (ERROR_SUCCESS == error || ERROR_EVT_UNRESOLVED_VALUE_INSERT == error ||
589 ERROR_EVT_UNRESOLVED_PARAMETER_INSERT == error ||
590 ERROR_EVT_MAX_INSERTS_REACHED == error)
591 {
592 out_message = zbx_unicode_to_utf8(pmessage);
593 }
594 else
595 {
596 zabbix_log(LOG_LEVEL_DEBUG, "%s() cannot format message: %s", __func__,
597 strerror_from_system(error));
598 goto out;
599 }
600 }
601 }
602 out:
603 if (NULL != provider)
604 EvtClose(provider);
605 zbx_free(pmessage);
606
607 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, out_message);
608
609 /* should be freed */
610 return out_message;
611 }
612
replace_sid_to_account(PSID sidVal,char ** out_message)613 static void replace_sid_to_account(PSID sidVal, char **out_message)
614 {
615 DWORD nlen = MAX_NAME, dlen = MAX_NAME;
616 wchar_t name[MAX_NAME], dom[MAX_NAME], *sid = NULL;
617 int iUse;
618 char userName[MAX_NAME * 4], domName[MAX_NAME * 4], sidName[MAX_NAME * 4], *tmp, buffer[MAX_NAME * 8];
619
620 if (0 == LookupAccountSid(NULL, sidVal, name, &nlen, dom, &dlen, (PSID_NAME_USE)&iUse))
621 {
622 /* don't replace security ID if no mapping between account names and security IDs was done */
623 zabbix_log(LOG_LEVEL_DEBUG, "LookupAccountSid failed:%s", strerror_from_system(GetLastError()));
624 return;
625 }
626
627 if (0 == nlen)
628 {
629 zabbix_log(LOG_LEVEL_DEBUG, "LookupAccountSid returned empty user name");
630 return;
631 }
632
633 if (0 == ConvertSidToStringSid(sidVal, &sid))
634 {
635 zabbix_log(LOG_LEVEL_DEBUG, "ConvertSidToStringSid failed:%s", strerror_from_system(GetLastError()));
636 return;
637 }
638
639 zbx_unicode_to_utf8_static(sid, sidName, sizeof(sidName));
640 zbx_unicode_to_utf8_static(name, userName, sizeof(userName));
641
642 if (0 != dlen)
643 {
644 zbx_unicode_to_utf8_static(dom, domName, sizeof(domName));
645 zbx_snprintf(buffer, sizeof(buffer), "%s\\%s", domName, userName);
646 }
647 else
648 zbx_strlcpy(buffer, userName, sizeof(buffer)); /* NULL SID */
649
650 tmp = *out_message;
651 *out_message = string_replace(*out_message, sidName, buffer);
652
653 LocalFree(sid);
654 zbx_free(tmp);
655 }
656
replace_sids_to_accounts(EVT_HANDLE event_bookmark,char ** out_message)657 static void replace_sids_to_accounts(EVT_HANDLE event_bookmark, char **out_message)
658 {
659 DWORD status, dwBufferSize = 0, dwBufferUsed = 0, dwPropertyCount = 0, i;
660 PEVT_VARIANT renderedContent = NULL;
661 EVT_HANDLE render_context;
662
663 if (NULL == (render_context = EvtCreateRenderContext(0, NULL, EvtRenderContextUser)))
664 {
665 zabbix_log(LOG_LEVEL_WARNING, "EvtCreateRenderContext failed:%s", strerror_from_system(GetLastError()));
666 goto cleanup;
667 }
668
669 if (TRUE != EvtRender(render_context, event_bookmark, EvtRenderEventValues, dwBufferSize, renderedContent,
670 &dwBufferUsed, &dwPropertyCount))
671 {
672 if (ERROR_INSUFFICIENT_BUFFER != (status = GetLastError()))
673 {
674 zabbix_log(LOG_LEVEL_WARNING, "EvtRender failed:%s", strerror_from_system(status));
675 goto cleanup;
676 }
677
678 dwBufferSize = dwBufferUsed;
679 renderedContent = (PEVT_VARIANT)zbx_malloc(NULL, dwBufferSize);
680
681 if (TRUE != EvtRender(render_context, event_bookmark, EvtRenderEventValues, dwBufferSize,
682 renderedContent, &dwBufferUsed, &dwPropertyCount))
683 {
684 zabbix_log(LOG_LEVEL_WARNING, "EvtRender failed:%s", strerror_from_system(GetLastError()));
685 goto cleanup;
686 }
687 }
688
689 for (i = 0; i < dwPropertyCount; i++)
690 {
691 if (EvtVarTypeSid == renderedContent[i].Type)
692 replace_sid_to_account(renderedContent[i].SidVal, out_message);
693 }
694 cleanup:
695 if (NULL != render_context)
696 EvtClose(render_context);
697
698 zbx_free(renderedContent);
699 }
700
701 /******************************************************************************
702 * *
703 * Function: zbx_parse_eventlog_message6 *
704 * *
705 * Purpose: details parse of a single EventLog record *
706 * *
707 * Parameters: wsource - [IN] EventLog file name *
708 * render_context - [IN] the handle to the rendering context *
709 * event_bookmark - [IN/OUT] the handle of Event record for parse *
710 * which - [IN/OUT] the position of the EventLog record *
711 * out_severity - [OUT] the ELR detail *
712 * out_timestamp - [OUT] the ELR detail *
713 * out_provider - [OUT] the ELR detail *
714 * out_source - [OUT] the ELR detail *
715 * out_message - [OUT] the ELR detail *
716 * out_eventid - [OUT] the ELR detail *
717 * out_keywords - [OUT] the ELR detail *
718 * error - [OUT] the error message in the case of *
719 * failure *
720 * *
721 * Return value: SUCCEED or FAIL *
722 * *
723 ******************************************************************************/
zbx_parse_eventlog_message6(const wchar_t * wsource,EVT_HANDLE * render_context,EVT_HANDLE * event_bookmark,zbx_uint64_t * which,unsigned short * out_severity,unsigned long * out_timestamp,char ** out_provider,char ** out_source,char ** out_message,unsigned long * out_eventid,zbx_uint64_t * out_keywords,char ** error)724 static int zbx_parse_eventlog_message6(const wchar_t *wsource, EVT_HANDLE *render_context,
725 EVT_HANDLE *event_bookmark, zbx_uint64_t *which, unsigned short *out_severity,
726 unsigned long *out_timestamp, char **out_provider, char **out_source, char **out_message,
727 unsigned long *out_eventid, zbx_uint64_t *out_keywords, char **error)
728 {
729 EVT_VARIANT* renderedContent = NULL;
730 const wchar_t *pprovider = NULL;
731 char *tmp_str = NULL;
732 DWORD size = 0, bookmarkedCount = 0, require = 0, error_code;
733 const zbx_uint64_t sec_1970 = 116444736000000000;
734 const zbx_uint64_t success_audit = 0x20000000000000;
735 const zbx_uint64_t failure_audit = 0x10000000000000;
736 int ret = FAIL;
737
738 zabbix_log(LOG_LEVEL_DEBUG, "In %s() EventRecordID:" ZBX_FS_UI64, __func__, *which);
739
740 /* obtain the information from the selected events */
741 if (TRUE != EvtRender(*render_context, *event_bookmark, EvtRenderEventValues, size, renderedContent,
742 &require, &bookmarkedCount))
743 {
744 /* information exceeds the space allocated */
745 if (ERROR_INSUFFICIENT_BUFFER != (error_code = GetLastError()))
746 {
747 *error = zbx_dsprintf(*error, "EvtRender failed: %s", strerror_from_system(error_code));
748 goto out;
749 }
750
751 size = require;
752 renderedContent = (EVT_VARIANT *)zbx_malloc(NULL, size);
753
754 if (TRUE != EvtRender(*render_context, *event_bookmark, EvtRenderEventValues, size, renderedContent,
755 &require, &bookmarkedCount))
756 {
757 *error = zbx_dsprintf(*error, "EvtRender failed: %s", strerror_from_system(GetLastError()));
758 goto out;
759 }
760 }
761
762 pprovider = VAR_PROVIDER_NAME(renderedContent);
763 *out_provider = zbx_unicode_to_utf8(pprovider);
764 *out_source = NULL;
765
766 if (NULL != VAR_SOURCE_NAME(renderedContent))
767 {
768 *out_source = zbx_unicode_to_utf8(VAR_SOURCE_NAME(renderedContent));
769 }
770
771 *out_keywords = VAR_KEYWORDS(renderedContent) & (success_audit | failure_audit);
772 *out_severity = VAR_LEVEL(renderedContent);
773 *out_timestamp = (unsigned long)((VAR_TIME_CREATED(renderedContent) - sec_1970) / 10000000);
774 *out_eventid = VAR_EVENT_ID(renderedContent);
775 *out_message = expand_message6(pprovider, *event_bookmark);
776
777 if (NULL != *out_message)
778 replace_sids_to_accounts(*event_bookmark, out_message);
779
780 tmp_str = zbx_unicode_to_utf8(wsource);
781
782 if (VAR_RECORD_NUMBER(renderedContent) != *which)
783 {
784 zabbix_log(LOG_LEVEL_DEBUG, "%s() Overwriting expected EventRecordID:" ZBX_FS_UI64 " with the real"
785 " EventRecordID:" ZBX_FS_UI64 " in eventlog '%s'", __func__, *which,
786 VAR_RECORD_NUMBER(renderedContent), tmp_str);
787 *which = VAR_RECORD_NUMBER(renderedContent);
788 }
789
790 /* some events don't have enough information for making event message */
791 if (NULL == *out_message)
792 {
793 *out_message = zbx_strdcatf(*out_message, "The description for Event ID:%lu in Source:'%s'"
794 " cannot be found. Either the component that raises this event is not installed"
795 " on your local computer or the installation is corrupted. You can install or repair"
796 " the component on the local computer. If the event originated on another computer,"
797 " the display information had to be saved with the event.", *out_eventid,
798 NULL == *out_provider ? "" : *out_provider);
799
800 if (EvtVarTypeString == (VAR_EVENT_DATA_TYPE(renderedContent) & EVT_VARIANT_TYPE_MASK))
801 {
802 unsigned int i;
803 char *data = NULL;
804
805 if (0 != (VAR_EVENT_DATA_TYPE(renderedContent) & EVT_VARIANT_TYPE_ARRAY) &&
806 0 < VAR_EVENT_DATA_COUNT(renderedContent))
807 {
808 *out_message = zbx_strdcatf(*out_message, " The following information was included"
809 " with the event: ");
810
811 for (i = 0; i < VAR_EVENT_DATA_COUNT(renderedContent); i++)
812 {
813 if (NULL != VAR_EVENT_DATA_STRING_ARRAY(renderedContent, i))
814 {
815 if (0 < i)
816 *out_message = zbx_strdcat(*out_message, "; ");
817
818 data = zbx_unicode_to_utf8(VAR_EVENT_DATA_STRING_ARRAY(renderedContent,
819 i));
820 *out_message = zbx_strdcatf(*out_message, "%s", data);
821 zbx_free(data);
822 }
823 }
824 }
825 else if (NULL != VAR_EVENT_DATA_STRING(renderedContent))
826 {
827 data = zbx_unicode_to_utf8(VAR_EVENT_DATA_STRING(renderedContent));
828 *out_message = zbx_strdcatf(*out_message, "The following information was included"
829 " with the event: %s", data);
830 zbx_free(data);
831 }
832 }
833 }
834
835 ret = SUCCEED;
836 out:
837 EvtClose(*event_bookmark);
838 *event_bookmark = NULL;
839
840 zbx_free(tmp_str);
841 zbx_free(renderedContent);
842 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
843
844 return ret;
845 }
846
847 /******************************************************************************
848 * *
849 * Function: process_eventslog6 *
850 * *
851 * Purpose: batch processing of Event Log file *
852 * *
853 * Parameters: server - [IN] IP or Hostname of Zabbix server *
854 * port - [IN] port of Zabbix server *
855 * eventlog_name - [IN] the name of the event log *
856 * render_context - [IN] the handle to the rendering context *
857 * query - [IN] the handle to the query results *
858 * lastlogsize - [IN] position of the last processed record *
859 * FirstID - [IN] first record in the EventLog file *
860 * LastID - [IN] last record in the EventLog file *
861 * regexps - [IN] set of regexp rules for Event Log test *
862 * pattern - [IN] buffer for read of data of EventLog *
863 * key_severity - [IN] severity of logged data sources *
864 * key_source - [IN] name of logged data source *
865 * key_logeventid - [IN] the application-specific identifier *
866 * for the event *
867 * rate - [IN] threshold of records count at a time *
868 * process_value_cb - [IN] callback function for sending data to *
869 * the server *
870 * metric - [IN/OUT] parameters for EventLog process *
871 * lastlogsize_sent - [OUT] position of the last record sent to *
872 * the server *
873 * error - [OUT] the error message in the case of *
874 * failure *
875 * *
876 * Return value: SUCCEED or FAIL *
877 * *
878 ******************************************************************************/
process_eventslog6(const char * server,unsigned short port,const char * eventlog_name,EVT_HANDLE * render_context,EVT_HANDLE * query,zbx_uint64_t lastlogsize,zbx_uint64_t FirstID,zbx_uint64_t LastID,zbx_vector_ptr_t * regexps,const char * pattern,const char * key_severity,const char * key_source,const char * key_logeventid,int rate,zbx_process_value_func_t process_value_cb,ZBX_ACTIVE_METRIC * metric,zbx_uint64_t * lastlogsize_sent,char ** error)879 int process_eventslog6(const char *server, unsigned short port, const char *eventlog_name, EVT_HANDLE *render_context,
880 EVT_HANDLE *query, zbx_uint64_t lastlogsize, zbx_uint64_t FirstID, zbx_uint64_t LastID,
881 zbx_vector_ptr_t *regexps, const char *pattern, const char *key_severity, const char *key_source,
882 const char *key_logeventid, int rate, zbx_process_value_func_t process_value_cb,
883 ZBX_ACTIVE_METRIC *metric, zbx_uint64_t *lastlogsize_sent, char **error)
884 {
885 # define EVT_ARRAY_SIZE 100
886
887 const char *str_severity;
888 zbx_uint64_t keywords, i, reading_startpoint = 0;
889 wchar_t *eventlog_name_w = NULL;
890 int s_count = 0, p_count = 0, send_err = SUCCEED, ret = FAIL, match = SUCCEED;
891 DWORD required_buf_size = 0, error_code = ERROR_SUCCESS;
892
893 unsigned long evt_timestamp, evt_eventid = 0;
894 char *evt_provider, *evt_source, *evt_message, str_logeventid[8];
895 unsigned short evt_severity;
896 EVT_HANDLE event_bookmarks[EVT_ARRAY_SIZE];
897
898 zabbix_log(LOG_LEVEL_DEBUG, "In %s() source: '%s' previous lastlogsize: " ZBX_FS_UI64 ", FirstID: "
899 ZBX_FS_UI64 ", LastID: " ZBX_FS_UI64, __func__, eventlog_name, lastlogsize, FirstID,
900 LastID);
901
902 /* update counters */
903 if (1 == metric->skip_old_data)
904 {
905 metric->lastlogsize = LastID - 1;
906 metric->skip_old_data = 0;
907 zabbix_log(LOG_LEVEL_DEBUG, "skipping existing data: lastlogsize:" ZBX_FS_UI64, lastlogsize);
908 goto finish;
909 }
910
911 if (NULL == *query)
912 {
913 zabbix_log(LOG_LEVEL_DEBUG, "%s() no EvtQuery handle", __func__);
914 goto out;
915 }
916
917 if (lastlogsize >= FirstID && lastlogsize < LastID)
918 reading_startpoint = lastlogsize + 1;
919 else
920 reading_startpoint = FirstID;
921
922 if (reading_startpoint == LastID) /* LastID = FirstID + count */
923 goto finish;
924
925 eventlog_name_w = zbx_utf8_to_unicode(eventlog_name);
926
927 while (ERROR_SUCCESS == error_code)
928 {
929 /* get the entries */
930 if (TRUE != EvtNext(*query, EVT_ARRAY_SIZE, event_bookmarks, INFINITE, 0, &required_buf_size))
931 {
932 /* The event reading query had less items than we calculated before. */
933 /* Either the eventlog was cleaned or our calculations were wrong. */
934 /* Either way we can safely abort the query by setting NULL value */
935 /* and returning success, which is interpreted as empty eventlog. */
936 if (ERROR_NO_MORE_ITEMS == (error_code = GetLastError()))
937 continue;
938
939 *error = zbx_dsprintf(*error, "EvtNext failed: %s, EventRecordID:" ZBX_FS_UI64,
940 strerror_from_system(error_code), lastlogsize + 1);
941 goto out;
942 }
943
944 for (i = 0; i < required_buf_size; i++)
945 {
946 lastlogsize += 1;
947
948 if (SUCCEED != zbx_parse_eventlog_message6(eventlog_name_w, render_context, &event_bookmarks[i],
949 &lastlogsize, &evt_severity, &evt_timestamp, &evt_provider, &evt_source,
950 &evt_message, &evt_eventid, &keywords, error))
951 {
952 goto out;
953 }
954
955 switch (evt_severity)
956 {
957 case WINEVENT_LEVEL_LOG_ALWAYS:
958 case WINEVENT_LEVEL_INFO:
959 if (0 != (keywords & WINEVENT_KEYWORD_AUDIT_FAILURE))
960 {
961 evt_severity = ITEM_LOGTYPE_FAILURE_AUDIT;
962 str_severity = AUDIT_FAILURE;
963 break;
964 }
965 else if (0 != (keywords & WINEVENT_KEYWORD_AUDIT_SUCCESS))
966 {
967 evt_severity = ITEM_LOGTYPE_SUCCESS_AUDIT;
968 str_severity = AUDIT_SUCCESS;
969 break;
970 }
971 else
972 evt_severity = ITEM_LOGTYPE_INFORMATION;
973 str_severity = INFORMATION_TYPE;
974 break;
975 case WINEVENT_LEVEL_WARNING:
976 evt_severity = ITEM_LOGTYPE_WARNING;
977 str_severity = WARNING_TYPE;
978 break;
979 case WINEVENT_LEVEL_ERROR:
980 evt_severity = ITEM_LOGTYPE_ERROR;
981 str_severity = ERROR_TYPE;
982 break;
983 case WINEVENT_LEVEL_CRITICAL:
984 evt_severity = ITEM_LOGTYPE_CRITICAL;
985 str_severity = CRITICAL_TYPE;
986 break;
987 case WINEVENT_LEVEL_VERBOSE:
988 evt_severity = ITEM_LOGTYPE_VERBOSE;
989 str_severity = VERBOSE_TYPE;
990 break;
991 }
992
993 zbx_snprintf(str_logeventid, sizeof(str_logeventid), "%lu", evt_eventid);
994
995 if (0 == p_count)
996 {
997 int ret1, ret2, ret3, ret4;
998
999 if (FAIL == (ret1 = regexp_match_ex(regexps, evt_message, pattern,
1000 ZBX_CASE_SENSITIVE)))
1001 {
1002 *error = zbx_strdup(*error,
1003 "Invalid regular expression in the second parameter.");
1004 match = FAIL;
1005 }
1006 else if (FAIL == (ret2 = regexp_match_ex(regexps, str_severity, key_severity,
1007 ZBX_IGNORE_CASE)))
1008 {
1009 *error = zbx_strdup(*error,
1010 "Invalid regular expression in the third parameter.");
1011 match = FAIL;
1012 }
1013 else if (FAIL == (ret3 = regexp_match_ex(regexps, evt_provider, key_source,
1014 ZBX_IGNORE_CASE)))
1015 {
1016 *error = zbx_strdup(*error,
1017 "Invalid regular expression in the fourth parameter.");
1018 match = FAIL;
1019 }
1020 else if (FAIL == (ret4 = regexp_match_ex(regexps, str_logeventid,
1021 key_logeventid, ZBX_CASE_SENSITIVE)))
1022 {
1023 *error = zbx_strdup(*error,
1024 "Invalid regular expression in the fifth parameter.");
1025 match = FAIL;
1026 }
1027
1028 if (FAIL == match)
1029 {
1030 zbx_free(evt_source);
1031 zbx_free(evt_provider);
1032 zbx_free(evt_message);
1033
1034 ret = FAIL;
1035 break;
1036 }
1037
1038 match = ZBX_REGEXP_MATCH == ret1 && ZBX_REGEXP_MATCH == ret2 &&
1039 ZBX_REGEXP_MATCH == ret3 && ZBX_REGEXP_MATCH == ret4;
1040 }
1041 else
1042 {
1043 match = ZBX_REGEXP_MATCH == regexp_match_ex(regexps, evt_message, pattern,
1044 ZBX_CASE_SENSITIVE) &&
1045 ZBX_REGEXP_MATCH == regexp_match_ex(regexps, str_severity,
1046 key_severity, ZBX_IGNORE_CASE) &&
1047 ZBX_REGEXP_MATCH == regexp_match_ex(regexps, evt_provider,
1048 key_source, ZBX_IGNORE_CASE) &&
1049 ZBX_REGEXP_MATCH == regexp_match_ex(regexps, str_logeventid,
1050 key_logeventid, ZBX_CASE_SENSITIVE);
1051 }
1052
1053 if (1 == match)
1054 {
1055 send_err = process_value_cb(server, port, CONFIG_HOSTNAME, metric->key_orig,
1056 evt_message, ITEM_STATE_NORMAL, &lastlogsize, NULL, &evt_timestamp,
1057 evt_provider, &evt_severity, &evt_eventid,
1058 metric->flags | ZBX_METRIC_FLAG_PERSISTENT);
1059
1060 if (SUCCEED == send_err)
1061 {
1062 *lastlogsize_sent = lastlogsize;
1063 s_count++;
1064 }
1065 }
1066 p_count++;
1067
1068 zbx_free(evt_source);
1069 zbx_free(evt_provider);
1070 zbx_free(evt_message);
1071
1072 if (SUCCEED == send_err)
1073 {
1074 metric->lastlogsize = lastlogsize;
1075 }
1076 else
1077 {
1078 /* buffer is full, stop processing active checks */
1079 /* till the buffer is cleared */
1080 break;
1081 }
1082
1083 /* do not flood Zabbix server if file grows too fast */
1084 if (s_count >= (rate * metric->refresh))
1085 break;
1086
1087 /* do not flood local system if file grows too fast */
1088 if (p_count >= (4 * rate * metric->refresh))
1089 break;
1090 }
1091
1092 if (i < required_buf_size)
1093 error_code = ERROR_NO_MORE_ITEMS;
1094 }
1095 finish:
1096 ret = SUCCEED;
1097 out:
1098 for (i = 0; i < required_buf_size; i++)
1099 {
1100 if (NULL != event_bookmarks[i])
1101 EvtClose(event_bookmarks[i]);
1102 }
1103
1104 zbx_free(eventlog_name_w);
1105 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s last eventid:%lu", __func__, zbx_result_string(ret), evt_eventid);
1106
1107 return ret;
1108 }
1109
1110 /* finalize eventlog6 and free the handles */
finalize_eventlog6(EVT_HANDLE * render_context,EVT_HANDLE * query)1111 int finalize_eventlog6(EVT_HANDLE *render_context, EVT_HANDLE *query)
1112 {
1113 int ret = FAIL;
1114
1115 zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
1116
1117 if (NULL != *query)
1118 {
1119 EvtClose(*query);
1120 *query = NULL;
1121 }
1122
1123 if (NULL != *render_context)
1124 {
1125 EvtClose(*render_context);
1126 *render_context = NULL;
1127 }
1128
1129 ret = SUCCEED;
1130
1131 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
1132
1133 return ret;
1134 }
1135
1136 /******************************************************************************
1137 * *
1138 * Function: seek_eventlog *
1139 * *
1140 * Purpose: try to set reading position in event log *
1141 * *
1142 * Parameters: eventlog_handle - [IN] the handle to the event log to be read *
1143 * FirstID - [IN] the first Event log record to be parse *
1144 * ReadDirection - [IN] direction of reading: *
1145 * EVENTLOG_FORWARDS_READ or *
1146 * EVENTLOG_BACKWARDS_READ *
1147 * LastID - [IN] position of last record in EventLog *
1148 * eventlog_name - [IN] the name of the event log *
1149 * pELRs - [IN/OUT] buffer for read of data of EventLog *
1150 * buffer_size - [IN/OUT] size of the pELRs *
1151 * num_bytes_read - [OUT] the number of bytes read from EventLog *
1152 * error_code - [OUT] error code (e.g. from ReadEventLog()) *
1153 * error - [OUT] the error message in the case of *
1154 * failure *
1155 * *
1156 * Return value: SUCCEED or FAIL *
1157 * *
1158 ******************************************************************************/
seek_eventlog(HANDLE * eventlog_handle,zbx_uint64_t FirstID,DWORD ReadDirection,zbx_uint64_t LastID,const char * eventlog_name,BYTE ** pELRs,int * buffer_size,DWORD * num_bytes_read,DWORD * error_code,char ** error)1159 static int seek_eventlog(HANDLE *eventlog_handle, zbx_uint64_t FirstID, DWORD ReadDirection,
1160 zbx_uint64_t LastID, const char *eventlog_name, BYTE **pELRs, int *buffer_size, DWORD *num_bytes_read,
1161 DWORD *error_code, char **error)
1162 {
1163 DWORD dwRecordNumber, required_buf_size;
1164 zbx_uint64_t skip_count = 0;
1165
1166 /* convert to DWORD to handle possible event record number wraparound */
1167 dwRecordNumber = (DWORD)FirstID;
1168
1169 *error_code = ERROR_SUCCESS;
1170
1171 while (ERROR_SUCCESS == *error_code)
1172 {
1173 if (0 != ReadEventLog(eventlog_handle, EVENTLOG_SEEK_READ | EVENTLOG_FORWARDS_READ, dwRecordNumber,
1174 *pELRs, *buffer_size, num_bytes_read, &required_buf_size))
1175 {
1176 return SUCCEED;
1177 }
1178
1179 if (ERROR_INVALID_PARAMETER == (*error_code = GetLastError()))
1180 {
1181 /* See Microsoft Knowledge Base article, 177199 "BUG: ReadEventLog Fails with Error 87" */
1182 /* how ReadEventLog() can fail with all valid parameters. */
1183 /* Error code 87 is named ERROR_INVALID_PARAMETER. */
1184 break;
1185 }
1186
1187 if (ERROR_HANDLE_EOF == *error_code)
1188 return SUCCEED;
1189
1190 if (ERROR_INSUFFICIENT_BUFFER == *error_code)
1191 {
1192 *buffer_size = required_buf_size;
1193 *pELRs = (BYTE *)zbx_realloc((void *)*pELRs, *buffer_size);
1194 *error_code = ERROR_SUCCESS;
1195 continue;
1196 }
1197
1198 *error = zbx_dsprintf(*error, "Cannot read eventlog '%s': %s.", eventlog_name,
1199 strerror_from_system(*error_code));
1200 return FAIL;
1201 }
1202
1203 if (EVENTLOG_FORWARDS_READ == ReadDirection)
1204 {
1205 /* Error 87 when reading forwards is handled outside this function */
1206 *error_code = ERROR_SUCCESS;
1207 return SUCCEED;
1208 }
1209
1210 /* fallback implementation to deal with Error 87 when reading backwards */
1211
1212 if (ERROR_INVALID_PARAMETER == *error_code)
1213 {
1214 if (LastID == FirstID)
1215 skip_count = 1;
1216 else
1217 skip_count = LastID - FirstID;
1218
1219 zabbix_log(LOG_LEVEL_DEBUG, "In %s(): fallback error_code=%d skip_count="ZBX_FS_UI64, __func__,
1220 *error_code, skip_count);
1221 }
1222
1223 *error_code = ERROR_SUCCESS;
1224
1225 while (0 < skip_count && ERROR_SUCCESS == *error_code)
1226 {
1227 BYTE *pEndOfRecords, *pELR;
1228
1229 if (0 == ReadEventLog(eventlog_handle, EVENTLOG_SEQUENTIAL_READ | ReadDirection, 0, *pELRs,
1230 *buffer_size, num_bytes_read, &required_buf_size))
1231 {
1232 if (ERROR_INSUFFICIENT_BUFFER == (*error_code = GetLastError()))
1233 {
1234 *error_code = ERROR_SUCCESS;
1235 *buffer_size = required_buf_size;
1236 *pELRs = (BYTE *)zbx_realloc((void *)*pELRs, *buffer_size);
1237 continue;
1238 }
1239
1240 if (ERROR_HANDLE_EOF != *error_code)
1241 break;
1242
1243 *error = zbx_dsprintf(*error, "Cannot read eventlog '%s': %s.", eventlog_name,
1244 strerror_from_system(*error_code));
1245 return FAIL;
1246 }
1247
1248 pELR = *pELRs;
1249 pEndOfRecords = *pELRs + *num_bytes_read;
1250 *num_bytes_read = 0; /* we can't reuse the buffer value because of the sort order */
1251
1252 while (pELR < pEndOfRecords)
1253 {
1254 if (0 == --skip_count)
1255 break;
1256
1257 pELR += ((PEVENTLOGRECORD)pELR)->Length;
1258 }
1259 }
1260
1261 if (ERROR_HANDLE_EOF == *error_code)
1262 *error_code = ERROR_SUCCESS;
1263
1264 return SUCCEED;
1265 }
1266
1267 /******************************************************************************
1268 * *
1269 * Function: zbx_parse_eventlog_message *
1270 * *
1271 * Purpose: details parse of a single Event Log record *
1272 * *
1273 * Parameters: wsource - [IN] EventLog file name *
1274 * pELR - [IN] buffer with single Event Log Record *
1275 * out_source - [OUT] the ELR detail *
1276 * out_message - [OUT] the ELR detail *
1277 * out_severity - [OUT] the ELR detail *
1278 * out_timestamp - [OUT] the ELR detail *
1279 * out_eventid - [OUT] the ELR detail *
1280 * *
1281 ******************************************************************************/
1282 #define MAX_INSERT_STRS 100
zbx_parse_eventlog_message(const wchar_t * wsource,const EVENTLOGRECORD * pELR,char ** out_source,char ** out_message,unsigned short * out_severity,unsigned long * out_timestamp,unsigned long * out_eventid)1283 static void zbx_parse_eventlog_message(const wchar_t *wsource, const EVENTLOGRECORD *pELR, char **out_source,
1284 char **out_message, unsigned short *out_severity, unsigned long *out_timestamp,
1285 unsigned long *out_eventid)
1286 {
1287 wchar_t *pEventMessageFile = NULL, *pParamMessageFile = NULL, *pFile = NULL, *pNextFile = NULL, *pCh,
1288 *aInsertStrings[MAX_INSERT_STRS];
1289 HINSTANCE hLib = NULL, hParamLib = NULL;
1290 long i;
1291 int err;
1292
1293 memset(aInsertStrings, 0, sizeof(aInsertStrings));
1294
1295 *out_message = NULL;
1296 *out_severity = pELR->EventType; /* return event type */
1297 *out_timestamp = pELR->TimeGenerated; /* return timestamp */
1298 *out_eventid = pELR->EventID & 0xffff;
1299 *out_source = zbx_unicode_to_utf8((wchar_t *)(pELR + 1)); /* copy source name */
1300
1301 /* get message file names */
1302 zbx_get_message_files(wsource, (wchar_t *)(pELR + 1), &pEventMessageFile, &pParamMessageFile);
1303
1304 /* prepare insert string array */
1305 if (0 < pELR->NumStrings)
1306 {
1307 pCh = (wchar_t *)((unsigned char *)pELR + pELR->StringOffset);
1308
1309 for (i = 0; i < pELR->NumStrings && i < MAX_INSERT_STRS; i++)
1310 {
1311 aInsertStrings[i] = pCh;
1312 pCh += wcslen(pCh) + 1;
1313 }
1314 }
1315
1316 err = FAIL;
1317
1318 for (pFile = pEventMessageFile; NULL != pFile && err != SUCCEED; pFile = pNextFile)
1319 {
1320 if (NULL != (pNextFile = wcschr(pFile, TEXT(';'))))
1321 {
1322 *pNextFile = '\0';
1323 pNextFile++;
1324 }
1325
1326 if (NULL != (hLib = zbx_load_message_file(pFile)))
1327 {
1328 if (NULL != (*out_message = zbx_format_message(hLib, pELR->EventID, aInsertStrings)))
1329 {
1330 err = SUCCEED;
1331
1332 if (NULL != (hParamLib = zbx_load_message_file(pParamMessageFile)))
1333 {
1334 zbx_translate_message_params(out_message, hParamLib);
1335 FreeLibrary(hParamLib);
1336 }
1337 }
1338
1339 FreeLibrary(hLib);
1340 }
1341 }
1342
1343 zbx_free(pEventMessageFile);
1344 zbx_free(pParamMessageFile);
1345
1346 if (SUCCEED != err)
1347 {
1348 *out_message = zbx_strdcatf(*out_message, "The description for Event ID:%lu in Source:'%s'"
1349 " cannot be found. The local computer may not have the necessary registry"
1350 " information or message DLL files to display messages from a remote computer.",
1351 *out_eventid, NULL == *out_source ? "" : *out_source);
1352
1353 if (0 < pELR->NumStrings)
1354 {
1355 char *buf;
1356
1357 *out_message = zbx_strdcat(*out_message, " The following information is part of the event: ");
1358
1359 for (i = 0, pCh = (wchar_t *)((unsigned char *)pELR + pELR->StringOffset);
1360 i < pELR->NumStrings;
1361 i++, pCh += wcslen(pCh) + 1)
1362 {
1363 if (0 < i)
1364 *out_message = zbx_strdcat(*out_message, "; ");
1365
1366 buf = zbx_unicode_to_utf8(pCh);
1367 *out_message = zbx_strdcat(*out_message, buf);
1368 zbx_free(buf);
1369 }
1370 }
1371 }
1372
1373 zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
1374
1375 return;
1376 }
1377
1378 /******************************************************************************
1379 * *
1380 * Function: process_eventslog *
1381 * *
1382 * Purpose: batch processing of Event Log file *
1383 * *
1384 * Parameters: server - [IN] IP or Hostname of Zabbix server *
1385 * port - [IN] port of Zabbix server *
1386 * eventlog_name - [IN] the name of the event log *
1387 * regexps - [IN] set of regexp rules for Event Log test *
1388 * pattern - [IN] buffer for read of data of EventLog *
1389 * key_severity - [IN] severity of logged data sources *
1390 * key_source - [IN] name of logged data source *
1391 * key_logeventid - [IN] the application-specific identifier *
1392 * for the event *
1393 * rate - [IN] threshold of records count at a time *
1394 * process_value_cb - [IN] callback function for sending data to *
1395 * the server *
1396 * metric - [IN/OUT] parameters for EventLog process *
1397 * lastlogsize_sent - [OUT] position of the last record sent to *
1398 * the server *
1399 * error - [OUT] the error message in the case of *
1400 * failure *
1401 * *
1402 * Return value: SUCCEED or FAIL *
1403 * *
1404 ******************************************************************************/
process_eventslog(const char * server,unsigned short port,const char * eventlog_name,zbx_vector_ptr_t * regexps,const char * pattern,const char * key_severity,const char * key_source,const char * key_logeventid,int rate,zbx_process_value_func_t process_value_cb,ZBX_ACTIVE_METRIC * metric,zbx_uint64_t * lastlogsize_sent,char ** error)1405 int process_eventslog(const char *server, unsigned short port, const char *eventlog_name, zbx_vector_ptr_t *regexps,
1406 const char *pattern, const char *key_severity, const char *key_source, const char *key_logeventid,
1407 int rate, zbx_process_value_func_t process_value_cb, ZBX_ACTIVE_METRIC *metric,
1408 zbx_uint64_t *lastlogsize_sent, char **error)
1409 {
1410 int ret = FAIL;
1411 HANDLE eventlog_handle = NULL;
1412 wchar_t *eventlog_name_w;
1413 zbx_uint64_t FirstID, LastID, lastlogsize;
1414 int buffer_size = 64 * ZBX_KIBIBYTE;
1415 DWORD num_bytes_read = 0, required_buf_size, ReadDirection, error_code;
1416 BYTE *pELRs = NULL;
1417 int s_count, p_count, send_err = SUCCEED, match = SUCCEED;
1418 unsigned long timestamp = 0;
1419 char *source;
1420
1421 lastlogsize = metric->lastlogsize;
1422 zabbix_log(LOG_LEVEL_DEBUG, "In %s() source:'%s' lastlogsize:" ZBX_FS_UI64, __func__, eventlog_name,
1423 lastlogsize);
1424
1425 /* From MSDN documentation: */
1426 /* The RecordNumber member of EVENTLOGRECORD contains the record number for the event log record. */
1427 /* The very first record written to an event log is record number 1, and other records are */
1428 /* numbered sequentially. If the record number reaches ULONG_MAX, the next record number will be 0, */
1429 /* not 1; however, you use zero to seek to the record. */
1430 /* */
1431 /* This RecordNumber wraparound is handled simply by using 64bit integer to calculate record */
1432 /* numbers and then converting to DWORD values. */
1433
1434 if (NULL == eventlog_name || '\0' == *eventlog_name)
1435 {
1436 *error = zbx_strdup(*error, "Cannot open eventlog with empty name.");
1437 return ret;
1438 }
1439
1440 eventlog_name_w = zbx_utf8_to_unicode(eventlog_name);
1441
1442 if (SUCCEED != zbx_open_eventlog(eventlog_name_w, &eventlog_handle, &FirstID, &LastID, &error_code))
1443 {
1444 *error = zbx_dsprintf(*error, "Cannot open eventlog '%s': %s.", eventlog_name,
1445 strerror_from_system(error_code));
1446 goto out;
1447 }
1448
1449 if (1 == metric->skip_old_data)
1450 {
1451 metric->lastlogsize = LastID;
1452 metric->skip_old_data = 0;
1453 zabbix_log(LOG_LEVEL_DEBUG, "skipping existing data: lastlogsize:" ZBX_FS_UI64, metric->lastlogsize);
1454 goto finish;
1455 }
1456
1457 /* Having lastlogsize greater than LastID means that there was oldest event record */
1458 /* (FirstID) wraparound. In this case we must also wrap the lastlogsize value. */
1459 if (lastlogsize > LastID)
1460 lastlogsize = (DWORD)lastlogsize;
1461
1462 ReadDirection = ((LastID - FirstID) / 2) > lastlogsize ? EVENTLOG_FORWARDS_READ : EVENTLOG_BACKWARDS_READ;
1463
1464 /* if the lastlogsize is still outside log record interval reset it to the oldest record number, */
1465 /* otherwise set FirstID to the next record after lastlogsize, which is the first event record */
1466 /* to read */
1467 if (lastlogsize > LastID || lastlogsize < FirstID)
1468 {
1469 lastlogsize = FirstID;
1470 ReadDirection = 0;
1471 }
1472 else
1473 FirstID = lastlogsize + 1;
1474
1475 pELRs = (BYTE*)zbx_malloc((void *)pELRs, buffer_size);
1476
1477 if (0 == ReadDirection) /* read eventlog from the first record */
1478 {
1479 error_code = ERROR_SUCCESS;
1480 }
1481 else if (LastID < FirstID) /* no new records */
1482 {
1483 error_code = ERROR_HANDLE_EOF;
1484 }
1485 else if (SUCCEED != seek_eventlog(eventlog_handle, FirstID, ReadDirection, LastID, eventlog_name, &pELRs,
1486 &buffer_size, &num_bytes_read, &error_code, error))
1487 {
1488 goto out;
1489 }
1490
1491 zabbix_log(LOG_LEVEL_TRACE, "%s(): state before EventLog reading: num_bytes_read=%u error=%s FirstID="
1492 ZBX_FS_UI64 " LastID=" ZBX_FS_UI64 " lastlogsize=" ZBX_FS_UI64, __func__,
1493 (unsigned int)num_bytes_read, strerror_from_system(error_code), FirstID, LastID, lastlogsize);
1494
1495 if (ERROR_HANDLE_EOF == error_code)
1496 goto finish;
1497
1498 s_count = 0;
1499 p_count = 0;
1500
1501 /* Read blocks of records until you reach the end of the log or an */
1502 /* error occurs. The records are read from oldest to newest. If the buffer */
1503 /* is not big enough to hold a complete event record, reallocate the buffer. */
1504 while (ERROR_SUCCESS == error_code)
1505 {
1506 BYTE *pELR, *pEndOfRecords;
1507
1508 if (0 == num_bytes_read && 0 == ReadEventLog(eventlog_handle,
1509 EVENTLOG_SEQUENTIAL_READ | EVENTLOG_FORWARDS_READ, 0,
1510 pELRs, buffer_size, &num_bytes_read, &required_buf_size))
1511 {
1512 if (ERROR_INSUFFICIENT_BUFFER == (error_code = GetLastError()))
1513 {
1514 error_code = ERROR_SUCCESS;
1515 buffer_size = required_buf_size;
1516 pELRs = (BYTE *)zbx_realloc((void *)pELRs, buffer_size);
1517 continue;
1518 }
1519
1520 if (ERROR_HANDLE_EOF == error_code)
1521 break;
1522
1523 *error = zbx_dsprintf(*error, "Cannot read eventlog '%s': %s.", eventlog_name,
1524 strerror_from_system(error_code));
1525 goto out;
1526 }
1527
1528 pELR = pELRs;
1529 pEndOfRecords = pELR + num_bytes_read;
1530
1531 zabbix_log(LOG_LEVEL_TRACE, "%s(): state before buffer parsing: num_bytes_read = %u RecordNumber = %d"
1532 "FirstID = "ZBX_FS_UI64" LastID = "ZBX_FS_UI64" lastlogsize="ZBX_FS_UI64,
1533 __func__, (unsigned int)num_bytes_read, ((PEVENTLOGRECORD)pELR)->RecordNumber,
1534 FirstID, LastID, lastlogsize);
1535 num_bytes_read = 0;
1536
1537 while (pELR < pEndOfRecords)
1538 {
1539 /* to prevent mismatch in comparing with RecordNumber in case of wrap-around, */
1540 /* we look for using '=' */
1541 if (0 != timestamp || (DWORD)FirstID == ((PEVENTLOGRECORD)pELR)->RecordNumber)
1542 {
1543 const char *str_severity;
1544 unsigned short severity;
1545 unsigned long logeventid;
1546 char *value, str_logeventid[8];
1547
1548 /* increase counter only for records >= FirstID (start point for the search) */
1549 /* to avoid wrap-around of the 32b RecordNumber we increase the 64b lastlogsize */
1550 if (0 == timestamp)
1551 lastlogsize = FirstID;
1552 else
1553 lastlogsize += 1;
1554
1555 zbx_parse_eventlog_message(eventlog_name_w, (EVENTLOGRECORD *)pELR, &source, &value,
1556 &severity, ×tamp, &logeventid);
1557
1558 switch (severity)
1559 {
1560 case EVENTLOG_SUCCESS:
1561 case EVENTLOG_INFORMATION_TYPE:
1562 severity = ITEM_LOGTYPE_INFORMATION;
1563 str_severity = INFORMATION_TYPE;
1564 break;
1565 case EVENTLOG_WARNING_TYPE:
1566 severity = ITEM_LOGTYPE_WARNING;
1567 str_severity = WARNING_TYPE;
1568 break;
1569 case EVENTLOG_ERROR_TYPE:
1570 severity = ITEM_LOGTYPE_ERROR;
1571 str_severity = ERROR_TYPE;
1572 break;
1573 case EVENTLOG_AUDIT_FAILURE:
1574 severity = ITEM_LOGTYPE_FAILURE_AUDIT;
1575 str_severity = AUDIT_FAILURE;
1576 break;
1577 case EVENTLOG_AUDIT_SUCCESS:
1578 severity = ITEM_LOGTYPE_SUCCESS_AUDIT;
1579 str_severity = AUDIT_SUCCESS;
1580 break;
1581 }
1582
1583 zbx_snprintf(str_logeventid, sizeof(str_logeventid), "%lu", logeventid);
1584
1585 if (0 == p_count)
1586 {
1587 int ret1, ret2, ret3, ret4;
1588
1589 if (FAIL == (ret1 = regexp_match_ex(regexps, value, pattern,
1590 ZBX_CASE_SENSITIVE)))
1591 {
1592 *error = zbx_strdup(*error,
1593 "Invalid regular expression in the second parameter.");
1594 match = FAIL;
1595 }
1596 else if (FAIL == (ret2 = regexp_match_ex(regexps, str_severity, key_severity,
1597 ZBX_IGNORE_CASE)))
1598 {
1599 *error = zbx_strdup(*error,
1600 "Invalid regular expression in the third parameter.");
1601 match = FAIL;
1602 }
1603 else if (FAIL == (ret3 = regexp_match_ex(regexps, source, key_source,
1604 ZBX_IGNORE_CASE)))
1605 {
1606 *error = zbx_strdup(*error,
1607 "Invalid regular expression in the fourth parameter.");
1608 match = FAIL;
1609 }
1610 else if (FAIL == (ret4 = regexp_match_ex(regexps, str_logeventid,
1611 key_logeventid, ZBX_CASE_SENSITIVE)))
1612 {
1613 *error = zbx_strdup(*error,
1614 "Invalid regular expression in the fifth parameter.");
1615 match = FAIL;
1616 }
1617
1618 if (FAIL == match)
1619 {
1620 zbx_free(source);
1621 zbx_free(value);
1622
1623 ret = FAIL;
1624 break;
1625 }
1626
1627 match = ZBX_REGEXP_MATCH == ret1 && ZBX_REGEXP_MATCH == ret2 &&
1628 ZBX_REGEXP_MATCH == ret3 && ZBX_REGEXP_MATCH == ret4;
1629 }
1630 else
1631 {
1632 match = ZBX_REGEXP_MATCH == regexp_match_ex(regexps, value, pattern,
1633 ZBX_CASE_SENSITIVE) &&
1634 ZBX_REGEXP_MATCH == regexp_match_ex(regexps, str_severity,
1635 key_severity, ZBX_IGNORE_CASE) &&
1636 ZBX_REGEXP_MATCH == regexp_match_ex(regexps, source,
1637 key_source, ZBX_IGNORE_CASE) &&
1638 ZBX_REGEXP_MATCH == regexp_match_ex(regexps, str_logeventid,
1639 key_logeventid, ZBX_CASE_SENSITIVE);
1640 }
1641
1642 if (1 == match)
1643 {
1644 send_err = process_value_cb(server, port, CONFIG_HOSTNAME, metric->key_orig,
1645 value, ITEM_STATE_NORMAL, &lastlogsize, NULL, ×tamp,
1646 source, &severity, &logeventid,
1647 metric->flags | ZBX_METRIC_FLAG_PERSISTENT);
1648
1649 if (SUCCEED == send_err)
1650 {
1651 *lastlogsize_sent = lastlogsize;
1652 s_count++;
1653 }
1654 }
1655 p_count++;
1656
1657 zbx_free(source);
1658 zbx_free(value);
1659
1660 if (SUCCEED == send_err)
1661 {
1662 metric->lastlogsize = lastlogsize;
1663 }
1664 else
1665 {
1666 /* buffer is full, stop processing active checks */
1667 /* till the buffer is cleared */
1668 break;
1669 }
1670
1671 /* do not flood Zabbix server if file grows too fast */
1672 if (s_count >= (rate * metric->refresh))
1673 break;
1674
1675 /* do not flood local system if file grows too fast */
1676 if (p_count >= (4 * rate * metric->refresh))
1677 break;
1678 }
1679
1680 pELR += ((PEVENTLOGRECORD)pELR)->Length;
1681 }
1682
1683 if (pELR < pEndOfRecords)
1684 error_code = ERROR_NO_MORE_ITEMS;
1685 }
1686
1687 finish:
1688 ret = SUCCEED;
1689 out:
1690 zbx_close_eventlog(eventlog_handle);
1691 zbx_free(eventlog_name_w);
1692 zbx_free(pELRs);
1693 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
1694
1695 return ret;
1696 }
1697
process_eventlog_check(char * server,unsigned short port,zbx_vector_ptr_t * regexps,ZBX_ACTIVE_METRIC * metric,zbx_process_value_func_t process_value_cb,zbx_uint64_t * lastlogsize_sent,char ** error)1698 int process_eventlog_check(char *server, unsigned short port, zbx_vector_ptr_t *regexps, ZBX_ACTIVE_METRIC *metric,
1699 zbx_process_value_func_t process_value_cb, zbx_uint64_t *lastlogsize_sent, char **error)
1700 {
1701 int ret = FAIL;
1702 AGENT_REQUEST request;
1703 const char *filename, *pattern, *maxlines_persec, *key_severity, *key_source, *key_logeventid, *skip;
1704 int rate;
1705 OSVERSIONINFO versionInfo;
1706
1707 init_request(&request);
1708
1709 if (SUCCEED != parse_item_key(metric->key, &request))
1710 {
1711 *error = zbx_strdup(*error, "Invalid item key format.");
1712 goto out;
1713 }
1714
1715 if (0 == get_rparams_num(&request))
1716 {
1717 *error = zbx_strdup(*error, "Invalid number of parameters.");
1718 goto out;
1719 }
1720
1721 if (7 < get_rparams_num(&request))
1722 {
1723 *error = zbx_strdup(*error, "Too many parameters.");
1724 goto out;
1725 }
1726
1727 if (NULL == (filename = get_rparam(&request, 0)) || '\0' == *filename)
1728 {
1729 *error = zbx_strdup(*error, "Invalid first parameter.");
1730 goto out;
1731 }
1732
1733 if (NULL == (pattern = get_rparam(&request, 1)))
1734 {
1735 pattern = "";
1736 }
1737 else if ('@' == *pattern && SUCCEED != zbx_global_regexp_exists(pattern + 1, regexps))
1738 {
1739 *error = zbx_dsprintf(*error, "Global regular expression \"%s\" does not exist.", pattern + 1);
1740 goto out;
1741 }
1742
1743 if (NULL == (key_severity = get_rparam(&request, 2)))
1744 {
1745 key_severity = "";
1746 }
1747 else if ('@' == *key_severity && SUCCEED != zbx_global_regexp_exists(key_severity + 1, regexps))
1748 {
1749 *error = zbx_dsprintf(*error, "Global regular expression \"%s\" does not exist.", key_severity + 1);
1750 goto out;
1751 }
1752
1753 if (NULL == (key_source = get_rparam(&request, 3)))
1754 {
1755 key_source = "";
1756 }
1757 else if ('@' == *key_source && SUCCEED != zbx_global_regexp_exists(key_source + 1, regexps))
1758 {
1759 *error = zbx_dsprintf(*error, "Global regular expression \"%s\" does not exist.", key_source + 1);
1760 goto out;
1761 }
1762
1763 if (NULL == (key_logeventid = get_rparam(&request, 4)))
1764 {
1765 key_logeventid = "";
1766 }
1767 else if ('@' == *key_logeventid && SUCCEED != zbx_global_regexp_exists(key_logeventid + 1, regexps))
1768 {
1769 *error = zbx_dsprintf(*error, "Global regular expression \"%s\" does not exist.", key_logeventid + 1);
1770 goto out;
1771 }
1772
1773 if (NULL == (maxlines_persec = get_rparam(&request, 5)) || '\0' == *maxlines_persec)
1774 {
1775 rate = CONFIG_EVENTLOG_MAX_LINES_PER_SECOND;
1776 }
1777 else if (MIN_VALUE_LINES > (rate = atoi(maxlines_persec)) || MAX_VALUE_LINES < rate)
1778 {
1779 *error = zbx_strdup(*error, "Invalid sixth parameter.");
1780 goto out;
1781 }
1782
1783 if (NULL == (skip = get_rparam(&request, 6)) || '\0' == *skip || 0 == strcmp(skip, "all"))
1784 {
1785 metric->skip_old_data = 0;
1786 }
1787 else if (0 != strcmp(skip, "skip"))
1788 {
1789 *error = zbx_strdup(*error, "Invalid seventh parameter.");
1790 goto out;
1791 }
1792
1793 versionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
1794 GetVersionEx(&versionInfo);
1795
1796 if (versionInfo.dwMajorVersion >= 6) /* Windows Vista, 7 or Server 2008 */
1797 {
1798 __try
1799 {
1800
1801 zbx_uint64_t lastlogsize = metric->lastlogsize;
1802 EVT_HANDLE eventlog6_render_context = NULL;
1803 EVT_HANDLE eventlog6_query = NULL;
1804 zbx_uint64_t eventlog6_firstid = 0;
1805 zbx_uint64_t eventlog6_lastid = 0;
1806
1807 if (SUCCEED != initialize_eventlog6(filename, &lastlogsize, &eventlog6_firstid,
1808 &eventlog6_lastid, &eventlog6_render_context, &eventlog6_query, error))
1809 {
1810 finalize_eventlog6(&eventlog6_render_context, &eventlog6_query);
1811 goto out;
1812 }
1813
1814 ret = process_eventslog6(server, port, filename, &eventlog6_render_context, &eventlog6_query,
1815 lastlogsize, eventlog6_firstid, eventlog6_lastid, regexps, pattern,
1816 key_severity, key_source, key_logeventid, rate, process_value_cb, metric,
1817 lastlogsize_sent, error);
1818
1819 finalize_eventlog6(&eventlog6_render_context, &eventlog6_query);
1820 }
1821 __except (DelayLoadDllExceptionFilter(GetExceptionInformation()))
1822 {
1823 zabbix_log(LOG_LEVEL_WARNING, "failed to process eventlog");
1824 }
1825 }
1826 else if (versionInfo.dwMajorVersion < 6) /* Windows versions before Vista */
1827 {
1828 ret = process_eventslog(server, port, filename, regexps, pattern, key_severity, key_source,
1829 key_logeventid, rate, process_value_cb, metric, lastlogsize_sent, error);
1830 }
1831 out:
1832 free_request(&request);
1833
1834 return ret;
1835 }
1836