added method AbstractObject.getDashboards
[public/netxms.git] / src / server / core / syslogd.cpp
CommitLineData
5039dede
AK
1/*
2** NetXMS - Network Management System
47cacc1f 3** Copyright (C) 2003-2014 Victor Kirhenshtein
5039dede
AK
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., 675 Mass Ave, Cambridge, MA 02139, USA.
18**
19** File: syslogd.cpp
20**
21**/
22
23#include "nxcore.h"
24#include <nxlog.h>
25#include <nxlpapi.h>
26
271c3551
VK
27/**
28 * Max syslog message length
29 */
5039dede
AK
30#define MAX_SYSLOG_MSG_LEN 1024
31
271c3551
VK
32/**
33 * Queued syslog message structure
34 */
5039dede
AK
35struct QUEUED_SYSLOG_MESSAGE
36{
967893bb 37 UINT32 dwSourceIP;
5039dede
AK
38 int nBytes;
39 char *psMsg;
40};
41
f1784ab6
VK
42/**
43 * Queues
44 */
45Queue g_syslogProcessingQueue(1000, 100);
46Queue g_syslogWriteQueue(1000, 100);
47
47cacc1f
VK
48/**
49 * Node matching policy
50 */
51enum NodeMatchingPolicy
52{
53 SOURCE_IP_THEN_HOSTNAME = 0,
54 HOSTNAME_THEN_SOURCE_IP = 1
55};
56
271c3551
VK
57/**
58 * Static data
59 */
1499848d 60static UINT64 s_msgId = 1;
1499848d
VK
61static LogParser *s_parser = NULL;
62static MUTEX s_parserLock = INVALID_MUTEX_HANDLE;
47cacc1f 63static NodeMatchingPolicy s_nodeMatchingPolicy = SOURCE_IP_THEN_HOSTNAME;
5039dede 64
e9491562
VK
65/**
66 * Parse timestamp field
67 */
5039dede
AK
68static BOOL ParseTimeStamp(char **ppStart, int nMsgSize, int *pnPos, time_t *ptmTime)
69{
70 static char psMonth[12][5] = { "Jan ", "Feb ", "Mar ", "Apr ",
71 "May ", "Jun ", "Jul ", "Aug ",
72 "Sep ", "Oct ", "Nov ", "Dec " };
73 struct tm timestamp;
74 time_t t;
75 char szBuffer[16], *pCurr = *ppStart;
76 int i;
77
78 if (nMsgSize - *pnPos < 16)
79 return FALSE; // Timestamp cannot be shorter than 16 bytes
80
81 // Prepare local time structure
82 t = time(NULL);
83 memcpy(&timestamp, localtime(&t), sizeof(struct tm));
84
85 // Month
86 for(i = 0; i < 12; i++)
87 if (!memcmp(pCurr, psMonth[i], 4))
88 {
89 timestamp.tm_mon = i;
90 break;
91 }
92 if (i == 12)
93 return FALSE;
94 pCurr += 4;
95
96 // Day of week
97 if (isdigit(*pCurr))
98 {
99 timestamp.tm_mday = *pCurr - '0';
100 }
101 else
102 {
103 if (*pCurr != ' ')
104 return FALSE; // Invalid day of month
105 timestamp.tm_mday = 0;
106 }
107 pCurr++;
108 if (isdigit(*pCurr))
109 {
110 timestamp.tm_mday = timestamp.tm_mday * 10 + (*pCurr - '0');
111 }
112 else
113 {
114 return FALSE; // Invalid day of month
115 }
116 pCurr++;
117 if (*pCurr != ' ')
118 return FALSE;
119 pCurr++;
120
121 // HH:MM:SS
122 memcpy(szBuffer, pCurr, 8);
123 szBuffer[8] = 0;
124 if (sscanf(szBuffer, "%02d:%02d:%02d", &timestamp.tm_hour,
125 &timestamp.tm_min, &timestamp.tm_sec) != 3)
126 return FALSE; // Invalid time format
127 pCurr += 8;
128
129 // Check for Cisco variant - HH:MM:SS.nnn
130 if (*pCurr == '.')
131 {
132 pCurr++;
133 if (isdigit(*pCurr))
134 pCurr++;
135 if (isdigit(*pCurr))
136 pCurr++;
137 if (isdigit(*pCurr))
138 pCurr++;
139 }
140
141 if (*pCurr != ' ')
142 return FALSE; // Space should follow timestamp
143 pCurr++;
144
145 // Convert to system time
146 *ptmTime = mktime(&timestamp);
147 if (*ptmTime == ((time_t)-1))
148 return FALSE;
149
150 // Adjust current position
151 *pnPos += (int)(pCurr - *ppStart);
152 *ppStart = pCurr;
153 return TRUE;
154}
155
e9491562
VK
156/**
157 * Parse syslog message
158 */
e0f99bf0 159static BOOL ParseSyslogMessage(char *psMsg, int nMsgLen, NX_SYSLOG_RECORD *pRec)
5039dede
AK
160{
161 int i, nLen, nPos = 0;
162 char *pCurr = psMsg;
163
e0f99bf0 164 memset(pRec, 0, sizeof(NX_SYSLOG_RECORD));
5039dede
AK
165
166 // Parse PRI part
167 if (*psMsg == '<')
168 {
169 int nPri = 0, nCount = 0;
170
171 for(pCurr++, nPos++; isdigit(*pCurr) && (nPos < nMsgLen); pCurr++, nPos++, nCount++)
172 nPri = nPri * 10 + (*pCurr - '0');
173 if (nPos >= nMsgLen)
174 return FALSE; // Unexpected end of message
175
176 if ((*pCurr == '>') && (nCount > 0) && (nCount <4))
177 {
178 pRec->nFacility = nPri / 8;
179 pRec->nSeverity = nPri % 8;
180 pCurr++;
181 nPos++;
182 }
183 else
184 {
185 return FALSE; // Invalid message
186 }
187 }
188 else
189 {
190 // Set default PRI of 13
191 pRec->nFacility = 1;
192 pRec->nSeverity = SYSLOG_SEVERITY_NOTICE;
193 }
194
195 // Parse HEADER part
196 if (ParseTimeStamp(&pCurr, nMsgLen, &nPos, &pRec->tmTimeStamp))
197 {
198 // Hostname
5e5461a9 199 for(i = 0; (*pCurr >= 33) && (*pCurr <= 126) && (i < MAX_SYSLOG_HOSTNAME_LEN - 1) && (nPos < nMsgLen); i++, nPos++, pCurr++)
5039dede
AK
200 pRec->szHostName[i] = *pCurr;
201 if ((nPos >= nMsgLen) || (*pCurr != ' '))
202 {
203 // Not a valid hostname, assuming to be a part of message
204 pCurr -= i;
205 nPos -= i;
5e5461a9 206 pRec->szHostName[0] = 0;
5039dede
AK
207 }
208 else
209 {
210 pCurr++;
211 nPos++;
212 }
213 }
214 else
215 {
216 pRec->tmTimeStamp = time(NULL);
217 }
218
219 // Parse MSG part
220 for(i = 0; isalnum(*pCurr) && (i < MAX_SYSLOG_TAG_LEN) && (nPos < nMsgLen); i++, nPos++, pCurr++)
221 pRec->szTag[i] = *pCurr;
222 if ((i == MAX_SYSLOG_TAG_LEN) || (nPos >= nMsgLen))
223 {
224 // Too long tag, assuming that it's a part of message
225 pRec->szTag[0] = 0;
226 }
227 pCurr -= i;
228 nPos -= i;
229 nLen = min(nMsgLen - nPos, MAX_LOG_MSG_LENGTH);
230 memcpy(pRec->szMessage, pCurr, nLen);
231
232 return TRUE;
233}
234
e9491562 235/**
47cacc1f 236 * Find node by host name
e9491562 237 */
47cacc1f 238static Node *FindNodeByHostname(const char *hostName)
5039dede 239{
47cacc1f
VK
240 if (hostName[0] == 0)
241 return NULL;
5039dede 242
47cacc1f
VK
243 Node *node = NULL;
244 UINT32 ipAddr = ntohl(ResolveHostNameA(hostName));
245 if ((ipAddr != INADDR_NONE) && (ipAddr != INADDR_ANY))
5039dede 246 {
0eff2ce4 247 node = FindNodeByIP((g_flags & AF_TRAP_SOURCES_IN_ALL_ZONES) ? ALL_ZONES : 0, ipAddr);
5039dede 248 }
47cacc1f
VK
249
250 if (node == NULL)
251 {
e0f99bf0 252#ifdef UNICODE
47cacc1f
VK
253 WCHAR wname[MAX_OBJECT_NAME];
254 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, hostName, -1, wname, MAX_OBJECT_NAME);
255 wname[MAX_OBJECT_NAME - 1] = 0;
256 node = (Node *)FindObjectByName(wname, OBJECT_NODE);
e0f99bf0 257#else
47cacc1f 258 node = (Node *)FindObjectByName(hostName, OBJECT_NODE);
e0f99bf0 259#endif
5039dede 260 }
47cacc1f
VK
261 return node;
262}
5039dede 263
47cacc1f
VK
264/**
265 * Bind syslog message to NetXMS node object
266 * dwSourceIP is an IP address from which we receive message
267 */
268static Node *BindMsgToNode(NX_SYSLOG_RECORD *pRec, UINT32 dwSourceIP)
269{
270 Node *node = NULL;
3aa66b65 271
47cacc1f 272 if (s_nodeMatchingPolicy == SOURCE_IP_THEN_HOSTNAME)
299f1678 273 {
0eff2ce4 274 node = FindNodeByIP((g_flags & AF_TRAP_SOURCES_IN_ALL_ZONES) ? ALL_ZONES : 0, dwSourceIP);
47cacc1f
VK
275 if (node == NULL)
276 {
277 node = FindNodeByHostname(pRec->szHostName);
278 }
279 }
280 else
281 {
282 node = FindNodeByHostname(pRec->szHostName);
283 if (node == NULL)
284 {
0eff2ce4 285 node = FindNodeByIP((g_flags & AF_TRAP_SOURCES_IN_ALL_ZONES) ? ALL_ZONES : 0, dwSourceIP);
47cacc1f 286 }
299f1678
VK
287 }
288
47cacc1f 289 if (node != NULL)
3aa66b65 290 {
c42b4551 291 pRec->dwSourceObject = node->getId();
3aa66b65 292 if (pRec->szHostName[0] == 0)
e0f99bf0
VK
293 {
294#ifdef UNICODE
c42b4551 295 WideCharToMultiByte(CP_ACP, WC_DEFAULTCHAR | WC_COMPOSITECHECK, node->getName(), -1, pRec->szHostName, MAX_SYSLOG_HOSTNAME_LEN, NULL, NULL);
e0f99bf0
VK
296 pRec->szHostName[MAX_SYSLOG_HOSTNAME_LEN - 1] = 0;
297#else
c42b4551 298 nx_strncpy(pRec->szHostName, node->getName(), MAX_SYSLOG_HOSTNAME_LEN);
e0f99bf0
VK
299#endif
300 }
3aa66b65
VK
301 }
302 else
303 {
304 if (pRec->szHostName[0] == 0)
e0f99bf0 305 IpToStrA(dwSourceIP, pRec->szHostName);
5039dede 306 }
f8106e8b 307
47cacc1f 308 return node;
5039dede
AK
309}
310
e9491562
VK
311/**
312 * Handler for EnumerateSessions()
313 */
5039dede
AK
314static void BroadcastSyslogMessage(ClientSession *pSession, void *pArg)
315{
e05b1945 316 if (pSession->isAuthenticated())
e0f99bf0 317 pSession->onSyslogMessage((NX_SYSLOG_RECORD *)pArg);
5039dede
AK
318}
319
1499848d
VK
320/**
321 * Syslog writer thread
322 */
323static THREAD_RESULT THREAD_CALL SyslogWriterThread(void *arg)
324{
325 DbgPrintf(1, _T("Syslog writer thread started"));
326 while(true)
327 {
19dbc8ef 328 NX_SYSLOG_RECORD *r = (NX_SYSLOG_RECORD *)g_syslogWriteQueue.getOrBlock();
1499848d
VK
329 if (r == INVALID_POINTER_VALUE)
330 break;
331
332 DB_HANDLE hdb = DBConnectionPoolAcquireConnection();
333
334 DB_STATEMENT hStmt = DBPrepare(hdb, _T("INSERT INTO syslog (msg_id,msg_timestamp,facility,severity,source_object_id,hostname,msg_tag,msg_text) VALUES (?,?,?,?,?,?,?,?)"));
335 if (hStmt == NULL)
336 {
337 free(r);
338 continue;
339 }
340
341 int count = 0;
342 DBBegin(hdb);
343 while(true)
344 {
345 DBBind(hStmt, 1, DB_SQLTYPE_BIGINT, r->qwMsgId);
346 DBBind(hStmt, 2, DB_SQLTYPE_INTEGER, (INT32)r->tmTimeStamp);
347 DBBind(hStmt, 3, DB_SQLTYPE_INTEGER, r->nFacility);
348 DBBind(hStmt, 4, DB_SQLTYPE_INTEGER, r->nSeverity);
349 DBBind(hStmt, 5, DB_SQLTYPE_INTEGER, r->dwSourceObject);
350#ifdef UNICODE
351 DBBind(hStmt, 6, DB_SQLTYPE_VARCHAR, WideStringFromMBString(r->szHostName), DB_BIND_DYNAMIC);
352 DBBind(hStmt, 7, DB_SQLTYPE_VARCHAR, WideStringFromMBString(r->szTag), DB_BIND_DYNAMIC);
353 DBBind(hStmt, 8, DB_SQLTYPE_VARCHAR, WideStringFromMBString(r->szMessage), DB_BIND_DYNAMIC);
354#else
355 DBBind(hStmt, 6, DB_SQLTYPE_VARCHAR, r->szHostName, DB_BIND_STATIC);
356 DBBind(hStmt, 7, DB_SQLTYPE_VARCHAR, r->szTag, DB_BIND_STATIC);
357 DBBind(hStmt, 8, DB_SQLTYPE_VARCHAR, r->szMessage, DB_BIND_STATIC);
358#endif
359
360 if (!DBExecute(hStmt))
361 {
362 free(r);
363 break;
364 }
365 free(r);
366 count++;
367 if (count == 1000)
368 break;
19dbc8ef 369 r = (NX_SYSLOG_RECORD *)g_syslogWriteQueue.get();
1499848d
VK
370 if ((r == NULL) || (r == INVALID_POINTER_VALUE))
371 break;
372 }
373 DBCommit(hdb);
374 DBFreeStatement(hStmt);
375 DBConnectionPoolReleaseConnection(hdb);
376 if (r == INVALID_POINTER_VALUE)
377 break;
378 }
379 DbgPrintf(1, _T("Syslog writer thread stopped"));
380 return THREAD_OK;
381}
382
e9491562
VK
383/**
384 * Process syslog message
385 */
967893bb 386static void ProcessSyslogMessage(char *psMsg, int nMsgLen, UINT32 dwSourceIP)
5039dede 387{
e0f99bf0 388 NX_SYSLOG_RECORD record;
5039dede 389
271c3551 390 DbgPrintf(6, _T("ProcessSyslogMessage: Raw syslog message to process:\n%hs"), psMsg);
5039dede
AK
391 if (ParseSyslogMessage(psMsg, nMsgLen, &record))
392 {
1499848d 393 record.qwMsgId = s_msgId++;
f8106e8b 394 Node *node = BindMsgToNode(&record, dwSourceIP);
1499848d 395
19dbc8ef 396 g_syslogWriteQueue.put(nx_memdup(&record, sizeof(NX_SYSLOG_RECORD)));
5039dede
AK
397
398 // Send message to all connected clients
399 EnumerateClientSessions(BroadcastSyslogMessage, &record);
400
271c3551
VK
401 TCHAR ipAddr[32];
402 DbgPrintf(6, _T("Syslog message: ipAddr=%s objectId=%d tag=\"%hs\" msg=\"%hs\""),
403 IpToStr(dwSourceIP, ipAddr), record.dwSourceObject, record.szTag, record.szMessage);
404
1499848d 405 MutexLock(s_parserLock);
f8106e8b
VK
406 if ((record.dwSourceObject != 0) && (s_parser != NULL) &&
407 ((node->Status() != STATUS_UNMANAGED) || (g_flags & AF_TRAPS_FROM_UNMANAGED_NODES)))
5039dede 408 {
6646dca4
VK
409#ifdef UNICODE
410 WCHAR wtag[MAX_SYSLOG_TAG_LEN];
411 WCHAR wmsg[MAX_LOG_MSG_LENGTH];
412 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, record.szTag, -1, wtag, MAX_SYSLOG_TAG_LEN);
413 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, record.szMessage, -1, wmsg, MAX_LOG_MSG_LENGTH);
1499848d 414 s_parser->matchEvent(wtag, record.nFacility, 1 << record.nSeverity, wmsg, record.dwSourceObject);
6646dca4 415#else
1499848d 416 s_parser->matchEvent(record.szTag, record.nFacility, 1 << record.nSeverity, record.szMessage, record.dwSourceObject);
6646dca4 417#endif
5039dede 418 }
1499848d 419 MutexUnlock(s_parserLock);
5039dede 420 }
271c3551
VK
421 else
422 {
423 DbgPrintf(6, _T("ProcessSyslogMessage: Cannot parse syslog message"));
424 }
5039dede
AK
425}
426
e9491562
VK
427/**
428 * Syslog processing thread
429 */
5039dede
AK
430static THREAD_RESULT THREAD_CALL SyslogProcessingThread(void *pArg)
431{
432 QUEUED_SYSLOG_MESSAGE *pMsg;
433
434 while(1)
435 {
19dbc8ef 436 pMsg = (QUEUED_SYSLOG_MESSAGE *)g_syslogProcessingQueue.getOrBlock();
5039dede
AK
437 if (pMsg == INVALID_POINTER_VALUE)
438 break;
439
440 ProcessSyslogMessage(pMsg->psMsg, pMsg->nBytes, pMsg->dwSourceIP);
441 free(pMsg->psMsg);
442 free(pMsg);
443 }
444 return THREAD_OK;
445}
446
271c3551
VK
447/**
448 * Queue syslog message for processing
449 */
967893bb 450static void QueueSyslogMessage(char *psMsg, int nMsgLen, UINT32 dwSourceIP)
5039dede
AK
451{
452 QUEUED_SYSLOG_MESSAGE *pMsg;
453
454 pMsg = (QUEUED_SYSLOG_MESSAGE *)malloc(sizeof(QUEUED_SYSLOG_MESSAGE));
455 pMsg->dwSourceIP = dwSourceIP;
456 pMsg->nBytes = nMsgLen;
271c3551 457 pMsg->psMsg = (char *)nx_memdup(psMsg, nMsgLen + 1);
19dbc8ef 458 g_syslogProcessingQueue.put(pMsg);
5039dede
AK
459}
460
271c3551
VK
461/**
462 * Callback for syslog parser
463 */
da623f8b
VK
464static void SyslogParserCallback(UINT32 eventCode, const TCHAR *eventName, const TCHAR *line,
465 const TCHAR *source, UINT32 facility, UINT32 severity,
466 int paramCount, TCHAR **params, UINT32 objectId, void *userArg)
5039dede
AK
467{
468 char format[] = "ssssssssssssssssssssssssssssssss";
fe1d4002 469 TCHAR *plist[32];
5039dede
AK
470 int i, count;
471
472 count = min(paramCount, 32);
473 format[count] = 0;
474 for(i = 0; i < count; i++)
475 plist[i] = params[i];
2dd24569 476 PostEvent(eventCode, objectId, format,
5039dede
AK
477 plist[0], plist[1], plist[2], plist[3],
478 plist[4], plist[5], plist[6], plist[7],
479 plist[8], plist[9], plist[10], plist[11],
480 plist[12], plist[13], plist[14], plist[15],
481 plist[16], plist[17], plist[18], plist[19],
482 plist[20], plist[21], plist[22], plist[23],
483 plist[24], plist[25], plist[26], plist[27],
484 plist[28], plist[29], plist[30], plist[31]);
485}
486
271c3551
VK
487/**
488 * Event name resolver
489 */
da623f8b 490static bool EventNameResolver(const TCHAR *name, UINT32 *code)
5039dede
AK
491{
492 EVENT_TEMPLATE *event;
4d0c32f3 493 bool success = false;
5039dede
AK
494
495 event = FindEventTemplateByName(name);
496 if (event != NULL)
497 {
498 *code = event->dwCode;
4d0c32f3 499 success = true;
5039dede
AK
500 }
501 return success;
502}
503
271c3551
VK
504/**
505 * Create syslog parser from config
506 */
4d0c32f3
VK
507static void CreateParserFromConfig()
508{
9f24efb3 509 char *xml;
4d0c32f3 510
1499848d
VK
511 MutexLock(s_parserLock);
512 delete_and_null(s_parser);
9f24efb3
VK
513#ifdef UNICODE
514 WCHAR *wxml = ConfigReadCLOB(_T("SyslogParser"), _T("<parser></parser>"));
515 if (wxml != NULL)
516 {
517 xml = UTF8StringFromWideString(wxml);
518 free(wxml);
519 }
520 else
521 {
522 xml = NULL;
523 }
524#else
6646dca4 525 xml = ConfigReadCLOB("SyslogParser", "<parser></parser>");
9f24efb3 526#endif
4d0c32f3
VK
527 if (xml != NULL)
528 {
6646dca4 529 TCHAR parseError[256];
271c3551
VK
530 ObjectArray<LogParser> *parsers = LogParser::createFromXml(xml, -1, parseError, 256, EventNameResolver);
531 if ((parsers != NULL) && (parsers->size() > 0))
4d0c32f3 532 {
1499848d
VK
533 s_parser = parsers->get(0);
534 s_parser->setCallback(SyslogParserCallback);
4d0c32f3
VK
535 DbgPrintf(3, _T("syslogd: parser successfully created from config"));
536 }
537 else
538 {
4d0c32f3
VK
539 nxlog_write(MSG_SYSLOG_PARSER_INIT_FAILED, EVENTLOG_ERROR_TYPE, "s", parseError);
540 }
541 free(xml);
271c3551 542 delete parsers;
4d0c32f3 543 }
1499848d 544 MutexUnlock(s_parserLock);
4d0c32f3
VK
545}
546
271c3551
VK
547/**
548 * Syslog messages receiver thread
549 */
5039dede
AK
550THREAD_RESULT THREAD_CALL SyslogDaemon(void *pArg)
551{
552 SOCKET hSocket;
553 struct sockaddr_in addr;
554 int nBytes, nPort, nRet;
555 socklen_t nAddrLen;
271c3551 556 char sMsg[MAX_SYSLOG_MSG_LEN + 1];
5039dede 557 DB_RESULT hResult;
5039dede
AK
558 fd_set rdfs;
559 struct timeval tv;
5039dede 560
d368074d
VK
561 s_nodeMatchingPolicy = (NodeMatchingPolicy)ConfigReadInt(_T("SyslogNodeMatchingPolicy"), SOURCE_IP_THEN_HOSTNAME);
562
5039dede
AK
563 // Determine first available message id
564 hResult = DBSelect(g_hCoreDB, _T("SELECT max(msg_id) FROM syslog"));
565 if (hResult != NULL)
566 {
567 if (DBGetNumRows(hResult) > 0)
568 {
1499848d 569 s_msgId = max(DBGetFieldUInt64(hResult, 0, 0) + 1, s_msgId);
5039dede
AK
570 }
571 DBFreeResult(hResult);
572 }
573
574 hSocket = socket(AF_INET, SOCK_DGRAM, 0);
4c0c75c7 575 if (hSocket == INVALID_SOCKET)
5039dede 576 {
35f836fe 577 nxlog_write(MSG_SOCKET_FAILED, EVENTLOG_ERROR_TYPE, "s", _T("SyslogDaemon"));
5039dede
AK
578 return THREAD_OK;
579 }
580
1ddf3f0c 581 SetSocketExclusiveAddrUse(hSocket);
5039dede
AK
582 SetSocketReuseFlag(hSocket);
583
584 // Get listen port number
585 nPort = ConfigReadInt(_T("SyslogListenPort"), 514);
586 if ((nPort < 1) || (nPort > 65535))
587 nPort = 514;
588
589 // Fill in local address structure
590 memset(&addr, 0, sizeof(struct sockaddr_in));
591 addr.sin_family = AF_INET;
5f6e8d09 592 addr.sin_addr.s_addr = !_tcscmp(g_szListenAddress, _T("*")) ? 0 : ResolveHostName(g_szListenAddress);
5039dede
AK
593 addr.sin_port = htons((WORD)nPort);
594
595 // Bind socket
596 if (bind(hSocket, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) != 0)
597 {
35f836fe 598 nxlog_write(MSG_BIND_ERROR, EVENTLOG_ERROR_TYPE, "dse", nPort, _T("SyslogDaemon"), WSAGetLastError());
5039dede
AK
599 closesocket(hSocket);
600 return THREAD_OK;
601 }
602 nxlog_write(MSG_LISTENING_FOR_SYSLOG, EVENTLOG_INFORMATION_TYPE, "ad", ntohl(addr.sin_addr.s_addr), nPort);
603
07a1156a
VK
604 SetLogParserTraceCallback(DbgPrintf2);
605 InitLogParserLibrary();
606
5039dede 607 // Create message parser
1499848d 608 s_parserLock = MutexCreate();
4d0c32f3 609 CreateParserFromConfig();
5039dede
AK
610
611 // Start processing thread
1499848d
VK
612 THREAD hProcessingThread = ThreadCreateEx(SyslogProcessingThread, 0, NULL);
613 THREAD hWriterThread = ThreadCreateEx(SyslogWriterThread, 0, NULL);
5039dede
AK
614
615 DbgPrintf(1, _T("Syslog Daemon started"));
616
617 // Wait for packets
89135050 618 while(!IsShutdownInProgress())
5039dede
AK
619 {
620 FD_ZERO(&rdfs);
621 FD_SET(hSocket, &rdfs);
622 tv.tv_sec = 1;
623 tv.tv_usec = 0;
624 nRet = select((int)hSocket + 1, &rdfs, NULL, NULL, &tv);
625 if (nRet > 0)
626 {
627 nAddrLen = sizeof(struct sockaddr_in);
1499848d 628 nBytes = recvfrom(hSocket, sMsg, MAX_SYSLOG_MSG_LEN, 0, (struct sockaddr *)&addr, &nAddrLen);
5039dede
AK
629 if (nBytes > 0)
630 {
271c3551 631 sMsg[nBytes] = 0;
5039dede
AK
632 QueueSyslogMessage(sMsg, nBytes, ntohl(addr.sin_addr.s_addr));
633 }
634 else
635 {
636 // Sleep on error
637 ThreadSleepMs(100);
638 }
639 }
640 else if (nRet == -1)
641 {
642 // Sleep on error
643 ThreadSleepMs(100);
644 }
645 }
646
647 // Stop processing thread
19dbc8ef 648 g_syslogProcessingQueue.put(INVALID_POINTER_VALUE);
5039dede 649 ThreadJoin(hProcessingThread);
1499848d
VK
650
651 // Stop writer thread - it must be done after processing thread already finished
19dbc8ef 652 g_syslogWriteQueue.put(INVALID_POINTER_VALUE);
1499848d
VK
653 ThreadJoin(hWriterThread);
654
655 delete s_parser;
07a1156a 656 CleanupLogParserLibrary();
5039dede
AK
657
658 DbgPrintf(1, _T("Syslog Daemon stopped"));
659 return THREAD_OK;
660}
661
271c3551
VK
662/**
663 * Create NXCP message from NX_SYSLOG_RECORD structure
664 */
b368969c 665void CreateMessageFromSyslogMsg(NXCPMessage *pMsg, NX_SYSLOG_RECORD *pRec)
5039dede 666{
967893bb 667 UINT32 dwId = VID_SYSLOG_MSG_BASE;
5039dede 668
b368969c
VK
669 pMsg->setField(VID_NUM_RECORDS, (UINT32)1);
670 pMsg->setField(dwId++, pRec->qwMsgId);
671 pMsg->setField(dwId++, (UINT32)pRec->tmTimeStamp);
672 pMsg->setField(dwId++, (WORD)pRec->nFacility);
673 pMsg->setField(dwId++, (WORD)pRec->nSeverity);
674 pMsg->setField(dwId++, pRec->dwSourceObject);
675 pMsg->setFieldFromMBString(dwId++, pRec->szHostName);
676 pMsg->setFieldFromMBString(dwId++, pRec->szTag);
677 pMsg->setFieldFromMBString(dwId++, pRec->szMessage);
5039dede 678}
4d0c32f3 679
271c3551
VK
680/**
681 * Reinitialize parser on configuration change
682 */
4d0c32f3
VK
683void ReinitializeSyslogParser()
684{
1499848d 685 if (s_parserLock == INVALID_MUTEX_HANDLE)
4d0c32f3
VK
686 return; // Syslog daemon not initialized
687 CreateParserFromConfig();
688}