"alarm details" view mostly working; event history for active alarms preserved; minor...
[public/netxms.git] / src / server / core / client.cpp
CommitLineData
5039dede
AK
1/*
2** NetXMS - Network Management System
f8c6f10f 3** Copyright (C) 2003-2012 Victor Kirhenshtein
5039dede
AK
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** File: client.cpp
20**
21**/
22
23#include "nxcore.h"
24
534e1b83
VK
25/**
26 * Constants
27 */
5039dede
AK
28#define MAX_CLIENT_SESSIONS 128
29
534e1b83
VK
30/**
31 * Static data
32 */
5039dede
AK
33static ClientSession *m_pSessionList[MAX_CLIENT_SESSIONS];
34static RWLOCK m_rwlockSessionListAccess;
35
534e1b83
VK
36/**
37 * Register new session in list
38 */
39static BOOL RegisterClientSession(ClientSession *pSession)
5039dede
AK
40{
41 DWORD i;
42
43 RWLockWriteLock(m_rwlockSessionListAccess, INFINITE);
44 for(i = 0; i < MAX_CLIENT_SESSIONS; i++)
45 if (m_pSessionList[i] == NULL)
46 {
47 m_pSessionList[i] = pSession;
e05b1945 48 pSession->setIndex(i);
5039dede
AK
49 RWLockUnlock(m_rwlockSessionListAccess);
50 return TRUE;
51 }
52
53 RWLockUnlock(m_rwlockSessionListAccess);
54 nxlog_write(MSG_TOO_MANY_SESSIONS, EVENTLOG_WARNING_TYPE, NULL);
55 return FALSE;
56}
57
534e1b83
VK
58/**
59 * Unregister session
60 */
61void UnregisterClientSession(DWORD dwIndex)
5039dede
AK
62{
63 RWLockWriteLock(m_rwlockSessionListAccess, INFINITE);
64 m_pSessionList[dwIndex] = NULL;
65 RWLockUnlock(m_rwlockSessionListAccess);
66}
67
534e1b83
VK
68/**
69 * Keep-alive thread
70 */
5039dede
AK
71static THREAD_RESULT THREAD_CALL ClientKeepAliveThread(void *)
72{
73 int i, iSleepTime;
74 CSCPMessage msg;
75
76 // Read configuration
35f836fe 77 iSleepTime = ConfigReadInt(_T("KeepAliveInterval"), 60);
5039dede
AK
78
79 // Prepare keepalive message
80 msg.SetCode(CMD_KEEPALIVE);
81 msg.SetId(0);
82
83 while(1)
84 {
85 if (SleepAndCheckForShutdown(iSleepTime))
86 break;
87
88 msg.SetVariable(VID_TIMESTAMP, (DWORD)time(NULL));
89 RWLockReadLock(m_rwlockSessionListAccess, INFINITE);
90 for(i = 0; i < MAX_CLIENT_SESSIONS; i++)
91 if (m_pSessionList[i] != NULL)
e05b1945
VK
92 if (m_pSessionList[i]->isAuthenticated())
93 m_pSessionList[i]->sendMessage(&msg);
5039dede
AK
94 RWLockUnlock(m_rwlockSessionListAccess);
95 }
96
97 DbgPrintf(1, _T("Client keep-alive thread terminated"));
98 return THREAD_OK;
99}
100
534e1b83
VK
101/**
102 * Initialize client listener(s)
103 */
36e44abe
VK
104void InitClientListeners()
105{
106 // Create session list access rwlock
107 m_rwlockSessionListAccess = RWLockCreate();
108
109 // Start client keep-alive thread
110 ThreadCreate(ClientKeepAliveThread, 0, NULL);
111}
112
534e1b83
VK
113/**
114 * Listener thread
115 */
36e44abe 116THREAD_RESULT THREAD_CALL ClientListener(void *arg)
5039dede
AK
117{
118 SOCKET sock, sockClient;
119 struct sockaddr_in servAddr;
120 int errorCount = 0;
121 socklen_t iSize;
122 WORD wListenPort;
123 ClientSession *pSession;
124
125 // Read configuration
534e1b83 126 wListenPort = (WORD)ConfigReadInt(_T("ClientListenerPort"), SERVER_LISTEN_PORT_FOR_CLIENTS);
5039dede
AK
127
128 // Create socket
129 if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1)
130 {
35f836fe 131 nxlog_write(MSG_SOCKET_FAILED, EVENTLOG_ERROR_TYPE, "s", _T("ClientListener"));
5039dede
AK
132 return THREAD_OK;
133 }
134
1ddf3f0c 135 SetSocketExclusiveAddrUse(sock);
5039dede
AK
136 SetSocketReuseFlag(sock);
137
5039dede
AK
138 // Fill in local address structure
139 memset(&servAddr, 0, sizeof(struct sockaddr_in));
140 servAddr.sin_family = AF_INET;
141 servAddr.sin_addr.s_addr = ResolveHostName(g_szListenAddress);
142 servAddr.sin_port = htons(wListenPort);
143
144 // Bind socket
145 if (bind(sock, (struct sockaddr *)&servAddr, sizeof(struct sockaddr_in)) != 0)
146 {
35f836fe 147 nxlog_write(MSG_BIND_ERROR, EVENTLOG_ERROR_TYPE, "dse", wListenPort, _T("ClientListener"), WSAGetLastError());
5039dede
AK
148 closesocket(sock);
149 /* TODO: we should initiate shutdown procedure here */
150 return THREAD_OK;
151 }
152
153 // Set up queue
154 listen(sock, SOMAXCONN);
155 nxlog_write(MSG_LISTENING_FOR_CLIENTS, EVENTLOG_INFORMATION_TYPE, "ad", ntohl(servAddr.sin_addr.s_addr), wListenPort);
156
36e44abe 157 // Wait for connection requests
89135050 158 while(!IsShutdownInProgress())
36e44abe
VK
159 {
160 iSize = sizeof(struct sockaddr_in);
161 if ((sockClient = accept(sock, (struct sockaddr *)&servAddr, &iSize)) == -1)
162 {
163 int error;
164
165#ifdef _WIN32
166 error = WSAGetLastError();
167 if (error != WSAEINTR)
168#else
169 error = errno;
170 if (error != EINTR)
171#endif
172 nxlog_write(MSG_ACCEPT_ERROR, EVENTLOG_ERROR_TYPE, "e", error);
173 errorCount++;
174 if (errorCount > 1000)
175 {
176 nxlog_write(MSG_TOO_MANY_ACCEPT_ERRORS, EVENTLOG_WARNING_TYPE, NULL);
177 errorCount = 0;
178 }
179 ThreadSleepMs(500);
c6599f4e 180 continue;
36e44abe
VK
181 }
182
183 errorCount = 0; // Reset consecutive errors counter
d86c557c 184 SetSocketNonBlocking(sockClient);
36e44abe
VK
185
186 // Create new session structure and threads
60557d06 187 pSession = new ClientSession(sockClient, (struct sockaddr *)&servAddr);
534e1b83 188 if (!RegisterClientSession(pSession))
36e44abe
VK
189 {
190 delete pSession;
191 }
192 else
193 {
194 pSession->run();
195 }
196 }
197
198 closesocket(sock);
199 return THREAD_OK;
200}
201
534e1b83
VK
202/**
203 * Listener thread - IPv6
204 */
36e44abe
VK
205#ifdef WITH_IPV6
206
207THREAD_RESULT THREAD_CALL ClientListenerIPv6(void *arg)
208{
209 SOCKET sock, sockClient;
210 struct sockaddr_in6 servAddr;
211 int errorCount = 0;
212 socklen_t iSize;
213 WORD wListenPort;
214 ClientSession *pSession;
215
216 // Read configuration
534e1b83 217 wListenPort = (WORD)ConfigReadInt(_T("ClientListenerPort"), SERVER_LISTEN_PORT_FOR_CLIENTS);
36e44abe
VK
218
219 // Create socket
220 if ((sock = socket(AF_INET6, SOCK_STREAM, 0)) == -1)
221 {
222 nxlog_write(MSG_SOCKET_FAILED, EVENTLOG_ERROR_TYPE, "s", _T("ClientListener"));
223 return THREAD_OK;
224 }
225
226 SetSocketExclusiveAddrUse(sock);
227 SetSocketReuseFlag(sock);
228
229 // Fill in local address structure
230 memset(&servAddr, 0, sizeof(struct sockaddr_in6));
231 servAddr.sin6_family = AF_INET6;
232 //servAddr.sin6_addr.s6_addr = ResolveHostName(g_szListenAddress);
233 servAddr.sin6_port = htons(wListenPort);
234
235 // Bind socket
236 if (bind(sock, (struct sockaddr *)&servAddr, sizeof(struct sockaddr_in6)) != 0)
237 {
238 nxlog_write(MSG_BIND_ERROR, EVENTLOG_ERROR_TYPE, "dse", wListenPort, _T("ClientListener"), WSAGetLastError());
239 closesocket(sock);
240 /* TODO: we should initiate shutdown procedure here */
241 return THREAD_OK;
242 }
243
244 // Set up queue
245 listen(sock, SOMAXCONN);
60557d06 246 nxlog_write(MSG_LISTENING_FOR_CLIENTS, EVENTLOG_INFORMATION_TYPE, "Ad", servAddr.sin6_addr.s6_addr, wListenPort);
5039dede
AK
247
248 // Wait for connection requests
89135050 249 while(!IsShutdownInProgress())
5039dede 250 {
60557d06 251 iSize = sizeof(struct sockaddr_in6);
5039dede
AK
252 if ((sockClient = accept(sock, (struct sockaddr *)&servAddr, &iSize)) == -1)
253 {
254 int error;
255
256#ifdef _WIN32
257 error = WSAGetLastError();
258 if (error != WSAEINTR)
259#else
260 error = errno;
261 if (error != EINTR)
262#endif
263 nxlog_write(MSG_ACCEPT_ERROR, EVENTLOG_ERROR_TYPE, "e", error);
264 errorCount++;
265 if (errorCount > 1000)
266 {
267 nxlog_write(MSG_TOO_MANY_ACCEPT_ERRORS, EVENTLOG_WARNING_TYPE, NULL);
268 errorCount = 0;
269 }
270 ThreadSleepMs(500);
c6599f4e 271 continue;
5039dede
AK
272 }
273
274 errorCount = 0; // Reset consecutive errors counter
d86c557c 275 SetSocketNonBlocking(sockClient);
5039dede
AK
276
277 // Create new session structure and threads
60557d06 278 pSession = new ClientSession(sockClient, (struct sockaddr *)&servAddr);
534e1b83 279 if (!RegisterClientSession(pSession))
5039dede
AK
280 {
281 delete pSession;
282 }
283 else
284 {
e05b1945 285 pSession->run();
5039dede
AK
286 }
287 }
288
289 closesocket(sock);
290 return THREAD_OK;
291}
292
36e44abe
VK
293#endif
294
534e1b83
VK
295/**
296 * Dump client sessions to screen
297 */
298void DumpClientSessions(CONSOLE_CTX pCtx)
5039dede
AK
299{
300 int i, iCount;
301 TCHAR szBuffer[256];
302 static const TCHAR *pszStateName[] = { _T("init"), _T("idle"), _T("processing") };
9e9d631e 303 static const TCHAR *pszCipherName[] = { _T("NONE"), _T("AES-256"), _T("BLOWFISH"), _T("IDEA"), _T("3DES"), _T("AES-128") };
f8c6f10f 304 static const TCHAR *pszClientType[] = { _T("DESKTOP"), _T("WEB"), _T("MOBILE"), _T("TABLET"), _T("APP") };
5039dede 305
f8c6f10f 306 ConsolePrintf(pCtx, _T("ID STATE CIPHER CLTYPE USER [CLIENT]\n"));
5039dede
AK
307 RWLockReadLock(m_rwlockSessionListAccess, INFINITE);
308 for(i = 0, iCount = 0; i < MAX_CLIENT_SESSIONS; i++)
309 if (m_pSessionList[i] != NULL)
310 {
f8c6f10f 311 ConsolePrintf(pCtx, _T("%-3d %-24s %-8s %-7s %s [%s]\n"), i,
e05b1945
VK
312 (m_pSessionList[i]->getState() != SESSION_STATE_PROCESSING) ?
313 pszStateName[m_pSessionList[i]->getState()] :
314 NXCPMessageCodeName(m_pSessionList[i]->getCurrentCmd(), szBuffer),
315 pszCipherName[m_pSessionList[i]->getCipher() + 1],
f8c6f10f 316 pszClientType[m_pSessionList[i]->getClientType()],
e05b1945
VK
317 m_pSessionList[i]->getUserName(),
318 m_pSessionList[i]->getClientInfo());
5039dede
AK
319 iCount++;
320 }
321 RWLockUnlock(m_rwlockSessionListAccess);
66af8fda 322 ConsolePrintf(pCtx, _T("\n%d active session%s\n\n"), iCount, iCount == 1 ? _T("") : _T("s"));
5039dede
AK
323}
324
534e1b83
VK
325/**
326 * Enumerate active sessions
327 */
b1e9b6b3 328void NXCORE_EXPORTABLE EnumerateClientSessions(void (*pHandler)(ClientSession *, void *), void *pArg)
5039dede
AK
329{
330 int i;
331
332 RWLockReadLock(m_rwlockSessionListAccess, INFINITE);
333 for(i = 0; i < MAX_CLIENT_SESSIONS; i++)
b1e9b6b3 334 if (m_pSessionList[i] != NULL)
5039dede
AK
335 pHandler(m_pSessionList[i], pArg);
336 RWLockUnlock(m_rwlockSessionListAccess);
337}
338
534e1b83
VK
339/**
340 * Send user database update notification to all clients
341 */
a50eaebe 342void SendUserDBUpdate(int code, DWORD id, UserDatabaseObject *object)
5039dede
AK
343{
344 int i;
345
346 RWLockReadLock(m_rwlockSessionListAccess, INFINITE);
347 for(i = 0; i < MAX_CLIENT_SESSIONS; i++)
348 if (m_pSessionList[i] != NULL)
e05b1945 349 m_pSessionList[i]->onUserDBUpdate(code, id, object);
5039dede
AK
350 RWLockUnlock(m_rwlockSessionListAccess);
351}
352
534e1b83
VK
353/**
354 * Send notification to all active user sessions
355 */
5039dede
AK
356void NXCORE_EXPORTABLE NotifyClientSessions(DWORD dwCode, DWORD dwData)
357{
358 int i;
359
360 RWLockReadLock(m_rwlockSessionListAccess, INFINITE);
361 for(i = 0; i < MAX_CLIENT_SESSIONS; i++)
362 if (m_pSessionList[i] != NULL)
e05b1945 363 m_pSessionList[i]->notify(dwCode, dwData);
5039dede
AK
364 RWLockUnlock(m_rwlockSessionListAccess);
365}
366
534e1b83
VK
367/**
368 * Get number of active user sessions
369 */
870d2546 370int GetSessionCount()
5039dede
AK
371{
372 int i, nCount;
373
374 RWLockReadLock(m_rwlockSessionListAccess, INFINITE);
375 for(i = 0, nCount = 0; i < MAX_CLIENT_SESSIONS; i++)
376 if (m_pSessionList[i] != NULL)
377 nCount++;
378 RWLockUnlock(m_rwlockSessionListAccess);
379 return nCount;
380}