Unfinished changes in library structure
[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 }
242 break;
21e4b6f0
VK
243 default:
244 break;
245 }
246 delete pMsg;
247 }
ecb7e1ee 248 ConditionSet(m_hCondProcessingThreadStopped);
21e4b6f0
VK
249}
250
251
b54b2b11
VK
252//
253// Send event configuration to client
254//
255
256void ClientSession::SendEventDB(DWORD dwRqId)
257{
258 DB_ASYNC_RESULT hResult;
259 CSCPMessage msg;
260 char szBuffer[1024];
261
262 // Prepare responce message
263 msg.SetCode(CMD_OPEN_EVENT_DB);
264 msg.SetId(dwRqId);
265
266 if (!LockComponent(CID_EVENT_DB, m_dwIndex, m_szUserName, NULL, szBuffer))
267 {
268 msg.SetVariable(VID_RCC, RCC_COMPONENT_LOCKED);
269 msg.SetVariable(VID_LOCKED_BY, szBuffer);
270 SendMessage(&msg);
271 }
272 else
273 {
274 m_dwFlags |= CSF_EVENT_DB_LOCKED;
275
276 msg.SetVariable(VID_RCC, RCC_SUCCESS);
277 SendMessage(&msg);
278 msg.DeleteAllVariables();
279
280 // Prepare data message
281 msg.SetCode(CMD_EVENT_DB_RECORD);
282 msg.SetId(dwRqId);
283
284 hResult = DBAsyncSelect(g_hCoreDB, "SELECT id,name,severity,flags,message,description FROM events");
285 while(DBFetch(hResult))
286 {
287 msg.SetVariable(VID_EVENT_ID, DBGetFieldAsyncULong(hResult, 0));
288 msg.SetVariable(VID_NAME, DBGetFieldAsync(hResult, 1, szBuffer, 1024));
289 msg.SetVariable(VID_SEVERITY, DBGetFieldAsyncULong(hResult, 2));
290 msg.SetVariable(VID_FLAGS, DBGetFieldAsyncULong(hResult, 3));
291 msg.SetVariable(VID_MESSAGE, DBGetFieldAsync(hResult, 4, szBuffer, 1024));
292 msg.SetVariable(VID_DESCRIPTION, DBGetFieldAsync(hResult, 5, szBuffer, 1024));
293 SendMessage(&msg);
294 msg.DeleteAllVariables();
295 }
296 DBFreeAsyncResult(hResult);
297 }
298}
299
300
21e4b6f0
VK
301//
302// Send all objects to client
303//
304
305void ClientSession::SendAllObjects(void)
306{
307 DWORD i;
308 CSCPMessage msg;
309
310 // Prepare message
311 msg.SetCode(CMD_OBJECT);
312
313 // Send objects, one per message
314 ObjectsGlobalLock();
315 for(i = 0; i < g_dwIdIndexSize; i++)
316 {
317 g_pIndexById[i].pObject->CreateMessage(&msg);
318 SendMessage(&msg);
319 msg.DeleteAllVariables();
320 }
321 ObjectsGlobalUnlock();
322
323 // Send end of list notification
324 msg.SetCode(CMD_OBJECT_LIST_END);
325 SendMessage(&msg);
326}
327
328
329//
330// Send all events to client
331//
332
333void ClientSession::SendAllEvents(void)
334{
21e4b6f0 335 CSCPMessage msg;
20177e8e 336 DB_ASYNC_RESULT hResult;
9c36ef66 337 NXC_EVENT event;
21e4b6f0 338
62f5857f
VK
339 MutexLock(m_hMutexSendEvents, INFINITE);
340
21e4b6f0 341 // Retrieve events from database
20177e8e 342 hResult = DBAsyncSelect(g_hCoreDB, "SELECT event_id,timestamp,source,severity,message FROM EventLog ORDER BY timestamp");
21e4b6f0
VK
343 if (hResult != NULL)
344 {
345 // Send events, one per message
20177e8e 346 while(DBFetch(hResult))
21e4b6f0 347 {
20177e8e
VK
348 event.dwEventId = htonl(DBGetFieldAsyncULong(hResult, 0));
349 event.dwTimeStamp = htonl(DBGetFieldAsyncULong(hResult, 1));
350 event.dwSourceId = htonl(DBGetFieldAsyncULong(hResult, 2));
351 event.dwSeverity = htonl(DBGetFieldAsyncULong(hResult, 3));
352 DBGetFieldAsync(hResult, 4, event.szMessage, MAX_EVENT_MSG_LENGTH);
9c36ef66
VK
353 m_pSendQueue->Put(CreateRawCSCPMessage(CMD_EVENT, 0, sizeof(NXC_EVENT), &event, NULL));
354 }
20177e8e 355 DBFreeAsyncResult(hResult);
21e4b6f0
VK
356 }
357
358 // Send end of list notification
359 msg.SetCode(CMD_EVENT_LIST_END);
360 SendMessage(&msg);
62f5857f
VK
361
362 MutexUnlock(m_hMutexSendEvents);
21e4b6f0
VK
363}
364
365
366//
367// Send all configuration variables to client
368//
369
370void ClientSession::SendAllConfigVars(void)
371{
372 DWORD i, dwNumRecords;
373 CSCPMessage msg;
374 DB_RESULT hResult;
375
376 // Check user rights
377 if ((m_dwUserId != 0) && ((m_dwSystemAccess & SYSTEM_ACCESS_VIEW_CONFIG) == 0))
378 {
379 // Access denied
380 msg.SetCode(CMD_CONFIG_VARLIST_END);
a5f8dbb8 381 msg.SetVariable(VID_ERROR, (DWORD)1);
21e4b6f0
VK
382 SendMessage(&msg);
383 }
384 else
385 {
386 // Prepare message
387 msg.SetCode(CMD_CONFIG_VARIABLE);
388
389 // Retrieve configuration variables from database
390 hResult = DBSelect(g_hCoreDB, "SELECT name,value FROM config");
391 if (hResult != NULL)
392 {
393 // Send events, one per message
394 dwNumRecords = DBGetNumRows(hResult);
395 for(i = 0; i < dwNumRecords; i++)
396 {
a5f8dbb8
VK
397 msg.SetVariable(VID_NAME, DBGetField(hResult, i, 0));
398 msg.SetVariable(VID_VALUE, DBGetField(hResult, i, 1));
21e4b6f0
VK
399 SendMessage(&msg);
400 msg.DeleteAllVariables();
401 }
20177e8e 402 DBFreeResult(hResult);
21e4b6f0
VK
403 }
404
405 // Send end of list notification
406 msg.SetCode(CMD_CONFIG_VARLIST_END);
a5f8dbb8 407 msg.SetVariable(VID_ERROR, (DWORD)0);
21e4b6f0
VK
408 SendMessage(&msg);
409 }
410}
20177e8e
VK
411
412
413//
414// Close session forcibly
415//
416
417void ClientSession::Kill(void)
418{
419 // We shutdown socket connection, which will cause
420 // read thread to stop, and other threads will follow
421 shutdown(m_hSocket, 2);
422}
62f5857f
VK
423
424
425//
426// Handler for new events
427//
428
429void ClientSession::OnNewEvent(Event *pEvent)
430{
431 UPDATE_INFO *pUpdate;
432
433 pUpdate = (UPDATE_INFO *)malloc(sizeof(UPDATE_INFO));
434 pUpdate->dwCategory = INFO_CAT_EVENT;
435 pUpdate->pData = malloc(sizeof(NXC_EVENT));
436 pEvent->PrepareMessage((NXC_EVENT *)pUpdate->pData);
437 m_pUpdateQueue->Put(pUpdate);
438}
439
440
441//
442// Handler for object changes
443//
444
445void ClientSession::OnObjectChange(DWORD dwObjectId)
446{
447}
448
449
450//
451// Update processing thread
452//
453
454void ClientSession::UpdateThread(void)
455{
456 UPDATE_INFO *pUpdate;
457
458 while(1)
459 {
460 pUpdate = (UPDATE_INFO *)m_pUpdateQueue->GetOrBlock();
461 if (pUpdate == INVALID_POINTER_VALUE) // Session termination indicator
462 break;
463
464 switch(pUpdate->dwCategory)
465 {
466 case INFO_CAT_EVENT:
467 MutexLock(m_hMutexSendEvents, INFINITE);
468 m_pSendQueue->Put(CreateRawCSCPMessage(CMD_EVENT, 0, sizeof(NXC_EVENT), pUpdate->pData, NULL));
469 MutexUnlock(m_hMutexSendEvents);
470 free(pUpdate->pData);
471 break;
472 default:
473 break;
474 }
475
476 free(pUpdate);
477 }
478 ConditionSet(m_hCondUpdateThreadStopped);
479}
83f0529c
VK
480
481
482//
483// Send notification message to server
484//
485
486void ClientSession::Notify(DWORD dwCode)
487{
488 CSCPMessage msg;
489
490 msg.SetCode(CMD_NOTIFY);
491 msg.SetVariable(VID_NOTIFICATION_CODE, dwCode);
492 SendMessage(&msg);
493}