implemented helper class SocketListener; server listeners converted to use SocketListener
[public/netxms.git] / src / server / core / client.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Copyright (C) 2003-2016 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 ** File: client.cpp
20 **
21 **/
22
23 #include "nxcore.h"
24 #include <socket_listener.h>
25
26 /**
27 * Static data
28 */
29 static ClientSession *s_sessionList[MAX_CLIENT_SESSIONS];
30 static RWLOCK s_sessionListLock;
31
32 /**
33 * Register new session in list
34 */
35 static BOOL RegisterClientSession(ClientSession *pSession)
36 {
37 UINT32 i;
38
39 RWLockWriteLock(s_sessionListLock, INFINITE);
40 for(i = 0; i < MAX_CLIENT_SESSIONS; i++)
41 if (s_sessionList[i] == NULL)
42 {
43 s_sessionList[i] = pSession;
44 pSession->setId(i);
45 RWLockUnlock(s_sessionListLock);
46 return TRUE;
47 }
48
49 RWLockUnlock(s_sessionListLock);
50 nxlog_write(MSG_TOO_MANY_SESSIONS, EVENTLOG_WARNING_TYPE, NULL);
51 return FALSE;
52 }
53
54 /**
55 * Unregister session
56 */
57 void UnregisterClientSession(int id)
58 {
59 RWLockWriteLock(s_sessionListLock, INFINITE);
60 s_sessionList[id] = NULL;
61 RWLockUnlock(s_sessionListLock);
62 }
63
64 /**
65 * Keep-alive thread
66 */
67 static THREAD_RESULT THREAD_CALL ClientKeepAliveThread(void *)
68 {
69 int i, iSleepTime;
70 NXCPMessage msg;
71
72 // Read configuration
73 iSleepTime = ConfigReadInt(_T("KeepAliveInterval"), 60);
74
75 // Prepare keepalive message
76 msg.setCode(CMD_KEEPALIVE);
77 msg.setId(0);
78
79 while(1)
80 {
81 if (SleepAndCheckForShutdown(iSleepTime))
82 break;
83
84 msg.setField(VID_TIMESTAMP, (UINT32)time(NULL));
85 RWLockReadLock(s_sessionListLock, INFINITE);
86 for(i = 0; i < MAX_CLIENT_SESSIONS; i++)
87 if (s_sessionList[i] != NULL)
88 if (s_sessionList[i]->isAuthenticated())
89 s_sessionList[i]->postMessage(&msg);
90 RWLockUnlock(s_sessionListLock);
91 }
92
93 DbgPrintf(1, _T("Client keep-alive thread terminated"));
94 return THREAD_OK;
95 }
96
97 /**
98 * Initialize client listener(s)
99 */
100 void InitClientListeners()
101 {
102 memset(s_sessionList, 0, sizeof(s_sessionList));
103
104 // Create session list access rwlock
105 s_sessionListLock = RWLockCreate();
106
107 // Start client keep-alive thread
108 ThreadCreate(ClientKeepAliveThread, 0, NULL);
109 }
110
111 /**
112 * Client listener class
113 */
114 class ClientListener : public SocketListener
115 {
116 protected:
117 virtual ConnectionProcessingResult processConnection(SOCKET s, const InetAddress& peer);
118 virtual bool isStopConditionReached();
119
120 public:
121 ClientListener(UINT16 port) : SocketListener(port) { setName(_T("Clients")); }
122 };
123
124 /**
125 * Listener stop condition
126 */
127 bool ClientListener::isStopConditionReached()
128 {
129 return IsShutdownInProgress();
130 }
131
132 /**
133 * Process incoming connection
134 */
135 ConnectionProcessingResult ClientListener::processConnection(SOCKET s, const InetAddress& peer)
136 {
137 SetSocketNonBlocking(s);
138 ClientSession *session = new ClientSession(s, peer);
139 if (RegisterClientSession(session))
140 {
141 session->run();
142 }
143 else
144 {
145 delete session;
146 }
147 return CPR_BACKGROUND;
148 }
149
150 /**
151 * Listener thread
152 */
153 THREAD_RESULT THREAD_CALL ClientListenerThread(void *arg)
154 {
155 UINT16 listenPort = (UINT16)ConfigReadInt(_T("ClientListenerPort"), SERVER_LISTEN_PORT_FOR_CLIENTS);
156 ClientListener listener(listenPort);
157 listener.setListenAddress(g_szListenAddress);
158 if (!listener.initialize())
159 return THREAD_OK;
160
161 listener.mainLoop();
162 listener.shutdown();
163 return THREAD_OK;
164 }
165
166 /**
167 * Dump client sessions to screen
168 */
169 void DumpClientSessions(CONSOLE_CTX pCtx)
170 {
171 int i, iCount;
172 TCHAR szBuffer[256];
173 static const TCHAR *pszStateName[] = { _T("init"), _T("idle"), _T("processing") };
174 static const TCHAR *pszCipherName[] = { _T("NONE"), _T("AES-256"), _T("BF-256"), _T("IDEA"), _T("3DES"), _T("AES-128"), _T("BF-128") };
175 static const TCHAR *pszClientType[] = { _T("DESKTOP"), _T("WEB"), _T("MOBILE"), _T("TABLET"), _T("APP") };
176
177 ConsolePrintf(pCtx, _T("ID STATE CIPHER CLTYPE USER [CLIENT]\n"));
178 RWLockReadLock(s_sessionListLock, INFINITE);
179 for(i = 0, iCount = 0; i < MAX_CLIENT_SESSIONS; i++)
180 if (s_sessionList[i] != NULL)
181 {
182 TCHAR webServer[256] = _T("");
183 if (s_sessionList[i]->getClientType() == CLIENT_TYPE_WEB)
184 {
185 _sntprintf(webServer, 256, _T(" (%s)"), s_sessionList[i]->getWebServerAddress());
186 }
187 ConsolePrintf(pCtx, _T("%-3d %-24s %-8s %-7s %s%s [%s]\n"), i,
188 (s_sessionList[i]->getState() != SESSION_STATE_PROCESSING) ?
189 pszStateName[s_sessionList[i]->getState()] :
190 NXCPMessageCodeName(s_sessionList[i]->getCurrentCmd(), szBuffer),
191 pszCipherName[s_sessionList[i]->getCipher() + 1],
192 pszClientType[s_sessionList[i]->getClientType()],
193 s_sessionList[i]->getSessionName(), webServer,
194 s_sessionList[i]->getClientInfo());
195 iCount++;
196 }
197 RWLockUnlock(s_sessionListLock);
198 ConsolePrintf(pCtx, _T("\n%d active session%s\n\n"), iCount, iCount == 1 ? _T("") : _T("s"));
199 }
200
201 /**
202 * Kill client session
203 */
204 bool NXCORE_EXPORTABLE KillClientSession(int id)
205 {
206 bool success = false;
207 RWLockReadLock(s_sessionListLock, INFINITE);
208 for(int i = 0; i < MAX_CLIENT_SESSIONS; i++)
209 {
210 if ((s_sessionList[i] != NULL) && (s_sessionList[i]->getId() == id))
211 {
212 s_sessionList[i]->kill();
213 success = true;
214 break;
215 }
216 }
217 RWLockUnlock(s_sessionListLock);
218 return success;
219 }
220
221 /**
222 * Enumerate active sessions
223 */
224 void NXCORE_EXPORTABLE EnumerateClientSessions(void (*pHandler)(ClientSession *, void *), void *pArg)
225 {
226 RWLockReadLock(s_sessionListLock, INFINITE);
227 for(int i = 0; i < MAX_CLIENT_SESSIONS; i++)
228 {
229 if ((s_sessionList[i] != NULL) && !s_sessionList[i]->isTerminated())
230 {
231 pHandler(s_sessionList[i], pArg);
232 }
233 }
234 RWLockUnlock(s_sessionListLock);
235 }
236
237 /**
238 * Send user database update notification to all clients
239 */
240 void SendUserDBUpdate(int code, UINT32 id, UserDatabaseObject *object)
241 {
242 NXCPMessage msg;
243 msg.setCode(CMD_USER_DB_UPDATE);
244 msg.setId(0);
245 msg.setField(VID_UPDATE_TYPE, (WORD)code);
246 switch(code)
247 {
248 case USER_DB_CREATE:
249 case USER_DB_MODIFY:
250 object->fillMessage(&msg);
251 break;
252 default:
253 msg.setField(VID_USER_ID, id);
254 break;
255 }
256
257 RWLockReadLock(s_sessionListLock, INFINITE);
258 for(int i = 0; i < MAX_CLIENT_SESSIONS; i++)
259 if ((s_sessionList[i] != NULL) &&
260 s_sessionList[i]->isAuthenticated() &&
261 !s_sessionList[i]->isTerminated() &&
262 s_sessionList[i]->isSubscribedTo(NXC_CHANNEL_USERDB))
263 s_sessionList[i]->postMessage(&msg);
264 RWLockUnlock(s_sessionListLock);
265 }
266
267 /**
268 * Send graph update to all active sessions
269 */
270 void NXCORE_EXPORTABLE NotifyClientGraphUpdate(NXCPMessage *update, UINT32 graphId)
271 {
272 RWLockReadLock(s_sessionListLock, INFINITE);
273 for(int i = 0; i < MAX_CLIENT_SESSIONS; i++)
274 if ((s_sessionList[i] != NULL) &&
275 s_sessionList[i]->isAuthenticated() &&
276 !s_sessionList[i]->isTerminated() &&
277 (GetGraphAccessCheckResult(graphId, s_sessionList[i]->getUserId()) == RCC_SUCCESS))
278 s_sessionList[i]->postMessage(update);
279 RWLockUnlock(s_sessionListLock);
280 }
281
282 /**
283 * Send notification to all active user sessions
284 */
285 void NXCORE_EXPORTABLE NotifyClientSessions(UINT32 dwCode, UINT32 dwData)
286 {
287 RWLockReadLock(s_sessionListLock, INFINITE);
288 for(int i = 0; i < MAX_CLIENT_SESSIONS; i++)
289 {
290 if ((s_sessionList[i] != NULL) &&
291 s_sessionList[i]->isAuthenticated() &&
292 !s_sessionList[i]->isTerminated())
293 {
294 s_sessionList[i]->notify(dwCode, dwData);
295 }
296 }
297 RWLockUnlock(s_sessionListLock);
298 }
299
300 /**
301 * Send notification to specified user session
302 */
303 void NXCORE_EXPORTABLE NotifyClientSession(UINT32 sessionId, UINT32 dwCode, UINT32 dwData)
304 {
305 RWLockReadLock(s_sessionListLock, INFINITE);
306 for(int i = 0; i < MAX_CLIENT_SESSIONS; i++)
307 if ((s_sessionList[i] != NULL) && (s_sessionList[i]->getId() == sessionId))
308 s_sessionList[i]->notify(dwCode, dwData);
309 RWLockUnlock(s_sessionListLock);
310 }
311
312 /**
313 * Get number of active user sessions
314 */
315 int GetSessionCount(bool includeSystemAccount)
316 {
317 int i, nCount;
318
319 RWLockReadLock(s_sessionListLock, INFINITE);
320 for(i = 0, nCount = 0; i < MAX_CLIENT_SESSIONS; i++)
321 {
322 if ((s_sessionList[i] != NULL) &&
323 (includeSystemAccount || (s_sessionList[i]->getUserId() != 0)))
324 {
325 nCount++;
326 }
327 }
328 RWLockUnlock(s_sessionListLock);
329 return nCount;
330 }
331
332 /**
333 * Check if given user is currenly logged in
334 */
335 bool IsLoggedIn(UINT32 dwUserId)
336 {
337 bool result = false;
338 RWLockReadLock(s_sessionListLock, INFINITE);
339 for(int i = 0; i < MAX_CLIENT_SESSIONS; i++)
340 if (s_sessionList[i] != NULL && s_sessionList[i]->getUserId() == dwUserId)
341 {
342 result = true;
343 break;
344 }
345 RWLockUnlock(s_sessionListLock);
346 return result;
347 }
348
349 /**
350 * Close all user's sessions except given one
351 */
352 void CloseOtherSessions(UINT32 userId, UINT32 thisSession)
353 {
354 RWLockReadLock(s_sessionListLock, INFINITE);
355 for(int i = 0; i < MAX_CLIENT_SESSIONS; i++)
356 {
357 if ((s_sessionList[i] != NULL) &&
358 (s_sessionList[i]->getUserId() == userId) &&
359 (s_sessionList[i]->getId() != thisSession))
360 {
361 nxlog_debug(4, _T("CloseOtherSessions(%d,%d): disconnecting session %d"), userId, thisSession, s_sessionList[i]->getId());
362 s_sessionList[i]->kill();
363 }
364 }
365 RWLockUnlock(s_sessionListLock);
366 }