min/max calls replaced with std::min/std::max
[public/netxms.git] / src / appagent / appagent.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Copyright (C) 2003-2013 Victor Kirhenshtein
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU Lesser General Public License as published by
7 ** the Free Software Foundation; either version 3 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 Lesser 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: appagent.cpp
20 **
21 **/
22
23 #include "appagent-internal.h"
24
25 #if defined(_AIX) || defined(__sun)
26 #include <signal.h>
27 #endif
28
29 /**
30 * Application agent config
31 */
32 static APPAGENT_INIT s_config;
33 static bool s_stop = false;
34 static bool s_initialized = false;
35 static HPIPE s_pipe = INVALID_PIPE_HANDLE;
36 static THREAD s_connectorThread = INVALID_THREAD_HANDLE;
37
38 /**
39 * Write log
40 */
41 static void AppAgentWriteLog(int level, const TCHAR *format, ...)
42 {
43 if (s_config.logger == NULL)
44 return;
45
46 va_list(args);
47 va_start(args, format);
48 s_config.logger(level, format, args);
49 va_end(args);
50 }
51
52 /**
53 * Get metric
54 */
55 static APPAGENT_MSG *GetMetric(WCHAR *name, int length)
56 {
57 TCHAR metricName[256];
58
59 #ifdef UNICODE
60 wcslcpy(metricName, name, std::min(length, 256));
61 #else
62 WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR, name, length, metricName, 256, NULL, NULL);
63 metricName[std::min(length, 255)] = 0;
64 #endif
65
66 for(int i = 0; i < s_config.numMetrics; i++)
67 {
68 if (MatchString(s_config.metrics[i].name, metricName, FALSE))
69 {
70 TCHAR value[256];
71 int rcc = s_config.metrics[i].handler(metricName, s_config.metrics[i].userArg, value);
72
73 APPAGENT_MSG *msg;
74 if (rcc == APPAGENT_RCC_SUCCESS)
75 {
76 int len = (int)_tcslen(value) + 1;
77 msg = NewMessage(APPAGENT_CMD_REQUEST_COMPLETED, rcc, len * sizeof(WCHAR));
78 #ifdef UNICODE
79 wcscpy((WCHAR *)msg->payload, value);
80 #else
81 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, value, -1, (WCHAR *)msg->payload, len);
82 #endif
83 }
84 else
85 {
86 msg = NewMessage(APPAGENT_CMD_REQUEST_COMPLETED, rcc, 0);
87 }
88 return msg;
89 }
90 }
91 return NewMessage(APPAGENT_CMD_REQUEST_COMPLETED, APPAGENT_RCC_NO_SUCH_METRIC, 0);
92 }
93
94 /**
95 * Encode string into message
96 */
97 static BYTE *EncodeString(BYTE *data, const TCHAR *str)
98 {
99 BYTE *curr = data;
100 int len = (int)_tcslen(str);
101 if (len > 255)
102 len = 255;
103 *curr = (BYTE)len;
104 curr += sizeof(TCHAR);
105 #ifdef UNICODE
106 memcpy(curr, str, len * sizeof(TCHAR));
107 #else
108 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, str, -1, (WCHAR *)curr, len);
109 #endif
110 curr += len * sizeof(WCHAR);
111 return curr;
112 }
113
114 /**
115 * List available metrics
116 */
117 static APPAGENT_MSG *ListMetrics()
118 {
119 int i;
120 int msgLen = sizeof(INT16);
121 for(i = 0; i < s_config.numMetrics; i++)
122 msgLen += (int)((_tcslen(s_config.metrics[i].name) + _tcslen(s_config.metrics[i].description) + 2) * sizeof(TCHAR) + sizeof(INT16));
123
124 APPAGENT_MSG *msg = NewMessage(APPAGENT_CMD_REQUEST_COMPLETED, APPAGENT_RCC_SUCCESS, msgLen);
125 *((INT16 *)msg->payload) = (INT16)s_config.numMetrics;
126
127 BYTE *curr = (BYTE *)msg->payload + sizeof(INT16);
128 for(i = 0; i < s_config.numMetrics; i++)
129 {
130 *((INT16 *)curr) = (INT16)s_config.metrics[i].type;
131 curr = EncodeString(curr + sizeof(INT16), s_config.metrics[i].name);
132 EncodeString(curr, s_config.metrics[i].description);
133 }
134
135 return msg;
136 }
137
138 /**
139 * Process incoming request
140 */
141 static void ProcessRequest(HPIPE hPipe)
142 {
143 AppAgentMessageBuffer *mb = new AppAgentMessageBuffer;
144
145 AppAgentWriteLog(7, _T("ProcessRequest: connection established"));
146 while(true)
147 {
148 APPAGENT_MSG *msg = ReadMessageFromPipe(hPipe, mb);
149 if (msg == NULL)
150 break;
151 AppAgentWriteLog(7, _T("ProcessRequest: received message %04X"), (unsigned int)msg->command);
152 APPAGENT_MSG *response;
153 switch(msg->command)
154 {
155 case APPAGENT_CMD_GET_METRIC:
156 response = GetMetric((WCHAR *)msg->payload, msg->length - APPAGENT_MSG_HEADER_LEN);
157 break;
158 case APPAGENT_CMD_LIST_METRICS:
159 response = ListMetrics();
160 break;
161 default:
162 response = NewMessage(APPAGENT_CMD_REQUEST_COMPLETED, APPAGENT_RCC_BAD_REQUEST, 0);
163 break;
164 }
165 free(msg);
166 SendMessageToPipe(hPipe, response);
167 free(response);
168 }
169 AppAgentWriteLog(7, _T("ProcessRequest: connection closed"));
170 delete mb;
171 }
172
173 /**
174 * Connector thread for application agent
175 */
176 #ifdef _WIN32
177
178 static THREAD_RESULT THREAD_CALL AppAgentConnector(void *arg)
179 {
180 SECURITY_ATTRIBUTES sa;
181 PSECURITY_DESCRIPTOR sd = NULL;
182 SID_IDENTIFIER_AUTHORITY sidAuthWorld = SECURITY_WORLD_SID_AUTHORITY;
183 EXPLICIT_ACCESS ea;
184 PSID sidEveryone = NULL;
185 ACL *acl = NULL;
186 TCHAR errorText[1024];
187
188 // Create a well-known SID for the Everyone group.
189 if(!AllocateAndInitializeSid(&sidAuthWorld, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &sidEveryone))
190 {
191 AppAgentWriteLog(2, _T("AppAgentConnector: AllocateAndInitializeSid failed (%s)"), GetSystemErrorText(GetLastError(), errorText, 1024));
192 goto cleanup;
193 }
194
195 // Initialize an EXPLICIT_ACCESS structure for an ACE.
196 // The ACE will allow either Everyone or given user to access pipe
197 ZeroMemory(&ea, sizeof(EXPLICIT_ACCESS));
198 ea.grfAccessPermissions = (FILE_GENERIC_READ | FILE_GENERIC_WRITE) & ~FILE_CREATE_PIPE_INSTANCE;
199 ea.grfAccessMode = SET_ACCESS;
200 ea.grfInheritance = NO_INHERITANCE;
201 if ((s_config.userId == NULL) || (s_config.userId[0] == 0) || !_tcscmp(s_config.userId, _T("*")))
202 {
203 ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;
204 ea.Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
205 ea.Trustee.ptstrName = (LPTSTR)sidEveryone;
206 }
207 else
208 {
209 ea.Trustee.TrusteeForm = TRUSTEE_IS_NAME;
210 ea.Trustee.TrusteeType = TRUSTEE_IS_USER;
211 ea.Trustee.ptstrName = (LPTSTR)s_config.userId;
212 AppAgentWriteLog(2, _T("AppAgentConnector: will allow connections only for user %s"), s_config.userId);
213 }
214
215 // Create a new ACL that contains the new ACEs.
216 if (SetEntriesInAcl(1, &ea, NULL, &acl) != ERROR_SUCCESS)
217 {
218 AppAgentWriteLog(2, _T("AppAgentConnector: SetEntriesInAcl failed (%s)"), GetSystemErrorText(GetLastError(), errorText, 1024));
219 goto cleanup;
220 }
221
222 sd = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
223 if (sd == NULL)
224 {
225 AppAgentWriteLog(2, _T("AppAgentConnector: LocalAlloc failed (%s)"), GetSystemErrorText(GetLastError(), errorText, 1024));
226 goto cleanup;
227 }
228
229 if (!InitializeSecurityDescriptor(sd, SECURITY_DESCRIPTOR_REVISION))
230 {
231 AppAgentWriteLog(2, _T("AppAgentConnector: InitializeSecurityDescriptor failed (%s)"), GetSystemErrorText(GetLastError(), errorText, 1024));
232 goto cleanup;
233 }
234
235 // Add the ACL to the security descriptor.
236 if (!SetSecurityDescriptorDacl(sd, TRUE, acl, FALSE))
237 {
238 AppAgentWriteLog(2, _T("AppAgentConnector: SetSecurityDescriptorDacl failed (%s)"), GetSystemErrorText(GetLastError(), errorText, 1024));
239 goto cleanup;
240 }
241
242 TCHAR pipeName[MAX_PATH];
243 _sntprintf(pipeName, MAX_PATH, _T("\\\\.\\pipe\\appagent.%s"), s_config.name);
244 sa.nLength = sizeof(SECURITY_ATTRIBUTES);
245 sa.bInheritHandle = FALSE;
246 sa.lpSecurityDescriptor = sd;
247 s_pipe = CreateNamedPipe(pipeName, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE, 1, 8192, 8192, 0, &sa);
248 if (s_pipe == INVALID_HANDLE_VALUE)
249 {
250 AppAgentWriteLog(2, _T("AppAgentConnector: CreateNamedPipe failed (%s)"), GetSystemErrorText(GetLastError(), errorText, 1024));
251 goto cleanup;
252 }
253
254 AppAgentWriteLog(2, _T("AppAgentConnector: named pipe created, waiting for connection"));
255 int connectErrors = 0;
256 while(!s_stop)
257 {
258 BOOL connected = ConnectNamedPipe(s_pipe, NULL);
259 if (connected || (GetLastError() == ERROR_PIPE_CONNECTED))
260 {
261 ProcessRequest(s_pipe);
262 DisconnectNamedPipe(s_pipe);
263 connectErrors = 0;
264 }
265 else
266 {
267 AppAgentWriteLog(2, _T("AppAgentConnector: ConnectNamedPipe failed (%s)"), GetSystemErrorText(GetLastError(), errorText, 1024));
268 connectErrors++;
269 if (connectErrors > 10)
270 break; // Stop this connector if ConnectNamedPipe fails instantly
271 }
272 }
273
274 cleanup:
275 if (s_pipe != NULL)
276 {
277 CloseHandle(s_pipe);
278 s_pipe = INVALID_PIPE_HANDLE;
279 }
280
281 if (sd != NULL)
282 LocalFree(sd);
283
284 if (acl != NULL)
285 LocalFree(acl);
286
287 if (sidEveryone != NULL)
288 FreeSid(sidEveryone);
289
290 AppAgentWriteLog(2, _T("AppAgentConnector: listener thread stopped"));
291 return THREAD_OK;
292 }
293
294 #else
295
296 static THREAD_RESULT THREAD_CALL AppAgentConnector(void *arg)
297 {
298 mode_t prevMask = 0;
299
300 s_pipe = socket(AF_UNIX, SOCK_STREAM, 0);
301 if (s_pipe == -1)
302 {
303 AppAgentWriteLog(2, _T("AppAgentConnector: socket failed (%s)"), _tcserror(errno));
304 goto cleanup;
305 }
306
307 struct sockaddr_un addrLocal;
308 addrLocal.sun_family = AF_UNIX;
309 #ifdef UNICODE
310 sprintf(addrLocal.sun_path, "/tmp/.appagent.%S", s_config.name);
311 #else
312 sprintf(addrLocal.sun_path, "/tmp/.appagent.%s", s_config.name);
313 #endif
314 unlink(addrLocal.sun_path);
315 prevMask = umask(S_IWGRP | S_IWOTH);
316 if (bind(s_pipe, (struct sockaddr *)&addrLocal, SUN_LEN(&addrLocal)) == -1)
317 {
318 AppAgentWriteLog(2, _T("AppAgentConnector: bind failed (%s)"), _tcserror(errno));
319 umask(prevMask);
320 goto cleanup;
321 }
322 umask(prevMask);
323
324 if (listen(s_pipe, 5) == -1)
325 {
326 AppAgentWriteLog(2, _T("AppAgentConnector: listen failed (%s)"), _tcserror(errno));
327 goto cleanup;
328 }
329
330 while(!s_stop)
331 {
332 struct sockaddr_un addrRemote;
333 socklen_t size = sizeof(struct sockaddr_un);
334 SOCKET cs = accept(s_pipe, (struct sockaddr *)&addrRemote, &size);
335 if (cs > 0)
336 {
337 ProcessRequest(cs);
338 shutdown(cs, 2);
339 close(cs);
340 }
341 else
342 {
343 AppAgentWriteLog(2, _T("AppAgentConnector: accept failed (%s)"), _tcserror(errno));
344 }
345 }
346
347 cleanup:
348 if (s_pipe != -1)
349 {
350 close(s_pipe);
351 s_pipe = INVALID_PIPE_HANDLE;
352 }
353
354 AppAgentWriteLog(2, _T("AppAgentConnector: listener thread stopped"));
355 return THREAD_OK;
356 }
357
358 #endif
359
360 #if defined(_AIX) || defined(__sun)
361
362 /**
363 * Dummy signal handler
364 */
365 static void DummySignalHandler(int s)
366 {
367 }
368
369 #endif
370
371 /**
372 * Initialize application agent
373 */
374 bool APPAGENT_EXPORTABLE AppAgentInit(APPAGENT_INIT *initData)
375 {
376 if (s_initialized)
377 return false; // already initialized
378
379 memcpy(&s_config, initData, sizeof(APPAGENT_INIT));
380 if ((s_config.name == NULL) || (s_config.name[0] == 0))
381 return false;
382
383 #if defined(_AIX) || defined(__sun)
384 signal(SIGUSR2, DummySignalHandler);
385 #endif
386
387 s_initialized = true;
388 return true;
389 }
390
391 /**
392 * Start application agent
393 */
394 void APPAGENT_EXPORTABLE AppAgentStart()
395 {
396 if ((s_initialized) && (s_connectorThread == INVALID_THREAD_HANDLE))
397 s_connectorThread = ThreadCreateEx(AppAgentConnector, 0, NULL);
398 }
399
400 /**
401 * Stop application agent
402 */
403 void APPAGENT_EXPORTABLE AppAgentStop()
404 {
405 if (s_initialized && (s_connectorThread != INVALID_THREAD_HANDLE))
406 {
407 s_stop = true;
408 if (s_pipe != INVALID_THREAD_HANDLE)
409 {
410 #ifdef _WIN32
411 CloseHandle(s_pipe);
412 #else
413 shutdown(s_pipe, SHUT_RDWR);
414 #if defined(_AIX) || defined(__sun)
415 pthread_kill(s_connectorThread, SIGUSR2);
416 #endif
417 #endif
418 s_pipe = INVALID_PIPE_HANDLE;
419 }
420 ThreadJoin(s_connectorThread);
421 s_connectorThread = INVALID_THREAD_HANDLE;
422 }
423 }
424
425 /**
426 * Post event
427 */
428 void APPAGENT_EXPORTABLE AppAgentPostEvent(int code, const TCHAR *name, const char *format, ...)
429 {
430 }