cd95f7731243c58524b4c1108f9c7d1b6cfbada6
[public/netxms.git] / src / server / core / session.cpp
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
30 ClientSession::ClientSession(SOCKET hSocket, DWORD dwHostAddr)
31 {
32 m_pSendQueue = new Queue;
33 m_pMessageQueue = new Queue;
34 m_pUpdateQueue = new Queue;
35 m_hSocket = hSocket;
36 m_dwIndex = INVALID_INDEX;
37 m_iState = STATE_CONNECTED;
38 m_pMsgBuffer = (CSCP_BUFFER *)malloc(sizeof(CSCP_BUFFER));
39 m_hCondWriteThreadStopped = ConditionCreate();
40 m_hCondProcessingThreadStopped = ConditionCreate();
41 m_hCondUpdateThreadStopped = ConditionCreate();
42 m_hMutexSendEvents = MutexCreate();
43 m_hMutexSendObjects = MutexCreate();
44 m_dwFlags = 0;
45 m_dwHostAddr = dwHostAddr;
46 strcpy(m_szUserName, "<not logged in>");
47 }
48
49
50 //
51 // Destructor
52 //
53
54 ClientSession::~ClientSession()
55 {
56 shutdown(m_hSocket, 2);
57 closesocket(m_hSocket);
58 delete m_pSendQueue;
59 delete m_pMessageQueue;
60 delete m_pUpdateQueue;
61 if (m_pMsgBuffer != NULL)
62 free(m_pMsgBuffer);
63 ConditionDestroy(m_hCondWriteThreadStopped);
64 ConditionDestroy(m_hCondProcessingThreadStopped);
65 ConditionDestroy(m_hCondUpdateThreadStopped);
66 MutexDestroy(m_hMutexSendEvents);
67 MutexDestroy(m_hMutexSendObjects);
68
69 // Unlock locked components
70 if (m_dwFlags & CSF_EVENT_DB_LOCKED)
71 UnlockComponent(CID_EVENT_DB);
72 }
73
74
75 //
76 // Print debug information
77 //
78
79 void 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
97 void ClientSession::SendMessage(CSCPMessage *pMsg)
98 {
99 m_pSendQueue->Put(pMsg->CreateMessage());
100 }
101
102
103 //
104 // ReadThread()
105 //
106
107 void 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
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);
144 }
145
146
147 //
148 // WriteThread()
149 //
150
151 void ClientSession::WriteThread(void)
152 {
153 CSCP_MESSAGE *pMsg;
154
155 while(1)
156 {
157 pMsg = (CSCP_MESSAGE *)m_pSendQueue->GetOrBlock();
158 if (pMsg == INVALID_POINTER_VALUE) // Session termination indicator
159 break;
160
161 if (send(m_hSocket, (const char *)pMsg, ntohs(pMsg->wSize), 0) <= 0)
162 {
163 MemFree(pMsg);
164 break;
165 }
166 MemFree(pMsg);
167 }
168 ConditionSet(m_hCondWriteThreadStopped);
169 }
170
171
172 //
173 // Message processing thread
174 //
175
176 void ClientSession::ProcessingThread(void)
177 {
178 CSCPMessage *pMsg, *pReply;
179
180 while(1)
181 {
182 pMsg = (CSCPMessage *)m_pMessageQueue->GetOrBlock();
183 if (pMsg == INVALID_POINTER_VALUE) // Session termination indicator
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 {
198 BYTE szPassword[SHA_DIGEST_LENGTH];
199 char *pszLogin, szBuffer[16];
200
201 pszLogin = pMsg->GetVariableStr(VID_LOGIN_NAME);
202 pMsg->GetVariableBinary(VID_PASSWORD, szPassword, SHA_DIGEST_LENGTH);
203
204 if (AuthenticateUser(pszLogin, szPassword, &m_dwUserId, &m_dwSystemAccess))
205 m_iState = STATE_AUTHENTICATED;
206
207 if (m_iState == STATE_AUTHENTICATED)
208 {
209 sprintf(m_szUserName, "%s@%s", pszLogin, IpToStr(m_dwHostAddr, szBuffer));
210 }
211
212 MemFree(pszLogin);
213
214 // Send reply
215 pReply = new CSCPMessage;
216 pReply->SetCode(CMD_LOGIN_RESP);
217 pReply->SetId(pMsg->GetId());
218 pReply->SetVariable(VID_LOGIN_RESULT, (DWORD)(m_iState == STATE_AUTHENTICATED));
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;
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 {
241 // Check if event configuration DB has been modified
242 if (m_dwFlags & CSF_EVENT_DB_MODIFIED)
243 ReloadEvents();
244 UnlockComponent(CID_EVENT_DB);
245 m_dwFlags &= ~CSF_EVENT_DB_LOCKED;
246 }
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;
254 break;
255 case CMD_SET_EVENT_INFO:
256 SetEventInfo(pMsg);
257 break;
258 default:
259 break;
260 }
261 delete pMsg;
262 }
263 ConditionSet(m_hCondProcessingThreadStopped);
264 }
265
266
267 //
268 // Send event configuration to client
269 //
270
271 void ClientSession::SendEventDB(DWORD dwRqId)
272 {
273 DB_ASYNC_RESULT hResult;
274 CSCPMessage msg;
275 char szBuffer[1024];
276
277 // Prepare responce message
278 msg.SetCode(CMD_REQUEST_COMPLETED);
279 msg.SetId(dwRqId);
280
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))
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;
295 m_dwFlags &= ~CSF_EVENT_DB_MODIFIED;
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
305 hResult = DBAsyncSelect(g_hCoreDB, "SELECT event_id,name,severity,flags,message,description FROM events");
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);
318
319 // Send end-of-list indicator
320 msg.SetCode(CMD_EVENT_DB_EOF);
321 SendMessage(&msg);
322 }
323 }
324
325
326 //
327 // Send all objects to client
328 //
329
330 void ClientSession::SendAllObjects(void)
331 {
332 DWORD i;
333 CSCPMessage msg;
334
335 MutexLock(m_hMutexSendObjects, INFINITE);
336
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);
353
354 MutexUnlock(m_hMutexSendObjects);
355 }
356
357
358 //
359 // Send all events to client
360 //
361
362 void ClientSession::SendAllEvents(void)
363 {
364 CSCPMessage msg;
365 DB_ASYNC_RESULT hResult;
366 NXC_EVENT event;
367
368 MutexLock(m_hMutexSendEvents, INFINITE);
369
370 // Retrieve events from database
371 hResult = DBAsyncSelect(g_hCoreDB, "SELECT event_id,timestamp,source,severity,message FROM EventLog ORDER BY timestamp");
372 if (hResult != NULL)
373 {
374 // Send events, one per message
375 while(DBFetch(hResult))
376 {
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);
382 m_pSendQueue->Put(CreateRawCSCPMessage(CMD_EVENT, 0, sizeof(NXC_EVENT), &event, NULL));
383 }
384 DBFreeAsyncResult(hResult);
385 }
386
387 // Send end of list notification
388 msg.SetCode(CMD_EVENT_LIST_END);
389 SendMessage(&msg);
390
391 MutexUnlock(m_hMutexSendEvents);
392 }
393
394
395 //
396 // Send all configuration variables to client
397 //
398
399 void 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);
410 msg.SetVariable(VID_ERROR, (DWORD)1);
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 {
426 msg.SetVariable(VID_NAME, DBGetField(hResult, i, 0));
427 msg.SetVariable(VID_VALUE, DBGetField(hResult, i, 1));
428 SendMessage(&msg);
429 msg.DeleteAllVariables();
430 }
431 DBFreeResult(hResult);
432 }
433
434 // Send end of list notification
435 msg.SetCode(CMD_CONFIG_VARLIST_END);
436 msg.SetVariable(VID_ERROR, (DWORD)0);
437 SendMessage(&msg);
438 }
439 }
440
441
442 //
443 // Close session forcibly
444 //
445
446 void 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 }
452
453
454 //
455 // Handler for new events
456 //
457
458 void 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
474 void ClientSession::OnObjectChange(NetObj *pObject)
475 {
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();
483 }
484
485
486 //
487 // Update processing thread
488 //
489
490 void ClientSession::UpdateThread(void)
491 {
492 UPDATE_INFO *pUpdate;
493 CSCPMessage msg;
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;
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;
519 default:
520 break;
521 }
522
523 free(pUpdate);
524 }
525 ConditionSet(m_hCondUpdateThreadStopped);
526 }
527
528
529 //
530 // Send notification message to server
531 //
532
533 void 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 }
541
542
543 //
544 // Update event template
545 //
546
547 void 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
581 // Prepare and execute SQL query
582 pszName = pRequest->GetVariableStr(VID_NAME);
583 pszMessage = pRequest->GetVariableStr(VID_MESSAGE);
584 pszDescription = pRequest->GetVariableStr(VID_DESCRIPTION);
585 if (bEventExist)
586 sprintf(szQuery, "UPDATE events SET name='%s',severity=%ld,flags=%ld,message='%s',description='%s' WHERE event_id=%ld",
587 pszName, pRequest->GetVariableLong(VID_SEVERITY), pRequest->GetVariableLong(VID_FLAGS),
588 pszMessage, pszDescription, dwEventId);
589 else
590 sprintf(szQuery, "INSERT INTO events SET event_id,name,severity,flags,message,description VALUES (%ld,'%s',%ld,%ld,'%s','%s')",
591 dwEventId, pszName, pRequest->GetVariableLong(VID_SEVERITY),
592 pRequest->GetVariableLong(VID_FLAGS), pszMessage, pszDescription);
593 if (DBQuery(g_hCoreDB, szQuery))
594 {
595 msg.SetVariable(VID_RCC, RCC_SUCCESS);
596 m_dwFlags |= CSF_EVENT_DB_MODIFIED;
597 }
598 else
599 {
600 msg.SetVariable(VID_RCC, RCC_DB_FAILURE);
601 }
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 }