Event codes moved to separate file 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
30ClientSession::ClientSession(SOCKET hSocket)
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();
21e4b6f0
VK
43}
44
45
46//
47// Destructor
48//
49
50ClientSession::~ClientSession()
51{
52 shutdown(m_hSocket, 2);
53 closesocket(m_hSocket);
54 delete m_pSendQueue;
55 delete m_pMessageQueue;
62f5857f 56 delete m_pUpdateQueue;
21e4b6f0
VK
57 if (m_pMsgBuffer != NULL)
58 free(m_pMsgBuffer);
ecb7e1ee
VK
59 ConditionDestroy(m_hCondWriteThreadStopped);
60 ConditionDestroy(m_hCondProcessingThreadStopped);
62f5857f
VK
61 ConditionDestroy(m_hCondUpdateThreadStopped);
62 MutexDestroy(m_hMutexSendEvents);
21e4b6f0
VK
63}
64
65
66//
67// Print debug information
68//
69
70void ClientSession::DebugPrintf(char *szFormat, ...)
71{
72 if ((g_dwFlags & AF_STANDALONE) && (g_dwFlags & AF_DEBUG_CSCP))
73 {
74 va_list args;
75
76 printf("*CSCP(%d)* ", m_dwIndex);
77 va_start(args, szFormat);
78 vprintf(szFormat, args);
79 va_end(args);
80 }
81}
82
83
84//
85// Post message to send queue
86//
87
88void ClientSession::SendMessage(CSCPMessage *pMsg)
89{
90 m_pSendQueue->Put(pMsg->CreateMessage());
91}
92
93
94//
95// ReadThread()
96//
97
98void ClientSession::ReadThread(void)
99{
100 CSCP_MESSAGE *pRawMsg;
101 CSCPMessage *pMsg;
102 int iErr;
103
104 // Initialize raw message receiving function
105 RecvCSCPMessage(0, NULL, m_pMsgBuffer);
106
107 pRawMsg = (CSCP_MESSAGE *)malloc(65536);
108 while(1)
109 {
110 if ((iErr = RecvCSCPMessage(m_hSocket, pRawMsg, m_pMsgBuffer)) <= 0)
111 break;
112
113 // Check that actual received packet size is equal to encoded in packet
114 if (ntohs(pRawMsg->wSize) != iErr)
115 {
116 DebugPrintf("Actual message size doesn't match wSize value (%d,%d)\n", iErr, ntohs(pRawMsg->wSize));
117 continue; // Bad packet, wait for next
118 }
119
120 // Create message object from raw message
121 pMsg = new CSCPMessage(pRawMsg);
122 m_pMessageQueue->Put(pMsg);
123 }
124 if (iErr < 0)
125 WriteLog(MSG_SESSION_CLOSED, EVENTLOG_WARNING_TYPE, "e", WSAGetLastError());
126 free(pRawMsg);
127
128 // Notify other threads to exit
ecb7e1ee
VK
129 m_pSendQueue->Put(INVALID_POINTER_VALUE);
130 m_pMessageQueue->Put(INVALID_POINTER_VALUE);
131
132 // Wait for other threads to finish
133 ConditionWait(m_hCondWriteThreadStopped, INFINITE);
134 ConditionWait(m_hCondProcessingThreadStopped, INFINITE);
21e4b6f0
VK
135}
136
137
138//
139// WriteThread()
140//
141
142void ClientSession::WriteThread(void)
143{
144 CSCP_MESSAGE *pMsg;
145
146 while(1)
147 {
148 pMsg = (CSCP_MESSAGE *)m_pSendQueue->GetOrBlock();
ecb7e1ee 149 if (pMsg == INVALID_POINTER_VALUE) // Session termination indicator
21e4b6f0
VK
150 break;
151
152 if (send(m_hSocket, (const char *)pMsg, ntohs(pMsg->wSize), 0) <= 0)
153 {
7968a52d 154 MemFree(pMsg);
21e4b6f0
VK
155 break;
156 }
7968a52d 157 MemFree(pMsg);
21e4b6f0 158 }
ecb7e1ee 159 ConditionSet(m_hCondWriteThreadStopped);
21e4b6f0
VK
160}
161
162
163//
164// Message processing thread
165//
166
167void ClientSession::ProcessingThread(void)
168{
169 CSCPMessage *pMsg, *pReply;
170
171 while(1)
172 {
173 pMsg = (CSCPMessage *)m_pMessageQueue->GetOrBlock();
ecb7e1ee 174 if (pMsg == INVALID_POINTER_VALUE) // Session termination indicator
21e4b6f0
VK
175 break;
176
177 DebugPrintf("Received message with code %d\n", pMsg->GetCode());
178 if ((m_iState != STATE_AUTHENTICATED) && (pMsg->GetCode() != CMD_LOGIN))
179 {
180 delete pMsg;
181 continue;
182 }
183
184 switch(pMsg->GetCode())
185 {
186 case CMD_LOGIN:
187 if (m_iState != STATE_AUTHENTICATED)
188 {
0fdc2761
VK
189 BYTE szPassword[SHA_DIGEST_LENGTH];
190 char *pszLogin;
191
192 pszLogin = pMsg->GetVariableStr(VID_LOGIN_NAME);
193 pMsg->GetVariableBinary(VID_PASSWORD, szPassword, SHA_DIGEST_LENGTH);
21e4b6f0 194
0fdc2761 195 if (AuthenticateUser(pszLogin, szPassword, &m_dwUserId, &m_dwSystemAccess))
21e4b6f0
VK
196 m_iState = STATE_AUTHENTICATED;
197
7968a52d 198 MemFree(pszLogin);
21e4b6f0
VK
199
200 // Send reply
201 pReply = new CSCPMessage;
202 pReply->SetCode(CMD_LOGIN_RESP);
203 pReply->SetId(pMsg->GetId());
a5f8dbb8 204 pReply->SetVariable(VID_LOGIN_RESULT, (DWORD)(m_iState == STATE_AUTHENTICATED));
21e4b6f0
VK
205 SendMessage(pReply);
206 delete pReply;
207 }
208 else
209 {
210 }
211 break;
212 case CMD_GET_OBJECTS:
213 SendAllObjects();
214 break;
215 case CMD_GET_EVENTS:
216 SendAllEvents();
217 break;
218 case CMD_GET_CONFIG_VARLIST:
219 SendAllConfigVars();
220 break;
221 default:
222 break;
223 }
224 delete pMsg;
225 }
ecb7e1ee 226 ConditionSet(m_hCondProcessingThreadStopped);
21e4b6f0
VK
227}
228
229
230//
231// Send all objects to client
232//
233
234void ClientSession::SendAllObjects(void)
235{
236 DWORD i;
237 CSCPMessage msg;
238
239 // Prepare message
240 msg.SetCode(CMD_OBJECT);
241
242 // Send objects, one per message
243 ObjectsGlobalLock();
244 for(i = 0; i < g_dwIdIndexSize; i++)
245 {
246 g_pIndexById[i].pObject->CreateMessage(&msg);
247 SendMessage(&msg);
248 msg.DeleteAllVariables();
249 }
250 ObjectsGlobalUnlock();
251
252 // Send end of list notification
253 msg.SetCode(CMD_OBJECT_LIST_END);
254 SendMessage(&msg);
255}
256
257
258//
259// Send all events to client
260//
261
262void ClientSession::SendAllEvents(void)
263{
21e4b6f0 264 CSCPMessage msg;
20177e8e 265 DB_ASYNC_RESULT hResult;
9c36ef66 266 NXC_EVENT event;
21e4b6f0 267
62f5857f
VK
268 MutexLock(m_hMutexSendEvents, INFINITE);
269
21e4b6f0 270 // Retrieve events from database
20177e8e 271 hResult = DBAsyncSelect(g_hCoreDB, "SELECT event_id,timestamp,source,severity,message FROM EventLog ORDER BY timestamp");
21e4b6f0
VK
272 if (hResult != NULL)
273 {
274 // Send events, one per message
20177e8e 275 while(DBFetch(hResult))
21e4b6f0 276 {
20177e8e
VK
277 event.dwEventId = htonl(DBGetFieldAsyncULong(hResult, 0));
278 event.dwTimeStamp = htonl(DBGetFieldAsyncULong(hResult, 1));
279 event.dwSourceId = htonl(DBGetFieldAsyncULong(hResult, 2));
280 event.dwSeverity = htonl(DBGetFieldAsyncULong(hResult, 3));
281 DBGetFieldAsync(hResult, 4, event.szMessage, MAX_EVENT_MSG_LENGTH);
9c36ef66
VK
282 m_pSendQueue->Put(CreateRawCSCPMessage(CMD_EVENT, 0, sizeof(NXC_EVENT), &event, NULL));
283 }
20177e8e 284 DBFreeAsyncResult(hResult);
21e4b6f0
VK
285 }
286
287 // Send end of list notification
288 msg.SetCode(CMD_EVENT_LIST_END);
289 SendMessage(&msg);
62f5857f
VK
290
291 MutexUnlock(m_hMutexSendEvents);
21e4b6f0
VK
292}
293
294
295//
296// Send all configuration variables to client
297//
298
299void ClientSession::SendAllConfigVars(void)
300{
301 DWORD i, dwNumRecords;
302 CSCPMessage msg;
303 DB_RESULT hResult;
304
305 // Check user rights
306 if ((m_dwUserId != 0) && ((m_dwSystemAccess & SYSTEM_ACCESS_VIEW_CONFIG) == 0))
307 {
308 // Access denied
309 msg.SetCode(CMD_CONFIG_VARLIST_END);
a5f8dbb8 310 msg.SetVariable(VID_ERROR, (DWORD)1);
21e4b6f0
VK
311 SendMessage(&msg);
312 }
313 else
314 {
315 // Prepare message
316 msg.SetCode(CMD_CONFIG_VARIABLE);
317
318 // Retrieve configuration variables from database
319 hResult = DBSelect(g_hCoreDB, "SELECT name,value FROM config");
320 if (hResult != NULL)
321 {
322 // Send events, one per message
323 dwNumRecords = DBGetNumRows(hResult);
324 for(i = 0; i < dwNumRecords; i++)
325 {
a5f8dbb8
VK
326 msg.SetVariable(VID_NAME, DBGetField(hResult, i, 0));
327 msg.SetVariable(VID_VALUE, DBGetField(hResult, i, 1));
21e4b6f0
VK
328 SendMessage(&msg);
329 msg.DeleteAllVariables();
330 }
20177e8e 331 DBFreeResult(hResult);
21e4b6f0
VK
332 }
333
334 // Send end of list notification
335 msg.SetCode(CMD_CONFIG_VARLIST_END);
a5f8dbb8 336 msg.SetVariable(VID_ERROR, (DWORD)0);
21e4b6f0
VK
337 SendMessage(&msg);
338 }
339}
20177e8e
VK
340
341
342//
343// Close session forcibly
344//
345
346void ClientSession::Kill(void)
347{
348 // We shutdown socket connection, which will cause
349 // read thread to stop, and other threads will follow
350 shutdown(m_hSocket, 2);
351}
62f5857f
VK
352
353
354//
355// Handler for new events
356//
357
358void ClientSession::OnNewEvent(Event *pEvent)
359{
360 UPDATE_INFO *pUpdate;
361
362 pUpdate = (UPDATE_INFO *)malloc(sizeof(UPDATE_INFO));
363 pUpdate->dwCategory = INFO_CAT_EVENT;
364 pUpdate->pData = malloc(sizeof(NXC_EVENT));
365 pEvent->PrepareMessage((NXC_EVENT *)pUpdate->pData);
366 m_pUpdateQueue->Put(pUpdate);
367}
368
369
370//
371// Handler for object changes
372//
373
374void ClientSession::OnObjectChange(DWORD dwObjectId)
375{
376}
377
378
379//
380// Update processing thread
381//
382
383void ClientSession::UpdateThread(void)
384{
385 UPDATE_INFO *pUpdate;
386
387 while(1)
388 {
389 pUpdate = (UPDATE_INFO *)m_pUpdateQueue->GetOrBlock();
390 if (pUpdate == INVALID_POINTER_VALUE) // Session termination indicator
391 break;
392
393 switch(pUpdate->dwCategory)
394 {
395 case INFO_CAT_EVENT:
396 MutexLock(m_hMutexSendEvents, INFINITE);
397 m_pSendQueue->Put(CreateRawCSCPMessage(CMD_EVENT, 0, sizeof(NXC_EVENT), pUpdate->pData, NULL));
398 MutexUnlock(m_hMutexSendEvents);
399 free(pUpdate->pData);
400 break;
401 default:
402 break;
403 }
404
405 free(pUpdate);
406 }
407 ConditionSet(m_hCondUpdateThreadStopped);
408}
83f0529c
VK
409
410
411//
412// Send notification message to server
413//
414
415void ClientSession::Notify(DWORD dwCode)
416{
417 CSCPMessage msg;
418
419 msg.SetCode(CMD_NOTIFY);
420 msg.SetVariable(VID_NOTIFICATION_CODE, dwCode);
421 SendMessage(&msg);
422}