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