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