Added support for CMD_OBJECT_UPDATE message
[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 {
b8bad201
VK
239 // Check if event configuration DB has been modified
240 if (m_dwFlags & CSF_EVENT_DB_MODIFIED)
4d5a05a0 241 ReloadEvents();
b54b2b11
VK
242 UnlockComponent(CID_EVENT_DB);
243 m_dwFlags &= ~CSF_EVENT_DB_LOCKED;
244 }
3a5042fd
VK
245 // Send reply
246 pReply = new CSCPMessage;
247 pReply->SetCode(CMD_REQUEST_COMPLETED);
248 pReply->SetId(pMsg->GetId());
249 pReply->SetVariable(VID_RCC, RCC_SUCCESS);
250 SendMessage(pReply);
251 delete pReply;
b54b2b11 252 break;
605d2931
VK
253 case CMD_SET_EVENT_INFO:
254 SetEventInfo(pMsg);
255 break;
21e4b6f0
VK
256 default:
257 break;
258 }
259 delete pMsg;
260 }
ecb7e1ee 261 ConditionSet(m_hCondProcessingThreadStopped);
21e4b6f0
VK
262}
263
264
b54b2b11
VK
265//
266// Send event configuration to client
267//
268
269void ClientSession::SendEventDB(DWORD dwRqId)
270{
271 DB_ASYNC_RESULT hResult;
272 CSCPMessage msg;
273 char szBuffer[1024];
274
275 // Prepare responce message
3a5042fd 276 msg.SetCode(CMD_REQUEST_COMPLETED);
b54b2b11
VK
277 msg.SetId(dwRqId);
278
b8bad201
VK
279 if (!CheckSysAccessRights(SYSTEM_ACCESS_VIEW_EVENT_DB))
280 {
281 msg.SetVariable(VID_RCC, RCC_ACCESS_DENIED);
282 SendMessage(&msg);
283 }
284 else if (!LockComponent(CID_EVENT_DB, m_dwIndex, m_szUserName, NULL, szBuffer))
b54b2b11
VK
285 {
286 msg.SetVariable(VID_RCC, RCC_COMPONENT_LOCKED);
287 msg.SetVariable(VID_LOCKED_BY, szBuffer);
288 SendMessage(&msg);
289 }
290 else
291 {
292 m_dwFlags |= CSF_EVENT_DB_LOCKED;
b8bad201 293 m_dwFlags &= ~CSF_EVENT_DB_MODIFIED;
b54b2b11
VK
294
295 msg.SetVariable(VID_RCC, RCC_SUCCESS);
296 SendMessage(&msg);
297 msg.DeleteAllVariables();
298
299 // Prepare data message
300 msg.SetCode(CMD_EVENT_DB_RECORD);
301 msg.SetId(dwRqId);
302
eafa21c7 303 hResult = DBAsyncSelect(g_hCoreDB, "SELECT event_id,name,severity,flags,message,description FROM events");
b54b2b11
VK
304 while(DBFetch(hResult))
305 {
306 msg.SetVariable(VID_EVENT_ID, DBGetFieldAsyncULong(hResult, 0));
307 msg.SetVariable(VID_NAME, DBGetFieldAsync(hResult, 1, szBuffer, 1024));
308 msg.SetVariable(VID_SEVERITY, DBGetFieldAsyncULong(hResult, 2));
309 msg.SetVariable(VID_FLAGS, DBGetFieldAsyncULong(hResult, 3));
310 msg.SetVariable(VID_MESSAGE, DBGetFieldAsync(hResult, 4, szBuffer, 1024));
311 msg.SetVariable(VID_DESCRIPTION, DBGetFieldAsync(hResult, 5, szBuffer, 1024));
312 SendMessage(&msg);
313 msg.DeleteAllVariables();
314 }
315 DBFreeAsyncResult(hResult);
3a5042fd
VK
316
317 // Send end-of-list indicator
318 msg.SetCode(CMD_EVENT_DB_EOF);
319 SendMessage(&msg);
b54b2b11
VK
320 }
321}
322
323
21e4b6f0
VK
324//
325// Send all objects to client
326//
327
328void ClientSession::SendAllObjects(void)
329{
330 DWORD i;
331 CSCPMessage msg;
332
333 // Prepare message
334 msg.SetCode(CMD_OBJECT);
335
336 // Send objects, one per message
337 ObjectsGlobalLock();
338 for(i = 0; i < g_dwIdIndexSize; i++)
339 {
340 g_pIndexById[i].pObject->CreateMessage(&msg);
341 SendMessage(&msg);
342 msg.DeleteAllVariables();
343 }
344 ObjectsGlobalUnlock();
345
346 // Send end of list notification
347 msg.SetCode(CMD_OBJECT_LIST_END);
348 SendMessage(&msg);
349}
350
351
352//
353// Send all events to client
354//
355
356void ClientSession::SendAllEvents(void)
357{
21e4b6f0 358 CSCPMessage msg;
20177e8e 359 DB_ASYNC_RESULT hResult;
9c36ef66 360 NXC_EVENT event;
21e4b6f0 361
62f5857f
VK
362 MutexLock(m_hMutexSendEvents, INFINITE);
363
21e4b6f0 364 // Retrieve events from database
20177e8e 365 hResult = DBAsyncSelect(g_hCoreDB, "SELECT event_id,timestamp,source,severity,message FROM EventLog ORDER BY timestamp");
21e4b6f0
VK
366 if (hResult != NULL)
367 {
368 // Send events, one per message
20177e8e 369 while(DBFetch(hResult))
21e4b6f0 370 {
20177e8e
VK
371 event.dwEventId = htonl(DBGetFieldAsyncULong(hResult, 0));
372 event.dwTimeStamp = htonl(DBGetFieldAsyncULong(hResult, 1));
373 event.dwSourceId = htonl(DBGetFieldAsyncULong(hResult, 2));
374 event.dwSeverity = htonl(DBGetFieldAsyncULong(hResult, 3));
375 DBGetFieldAsync(hResult, 4, event.szMessage, MAX_EVENT_MSG_LENGTH);
9c36ef66
VK
376 m_pSendQueue->Put(CreateRawCSCPMessage(CMD_EVENT, 0, sizeof(NXC_EVENT), &event, NULL));
377 }
20177e8e 378 DBFreeAsyncResult(hResult);
21e4b6f0
VK
379 }
380
381 // Send end of list notification
382 msg.SetCode(CMD_EVENT_LIST_END);
383 SendMessage(&msg);
62f5857f
VK
384
385 MutexUnlock(m_hMutexSendEvents);
21e4b6f0
VK
386}
387
388
389//
390// Send all configuration variables to client
391//
392
393void ClientSession::SendAllConfigVars(void)
394{
395 DWORD i, dwNumRecords;
396 CSCPMessage msg;
397 DB_RESULT hResult;
398
399 // Check user rights
400 if ((m_dwUserId != 0) && ((m_dwSystemAccess & SYSTEM_ACCESS_VIEW_CONFIG) == 0))
401 {
402 // Access denied
403 msg.SetCode(CMD_CONFIG_VARLIST_END);
a5f8dbb8 404 msg.SetVariable(VID_ERROR, (DWORD)1);
21e4b6f0
VK
405 SendMessage(&msg);
406 }
407 else
408 {
409 // Prepare message
410 msg.SetCode(CMD_CONFIG_VARIABLE);
411
412 // Retrieve configuration variables from database
413 hResult = DBSelect(g_hCoreDB, "SELECT name,value FROM config");
414 if (hResult != NULL)
415 {
416 // Send events, one per message
417 dwNumRecords = DBGetNumRows(hResult);
418 for(i = 0; i < dwNumRecords; i++)
419 {
a5f8dbb8
VK
420 msg.SetVariable(VID_NAME, DBGetField(hResult, i, 0));
421 msg.SetVariable(VID_VALUE, DBGetField(hResult, i, 1));
21e4b6f0
VK
422 SendMessage(&msg);
423 msg.DeleteAllVariables();
424 }
20177e8e 425 DBFreeResult(hResult);
21e4b6f0
VK
426 }
427
428 // Send end of list notification
429 msg.SetCode(CMD_CONFIG_VARLIST_END);
a5f8dbb8 430 msg.SetVariable(VID_ERROR, (DWORD)0);
21e4b6f0
VK
431 SendMessage(&msg);
432 }
433}
20177e8e
VK
434
435
436//
437// Close session forcibly
438//
439
440void ClientSession::Kill(void)
441{
442 // We shutdown socket connection, which will cause
443 // read thread to stop, and other threads will follow
444 shutdown(m_hSocket, 2);
445}
62f5857f
VK
446
447
448//
449// Handler for new events
450//
451
452void ClientSession::OnNewEvent(Event *pEvent)
453{
454 UPDATE_INFO *pUpdate;
455
456 pUpdate = (UPDATE_INFO *)malloc(sizeof(UPDATE_INFO));
457 pUpdate->dwCategory = INFO_CAT_EVENT;
458 pUpdate->pData = malloc(sizeof(NXC_EVENT));
459 pEvent->PrepareMessage((NXC_EVENT *)pUpdate->pData);
460 m_pUpdateQueue->Put(pUpdate);
461}
462
463
464//
465// Handler for object changes
466//
467
468void ClientSession::OnObjectChange(DWORD dwObjectId)
469{
470}
471
472
473//
474// Update processing thread
475//
476
477void ClientSession::UpdateThread(void)
478{
479 UPDATE_INFO *pUpdate;
480
481 while(1)
482 {
483 pUpdate = (UPDATE_INFO *)m_pUpdateQueue->GetOrBlock();
484 if (pUpdate == INVALID_POINTER_VALUE) // Session termination indicator
485 break;
486
487 switch(pUpdate->dwCategory)
488 {
489 case INFO_CAT_EVENT:
490 MutexLock(m_hMutexSendEvents, INFINITE);
491 m_pSendQueue->Put(CreateRawCSCPMessage(CMD_EVENT, 0, sizeof(NXC_EVENT), pUpdate->pData, NULL));
492 MutexUnlock(m_hMutexSendEvents);
493 free(pUpdate->pData);
494 break;
495 default:
496 break;
497 }
498
499 free(pUpdate);
500 }
501 ConditionSet(m_hCondUpdateThreadStopped);
502}
83f0529c
VK
503
504
505//
506// Send notification message to server
507//
508
509void ClientSession::Notify(DWORD dwCode)
510{
511 CSCPMessage msg;
512
513 msg.SetCode(CMD_NOTIFY);
514 msg.SetVariable(VID_NOTIFICATION_CODE, dwCode);
515 SendMessage(&msg);
516}
605d2931
VK
517
518
519//
520// Update event template
521//
522
523void ClientSession::SetEventInfo(CSCPMessage *pRequest)
524{
525 CSCPMessage msg;
526
527 // Prepare reply message
528 msg.SetCode(CMD_REQUEST_COMPLETED);
529 msg.SetId(pRequest->GetId());
530
531 // Check if we have event configuration database opened
532 if (!(m_dwFlags & CSF_EVENT_DB_LOCKED))
533 {
534 msg.SetVariable(VID_RCC, RCC_OUT_OF_STATE_REQUEST);
535 }
536 else
537 {
538 // Check access rights
539 if (CheckSysAccessRights(SYSTEM_ACCESS_EDIT_EVENT_DB))
540 {
541 char szQuery[4096], *pszName, *pszMessage, *pszDescription;
542 DWORD dwEventId;
543 BOOL bEventExist = FALSE;
544 DB_RESULT hResult;
545
546 // Check if event with specific id exists
547 dwEventId = pRequest->GetVariableLong(VID_EVENT_ID);
548 sprintf(szQuery, "SELECT event_id FROM events WHERE event_id=%ld", dwEventId);
549 hResult = DBSelect(g_hCoreDB, szQuery);
550 if (hResult != NULL)
551 {
552 if (DBGetNumRows(hResult) > 0)
553 bEventExist = TRUE;
554 DBFreeResult(hResult);
555 }
556
b8bad201 557 // Prepare and execute SQL query
eafa21c7
VK
558 pszName = pRequest->GetVariableStr(VID_NAME);
559 pszMessage = pRequest->GetVariableStr(VID_MESSAGE);
560 pszDescription = pRequest->GetVariableStr(VID_DESCRIPTION);
605d2931
VK
561 if (bEventExist)
562 sprintf(szQuery, "UPDATE events SET name='%s',severity=%ld,flags=%ld,message='%s',description='%s' WHERE event_id=%ld",
eafa21c7 563 pszName, pRequest->GetVariableLong(VID_SEVERITY), pRequest->GetVariableLong(VID_FLAGS),
b8bad201 564 pszMessage, pszDescription, dwEventId);
605d2931 565 else
b8bad201 566 sprintf(szQuery, "INSERT INTO events SET event_id,name,severity,flags,message,description VALUES (%ld,'%s',%ld,%ld,'%s','%s')",
eafa21c7
VK
567 dwEventId, pszName, pRequest->GetVariableLong(VID_SEVERITY),
568 pRequest->GetVariableLong(VID_FLAGS), pszMessage, pszDescription);
605d2931 569 if (DBQuery(g_hCoreDB, szQuery))
b8bad201 570 {
605d2931 571 msg.SetVariable(VID_RCC, RCC_SUCCESS);
b8bad201
VK
572 m_dwFlags |= CSF_EVENT_DB_MODIFIED;
573 }
605d2931 574 else
b8bad201 575 {
605d2931 576 msg.SetVariable(VID_RCC, RCC_DB_FAILURE);
b8bad201 577 }
605d2931
VK
578
579 MemFree(pszName);
580 MemFree(pszMessage);
581 MemFree(pszDescription);
582 }
583 else
584 {
585 msg.SetVariable(VID_RCC, RCC_ACCESS_DENIED);
586 }
587 }
588
589 // Send responce
590 SendMessage(&msg);
591}