Added support for CMD_SET_EVENT_INFO on server side
[public/netxms.git] / src / server / core / session.cpp
CommitLineData
21e4b6f0
VK
1/*
2** NetXMS - Network Management System
3** Copyright (C) 2003, 2004 Victor Kirhenshtein
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** $module: session.cpp
20**
21**/
22
23#include "nms_core.h"
24
25
26//
27// Client session class constructor
28//
29
b54b2b11 30ClientSession::ClientSession(SOCKET hSocket, DWORD dwHostAddr)
21e4b6f0
VK
31{
32 m_pSendQueue = new Queue;
33 m_pMessageQueue = new Queue;
62f5857f 34 m_pUpdateQueue = new Queue;
21e4b6f0
VK
35 m_hSocket = hSocket;
36 m_dwIndex = INVALID_INDEX;
37 m_iState = STATE_CONNECTED;
38 m_pMsgBuffer = (CSCP_BUFFER *)malloc(sizeof(CSCP_BUFFER));
ecb7e1ee
VK
39 m_hCondWriteThreadStopped = ConditionCreate();
40 m_hCondProcessingThreadStopped = ConditionCreate();
62f5857f
VK
41 m_hCondUpdateThreadStopped = ConditionCreate();
42 m_hMutexSendEvents = MutexCreate();
b54b2b11
VK
43 m_dwFlags = 0;
44 m_dwHostAddr = dwHostAddr;
45 strcpy(m_szUserName, "<not logged in>");
21e4b6f0
VK
46}
47
48
49//
50// Destructor
51//
52
53ClientSession::~ClientSession()
54{
55 shutdown(m_hSocket, 2);
56 closesocket(m_hSocket);
57 delete m_pSendQueue;
58 delete m_pMessageQueue;
62f5857f 59 delete m_pUpdateQueue;
21e4b6f0
VK
60 if (m_pMsgBuffer != NULL)
61 free(m_pMsgBuffer);
ecb7e1ee
VK
62 ConditionDestroy(m_hCondWriteThreadStopped);
63 ConditionDestroy(m_hCondProcessingThreadStopped);
62f5857f
VK
64 ConditionDestroy(m_hCondUpdateThreadStopped);
65 MutexDestroy(m_hMutexSendEvents);
b54b2b11
VK
66
67 // Unlock locked components
68 if (m_dwFlags & CSF_EVENT_DB_LOCKED)
69 UnlockComponent(CID_EVENT_DB);
21e4b6f0
VK
70}
71
72
73//
74// Print debug information
75//
76
77void ClientSession::DebugPrintf(char *szFormat, ...)
78{
79 if ((g_dwFlags & AF_STANDALONE) && (g_dwFlags & AF_DEBUG_CSCP))
80 {
81 va_list args;
82
83 printf("*CSCP(%d)* ", m_dwIndex);
84 va_start(args, szFormat);
85 vprintf(szFormat, args);
86 va_end(args);
87 }
88}
89
90
91//
92// Post message to send queue
93//
94
95void ClientSession::SendMessage(CSCPMessage *pMsg)
96{
97 m_pSendQueue->Put(pMsg->CreateMessage());
98}
99
100
101//
102// ReadThread()
103//
104
105void ClientSession::ReadThread(void)
106{
107 CSCP_MESSAGE *pRawMsg;
108 CSCPMessage *pMsg;
109 int iErr;
110
111 // Initialize raw message receiving function
112 RecvCSCPMessage(0, NULL, m_pMsgBuffer);
113
114 pRawMsg = (CSCP_MESSAGE *)malloc(65536);
115 while(1)
116 {
117 if ((iErr = RecvCSCPMessage(m_hSocket, pRawMsg, m_pMsgBuffer)) <= 0)
118 break;
119
120 // Check that actual received packet size is equal to encoded in packet
121 if (ntohs(pRawMsg->wSize) != iErr)
122 {
123 DebugPrintf("Actual message size doesn't match wSize value (%d,%d)\n", iErr, ntohs(pRawMsg->wSize));
124 continue; // Bad packet, wait for next
125 }
126
127 // Create message object from raw message
128 pMsg = new CSCPMessage(pRawMsg);
129 m_pMessageQueue->Put(pMsg);
130 }
131 if (iErr < 0)
132 WriteLog(MSG_SESSION_CLOSED, EVENTLOG_WARNING_TYPE, "e", WSAGetLastError());
133 free(pRawMsg);
134
135 // Notify other threads to exit
ecb7e1ee
VK
136 m_pSendQueue->Put(INVALID_POINTER_VALUE);
137 m_pMessageQueue->Put(INVALID_POINTER_VALUE);
138
139 // Wait for other threads to finish
140 ConditionWait(m_hCondWriteThreadStopped, INFINITE);
141 ConditionWait(m_hCondProcessingThreadStopped, INFINITE);
21e4b6f0
VK
142}
143
144
145//
146// WriteThread()
147//
148
149void ClientSession::WriteThread(void)
150{
151 CSCP_MESSAGE *pMsg;
152
153 while(1)
154 {
155 pMsg = (CSCP_MESSAGE *)m_pSendQueue->GetOrBlock();
ecb7e1ee 156 if (pMsg == INVALID_POINTER_VALUE) // Session termination indicator
21e4b6f0
VK
157 break;
158
159 if (send(m_hSocket, (const char *)pMsg, ntohs(pMsg->wSize), 0) <= 0)
160 {
7968a52d 161 MemFree(pMsg);
21e4b6f0
VK
162 break;
163 }
7968a52d 164 MemFree(pMsg);
21e4b6f0 165 }
ecb7e1ee 166 ConditionSet(m_hCondWriteThreadStopped);
21e4b6f0
VK
167}
168
169
170//
171// Message processing thread
172//
173
174void ClientSession::ProcessingThread(void)
175{
176 CSCPMessage *pMsg, *pReply;
177
178 while(1)
179 {
180 pMsg = (CSCPMessage *)m_pMessageQueue->GetOrBlock();
ecb7e1ee 181 if (pMsg == INVALID_POINTER_VALUE) // Session termination indicator
21e4b6f0
VK
182 break;
183
184 DebugPrintf("Received message with code %d\n", pMsg->GetCode());
185 if ((m_iState != STATE_AUTHENTICATED) && (pMsg->GetCode() != CMD_LOGIN))
186 {
187 delete pMsg;
188 continue;
189 }
190
191 switch(pMsg->GetCode())
192 {
193 case CMD_LOGIN:
194 if (m_iState != STATE_AUTHENTICATED)
195 {
0fdc2761 196 BYTE szPassword[SHA_DIGEST_LENGTH];
b54b2b11 197 char *pszLogin, szBuffer[16];
0fdc2761
VK
198
199 pszLogin = pMsg->GetVariableStr(VID_LOGIN_NAME);
200 pMsg->GetVariableBinary(VID_PASSWORD, szPassword, SHA_DIGEST_LENGTH);
21e4b6f0 201
0fdc2761 202 if (AuthenticateUser(pszLogin, szPassword, &m_dwUserId, &m_dwSystemAccess))
21e4b6f0
VK
203 m_iState = STATE_AUTHENTICATED;
204
b54b2b11
VK
205 if (m_iState == STATE_AUTHENTICATED)
206 {
207 sprintf(m_szUserName, "%s@%s", pszLogin, IpToStr(m_dwHostAddr, szBuffer));
208 }
209
7968a52d 210 MemFree(pszLogin);
21e4b6f0
VK
211
212 // Send reply
213 pReply = new CSCPMessage;
214 pReply->SetCode(CMD_LOGIN_RESP);
215 pReply->SetId(pMsg->GetId());
a5f8dbb8 216 pReply->SetVariable(VID_LOGIN_RESULT, (DWORD)(m_iState == STATE_AUTHENTICATED));
21e4b6f0
VK
217 SendMessage(pReply);
218 delete pReply;
219 }
220 else
221 {
222 }
223 break;
224 case CMD_GET_OBJECTS:
225 SendAllObjects();
226 break;
227 case CMD_GET_EVENTS:
228 SendAllEvents();
229 break;
230 case CMD_GET_CONFIG_VARLIST:
231 SendAllConfigVars();
232 break;
b54b2b11
VK
233 case CMD_OPEN_EVENT_DB:
234 SendEventDB(pMsg->GetId());
235 break;
236 case CMD_CLOSE_EVENT_DB:
237 if (m_dwFlags & CSF_EVENT_DB_LOCKED)
238 {
239 UnlockComponent(CID_EVENT_DB);
240 m_dwFlags &= ~CSF_EVENT_DB_LOCKED;
241 }
3a5042fd
VK
242 // Send reply
243 pReply = new CSCPMessage;
244 pReply->SetCode(CMD_REQUEST_COMPLETED);
245 pReply->SetId(pMsg->GetId());
246 pReply->SetVariable(VID_RCC, RCC_SUCCESS);
247 SendMessage(pReply);
248 delete pReply;
b54b2b11 249 break;
605d2931
VK
250 case CMD_SET_EVENT_INFO:
251 SetEventInfo(pMsg);
252 break;
21e4b6f0
VK
253 default:
254 break;
255 }
256 delete pMsg;
257 }
ecb7e1ee 258 ConditionSet(m_hCondProcessingThreadStopped);
21e4b6f0
VK
259}
260
261
b54b2b11
VK
262//
263// Send event configuration to client
264//
265
266void ClientSession::SendEventDB(DWORD dwRqId)
267{
268 DB_ASYNC_RESULT hResult;
269 CSCPMessage msg;
270 char szBuffer[1024];
271
272 // Prepare responce message
3a5042fd 273 msg.SetCode(CMD_REQUEST_COMPLETED);
b54b2b11
VK
274 msg.SetId(dwRqId);
275
276 if (!LockComponent(CID_EVENT_DB, m_dwIndex, m_szUserName, NULL, szBuffer))
277 {
278 msg.SetVariable(VID_RCC, RCC_COMPONENT_LOCKED);
279 msg.SetVariable(VID_LOCKED_BY, szBuffer);
280 SendMessage(&msg);
281 }
282 else
283 {
284 m_dwFlags |= CSF_EVENT_DB_LOCKED;
285
286 msg.SetVariable(VID_RCC, RCC_SUCCESS);
287 SendMessage(&msg);
288 msg.DeleteAllVariables();
289
290 // Prepare data message
291 msg.SetCode(CMD_EVENT_DB_RECORD);
292 msg.SetId(dwRqId);
293
294 hResult = DBAsyncSelect(g_hCoreDB, "SELECT id,name,severity,flags,message,description FROM events");
295 while(DBFetch(hResult))
296 {
297 msg.SetVariable(VID_EVENT_ID, DBGetFieldAsyncULong(hResult, 0));
298 msg.SetVariable(VID_NAME, DBGetFieldAsync(hResult, 1, szBuffer, 1024));
299 msg.SetVariable(VID_SEVERITY, DBGetFieldAsyncULong(hResult, 2));
300 msg.SetVariable(VID_FLAGS, DBGetFieldAsyncULong(hResult, 3));
301 msg.SetVariable(VID_MESSAGE, DBGetFieldAsync(hResult, 4, szBuffer, 1024));
302 msg.SetVariable(VID_DESCRIPTION, DBGetFieldAsync(hResult, 5, szBuffer, 1024));
303 SendMessage(&msg);
304 msg.DeleteAllVariables();
305 }
306 DBFreeAsyncResult(hResult);
3a5042fd
VK
307
308 // Send end-of-list indicator
309 msg.SetCode(CMD_EVENT_DB_EOF);
310 SendMessage(&msg);
b54b2b11
VK
311 }
312}
313
314
21e4b6f0
VK
315//
316// Send all objects to client
317//
318
319void ClientSession::SendAllObjects(void)
320{
321 DWORD i;
322 CSCPMessage msg;
323
324 // Prepare message
325 msg.SetCode(CMD_OBJECT);
326
327 // Send objects, one per message
328 ObjectsGlobalLock();
329 for(i = 0; i < g_dwIdIndexSize; i++)
330 {
331 g_pIndexById[i].pObject->CreateMessage(&msg);
332 SendMessage(&msg);
333 msg.DeleteAllVariables();
334 }
335 ObjectsGlobalUnlock();
336
337 // Send end of list notification
338 msg.SetCode(CMD_OBJECT_LIST_END);
339 SendMessage(&msg);
340}
341
342
343//
344// Send all events to client
345//
346
347void ClientSession::SendAllEvents(void)
348{
21e4b6f0 349 CSCPMessage msg;
20177e8e 350 DB_ASYNC_RESULT hResult;
9c36ef66 351 NXC_EVENT event;
21e4b6f0 352
62f5857f
VK
353 MutexLock(m_hMutexSendEvents, INFINITE);
354
21e4b6f0 355 // Retrieve events from database
20177e8e 356 hResult = DBAsyncSelect(g_hCoreDB, "SELECT event_id,timestamp,source,severity,message FROM EventLog ORDER BY timestamp");
21e4b6f0
VK
357 if (hResult != NULL)
358 {
359 // Send events, one per message
20177e8e 360 while(DBFetch(hResult))
21e4b6f0 361 {
20177e8e
VK
362 event.dwEventId = htonl(DBGetFieldAsyncULong(hResult, 0));
363 event.dwTimeStamp = htonl(DBGetFieldAsyncULong(hResult, 1));
364 event.dwSourceId = htonl(DBGetFieldAsyncULong(hResult, 2));
365 event.dwSeverity = htonl(DBGetFieldAsyncULong(hResult, 3));
366 DBGetFieldAsync(hResult, 4, event.szMessage, MAX_EVENT_MSG_LENGTH);
9c36ef66
VK
367 m_pSendQueue->Put(CreateRawCSCPMessage(CMD_EVENT, 0, sizeof(NXC_EVENT), &event, NULL));
368 }
20177e8e 369 DBFreeAsyncResult(hResult);
21e4b6f0
VK
370 }
371
372 // Send end of list notification
373 msg.SetCode(CMD_EVENT_LIST_END);
374 SendMessage(&msg);
62f5857f
VK
375
376 MutexUnlock(m_hMutexSendEvents);
21e4b6f0
VK
377}
378
379
380//
381// Send all configuration variables to client
382//
383
384void ClientSession::SendAllConfigVars(void)
385{
386 DWORD i, dwNumRecords;
387 CSCPMessage msg;
388 DB_RESULT hResult;
389
390 // Check user rights
391 if ((m_dwUserId != 0) && ((m_dwSystemAccess & SYSTEM_ACCESS_VIEW_CONFIG) == 0))
392 {
393 // Access denied
394 msg.SetCode(CMD_CONFIG_VARLIST_END);
a5f8dbb8 395 msg.SetVariable(VID_ERROR, (DWORD)1);
21e4b6f0
VK
396 SendMessage(&msg);
397 }
398 else
399 {
400 // Prepare message
401 msg.SetCode(CMD_CONFIG_VARIABLE);
402
403 // Retrieve configuration variables from database
404 hResult = DBSelect(g_hCoreDB, "SELECT name,value FROM config");
405 if (hResult != NULL)
406 {
407 // Send events, one per message
408 dwNumRecords = DBGetNumRows(hResult);
409 for(i = 0; i < dwNumRecords; i++)
410 {
a5f8dbb8
VK
411 msg.SetVariable(VID_NAME, DBGetField(hResult, i, 0));
412 msg.SetVariable(VID_VALUE, DBGetField(hResult, i, 1));
21e4b6f0
VK
413 SendMessage(&msg);
414 msg.DeleteAllVariables();
415 }
20177e8e 416 DBFreeResult(hResult);
21e4b6f0
VK
417 }
418
419 // Send end of list notification
420 msg.SetCode(CMD_CONFIG_VARLIST_END);
a5f8dbb8 421 msg.SetVariable(VID_ERROR, (DWORD)0);
21e4b6f0
VK
422 SendMessage(&msg);
423 }
424}
20177e8e
VK
425
426
427//
428// Close session forcibly
429//
430
431void ClientSession::Kill(void)
432{
433 // We shutdown socket connection, which will cause
434 // read thread to stop, and other threads will follow
435 shutdown(m_hSocket, 2);
436}
62f5857f
VK
437
438
439//
440// Handler for new events
441//
442
443void ClientSession::OnNewEvent(Event *pEvent)
444{
445 UPDATE_INFO *pUpdate;
446
447 pUpdate = (UPDATE_INFO *)malloc(sizeof(UPDATE_INFO));
448 pUpdate->dwCategory = INFO_CAT_EVENT;
449 pUpdate->pData = malloc(sizeof(NXC_EVENT));
450 pEvent->PrepareMessage((NXC_EVENT *)pUpdate->pData);
451 m_pUpdateQueue->Put(pUpdate);
452}
453
454
455//
456// Handler for object changes
457//
458
459void ClientSession::OnObjectChange(DWORD dwObjectId)
460{
461}
462
463
464//
465// Update processing thread
466//
467
468void ClientSession::UpdateThread(void)
469{
470 UPDATE_INFO *pUpdate;
471
472 while(1)
473 {
474 pUpdate = (UPDATE_INFO *)m_pUpdateQueue->GetOrBlock();
475 if (pUpdate == INVALID_POINTER_VALUE) // Session termination indicator
476 break;
477
478 switch(pUpdate->dwCategory)
479 {
480 case INFO_CAT_EVENT:
481 MutexLock(m_hMutexSendEvents, INFINITE);
482 m_pSendQueue->Put(CreateRawCSCPMessage(CMD_EVENT, 0, sizeof(NXC_EVENT), pUpdate->pData, NULL));
483 MutexUnlock(m_hMutexSendEvents);
484 free(pUpdate->pData);
485 break;
486 default:
487 break;
488 }
489
490 free(pUpdate);
491 }
492 ConditionSet(m_hCondUpdateThreadStopped);
493}
83f0529c
VK
494
495
496//
497// Send notification message to server
498//
499
500void ClientSession::Notify(DWORD dwCode)
501{
502 CSCPMessage msg;
503
504 msg.SetCode(CMD_NOTIFY);
505 msg.SetVariable(VID_NOTIFICATION_CODE, dwCode);
506 SendMessage(&msg);
507}
605d2931
VK
508
509
510//
511// Update event template
512//
513
514void ClientSession::SetEventInfo(CSCPMessage *pRequest)
515{
516 CSCPMessage msg;
517
518 // Prepare reply message
519 msg.SetCode(CMD_REQUEST_COMPLETED);
520 msg.SetId(pRequest->GetId());
521
522 // Check if we have event configuration database opened
523 if (!(m_dwFlags & CSF_EVENT_DB_LOCKED))
524 {
525 msg.SetVariable(VID_RCC, RCC_OUT_OF_STATE_REQUEST);
526 }
527 else
528 {
529 // Check access rights
530 if (CheckSysAccessRights(SYSTEM_ACCESS_EDIT_EVENT_DB))
531 {
532 char szQuery[4096], *pszName, *pszMessage, *pszDescription;
533 DWORD dwEventId;
534 BOOL bEventExist = FALSE;
535 DB_RESULT hResult;
536
537 // Check if event with specific id exists
538 dwEventId = pRequest->GetVariableLong(VID_EVENT_ID);
539 sprintf(szQuery, "SELECT event_id FROM events WHERE event_id=%ld", dwEventId);
540 hResult = DBSelect(g_hCoreDB, szQuery);
541 if (hResult != NULL)
542 {
543 if (DBGetNumRows(hResult) > 0)
544 bEventExist = TRUE;
545 DBFreeResult(hResult);
546 }
547
548 // Prepare SQL query
549 pszName = msg.GetVariableStr(VID_NAME);
550 pszMessage = msg.GetVariableStr(VID_MESSAGE);
551 pszDescription = msg.GetVariableStr(VID_DESCRIPTION);
552 if (bEventExist)
553 sprintf(szQuery, "UPDATE events SET name='%s',severity=%ld,flags=%ld,message='%s',description='%s' WHERE event_id=%ld",
554 pszName, msg.GetVariableLong(VID_SEVERITY), msg.GetVariableLong(VID_FLAGS),
555 pszMessage, pszDescription);
556 else
557 sprintf(szQuery, "INSERT INTO events SET event_id,name,severity,flags,message,description VALUES ",
558 if (DBQuery(g_hCoreDB, szQuery))
559 msg.SetVariable(VID_RCC, RCC_SUCCESS);
560 else
561 msg.SetVariable(VID_RCC, RCC_DB_FAILURE);
562
563 MemFree(pszName);
564 MemFree(pszMessage);
565 MemFree(pszDescription);
566 }
567 else
568 {
569 msg.SetVariable(VID_RCC, RCC_ACCESS_DENIED);
570 }
571 }
572
573 // Send responce
574 SendMessage(&msg);
575}