preparation for object serialization to JSON
[public/netxms.git] / src / server / core / tools.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: tools.cpp
20 **
21 **/
22
23 #include "nxcore.h"
24
25 #ifdef _WIN32
26 # include <io.h>
27 #else
28 # ifdef HAVE_SYS_UTSNAME_H
29 # include <sys/utsname.h>
30 # endif
31 #endif
32
33 /**
34 * Get system information string
35 */
36 void GetSysInfoStr(TCHAR *pszBuffer, int nMaxSize)
37 {
38 #ifdef _WIN32
39 DWORD dwSize;
40 TCHAR computerName[MAX_COMPUTERNAME_LENGTH + 1] = _T("localhost"), osVersion[256] = _T("unknown");
41
42 dwSize = MAX_COMPUTERNAME_LENGTH + 1;
43 GetComputerName(computerName, &dwSize);
44
45 GetWindowsVersionString(osVersion, 256);
46 _sntprintf(pszBuffer, nMaxSize, _T("%s %s"), computerName, osVersion);
47 #else
48 # ifdef HAVE_SYS_UTSNAME_H
49 struct utsname uName;
50 if (uname(&uName) >= 0)
51 {
52 _sntprintf(pszBuffer, nMaxSize, _T("%hs %hs Release %hs"), uName.nodename, uName.sysname, uName.release);
53 }
54 else
55 {
56 #if HAVE_POSIX_STRERROR_R
57 _tcserror_r(errno, pszBuffer, nMaxSize);
58 #else
59 nx_strncpy(pszBuffer, _tcserror(errno), nMaxSize);
60 #endif
61 }
62 # else
63 _tprintf(_T("GetSysInfoStr: code not implemented\n"));
64 _tcscpy(pszBuffer, _T("UNIX"));
65 # endif // HAVE_SYS_UTSNAME_H
66
67 #endif
68 }
69
70 /**
71 * Get hostname for local machine
72 */
73 TCHAR *GetLocalHostName(TCHAR *buffer, size_t bufSize)
74 {
75 #ifdef _WIN32
76 DWORD dwSize = (UINT32)bufSize;
77 GetComputerName(buffer, &dwSize);
78 #else
79 #ifdef HAVE_SYS_UTSNAME_H
80 struct utsname uName;
81 if (uname(&uName) >= 0)
82 {
83 #ifdef UNICODE
84 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, uName.nodename, -1, buffer, bufSize);
85 buffer[bufSize - 1] = 0;
86 #else
87 nx_strncpy(buffer, uName.nodename, bufSize);
88 #endif
89 }
90 else
91 {
92 nx_strncpy(buffer, _T("unknown"), bufSize);
93 }
94 #else
95 nx_strncpy(buffer, _T("unknown"), bufSize);
96 #endif // HAVE_SYS_UTSNAME_H
97 #endif // _WIN32
98 return buffer;
99 }
100
101 /**
102 * Get IP address for local machine
103 */
104 InetAddress GetLocalIpAddr()
105 {
106 InetAddress addr;
107 InterfaceList *pIfList = GetLocalInterfaceList();
108 if (pIfList != NULL)
109 {
110 // Find first interface with IP address
111 for(int i = 0; i < pIfList->size(); i++)
112 {
113 InterfaceInfo *iface = pIfList->get(i);
114 if (iface->type == IFTYPE_SOFTWARE_LOOPBACK)
115 continue;
116 for(int j = 0; j < iface->ipAddrList.size(); j++)
117 {
118 const InetAddress& a = iface->ipAddrList.get(j);
119 if (a.isValidUnicast())
120 {
121 addr = a;
122 goto stop;
123 }
124 }
125 }
126 stop:
127 delete pIfList;
128 }
129 return addr;
130 }
131
132 /**
133 * Execute external command
134 */
135 BOOL ExecCommand(TCHAR *pszCommand)
136 {
137 BOOL bSuccess = TRUE;
138
139 #ifdef _WIN32
140 STARTUPINFO si;
141 PROCESS_INFORMATION pi;
142
143 // Fill in process startup info structure
144 memset(&si, 0, sizeof(STARTUPINFO));
145 si.cb = sizeof(STARTUPINFO);
146 si.dwFlags = 0;
147
148 // Create new process
149 if (!CreateProcess(NULL, pszCommand, NULL, NULL, FALSE, CREATE_NO_WINDOW | DETACHED_PROCESS, NULL, NULL, &si, &pi))
150 {
151 nxlog_write(MSG_CREATE_PROCESS_FAILED, EVENTLOG_ERROR_TYPE, "se", pszCommand, GetLastError());
152 bSuccess = FALSE;
153 }
154 else
155 {
156 // Close all handles
157 CloseHandle(pi.hThread);
158 CloseHandle(pi.hProcess);
159 }
160 #else
161 bSuccess = FALSE;
162 {
163 int nPid;
164 char *pCmd[128];
165 int nCount = 0;
166 char *pTmp;
167 struct stat st;
168 int state = 0;
169
170 #ifdef UNICODE
171 pTmp = MBStringFromWideString(pszCommand);
172 #else
173 pTmp = strdup(pszCommand);
174 #endif
175 if (pTmp != NULL)
176 {
177 pCmd[nCount++] = pTmp;
178 int nLen = strlen(pTmp);
179 for (int i = 0; (i < nLen) && (nCount < 127); i++)
180 {
181 switch(pTmp[i])
182 {
183 case ' ':
184 if (state == 0)
185 {
186 pTmp[i] = 0;
187 if (pTmp[i + 1] != 0)
188 {
189 pCmd[nCount++] = pTmp + i + 1;
190 }
191 }
192 break;
193 case '"':
194 state == 0 ? state++ : state--;
195
196 memmove(pTmp + i, pTmp + i + 1, nLen - i);
197 i--;
198 break;
199 case '\\':
200 if (pTmp[i+1] == '"')
201 {
202 memmove(pTmp + i, pTmp + i + 1, nLen - i);
203 }
204 break;
205 default:
206 break;
207 }
208 }
209 pCmd[nCount] = NULL;
210
211 if ((stat(pCmd[0], &st) == 0) && (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
212 {
213 switch ((nPid = fork()))
214 {
215 case -1:
216 nxlog_write(MSG_CREATE_PROCESS_FAILED, EVENTLOG_ERROR_TYPE, "se", pszCommand, errno);
217 break;
218 case 0: // child
219 {
220 int sd = open("/dev/null", O_RDWR);
221 if (sd == -1)
222 {
223 sd = open("/dev/null", O_RDONLY);
224 }
225 close(0);
226 close(1);
227 close(2);
228 dup2(sd, 0);
229 dup2(sd, 1);
230 dup2(sd, 2);
231 close(sd);
232 execv(pCmd[0], pCmd);
233 // should not be reached
234 //_exit((errno == EACCES || errno == ENOEXEC) ? 126 : 127);
235 _exit(127);
236 }
237 break;
238 default: // parent
239 bSuccess = TRUE;
240 break;
241 }
242 }
243
244 free(pTmp);
245 }
246 }
247 #endif
248
249 return bSuccess;
250 }
251
252 /**
253 * Send Wake-on-LAN packet (magic packet) to given IP address
254 * with given MAC address inside
255 */
256 BOOL SendMagicPacket(UINT32 dwIpAddr, BYTE *pbMacAddr, int iNumPackets)
257 {
258 BYTE *pCurr, bPacketData[102];
259 SOCKET hSocket;
260 struct sockaddr_in addr;
261 BOOL bResult = TRUE;
262 int i;
263
264 // Create data area
265 memset(bPacketData, 0xFF, 6);
266 for(i = 0, pCurr = bPacketData + 6; i < 16; i++, pCurr += 6)
267 memcpy(pCurr, pbMacAddr, 6);
268
269 // Create socket
270 hSocket = socket(AF_INET, SOCK_DGRAM, 0);
271 if (hSocket == INVALID_SOCKET)
272 {
273 DbgPrintf(5, _T("SendMagicPacket: ERROR creating socket: %s."), _tcserror(errno));
274 return FALSE;
275 }
276 SetSocketBroadcast(hSocket);
277
278 memset(&addr, 0, sizeof(struct sockaddr_in));
279 addr.sin_family = AF_INET;
280 addr.sin_addr.s_addr = dwIpAddr;
281 addr.sin_port = htons(53);
282
283 // Send requested number of packets
284 for(i = 0; i < iNumPackets; i++)
285 if (sendto(hSocket, (char *)bPacketData, 102, 0, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) < 0)
286 {
287 DbgPrintf(5, _T("SendMagicPacket: ERROR sending message: %s."), _tcserror(errno));
288 bResult = FALSE;
289 }
290
291 // Cleanup
292 closesocket(hSocket);
293 return bResult;
294 }
295
296 /**
297 * Decode SQL string and set as NXCP variable's value
298 */
299 void DecodeSQLStringAndSetVariable(NXCPMessage *pMsg, UINT32 dwVarId, TCHAR *pszStr)
300 {
301 DecodeSQLString(pszStr);
302 pMsg->setField(dwVarId, pszStr);
303 }
304
305 /**
306 * Escape string
307 */
308 void EscapeString(String &str)
309 {
310 str.escapeCharacter(_T('\\'), _T('\\'));
311 str.escapeCharacter(_T('"'), _T('\\'));
312 str.replace(_T("\b"), _T("\\b"));
313 str.replace(_T("\r"), _T("\\r"));
314 str.replace(_T("\n"), _T("\\n"));
315 str.replace(_T("\t"), _T("\\t"));
316 }
317
318 /**
319 * Prepare and execute SQL query with single binding - object ID.
320 */
321 bool NXCORE_EXPORTABLE ExecuteQueryOnObject(DB_HANDLE hdb, UINT32 objectId, const TCHAR *query)
322 {
323 DB_STATEMENT hStmt = DBPrepare(hdb, query);
324 if (hStmt == NULL)
325 return false;
326 DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, objectId);
327 bool success = DBExecute(hStmt) ? true : false;
328 DBFreeStatement(hStmt);
329 return success;
330 }
331
332 /**
333 * Resolve host name using zone if needed
334 */
335 InetAddress NXCORE_EXPORTABLE ResolveHostName(UINT32 zoneId, const TCHAR *hostname)
336 {
337 InetAddress ipAddr = InetAddress::parse(hostname);
338 if (!ipAddr.isValid() && IsZoningEnabled() && (zoneId != 0))
339 {
340 // resolve address through proxy agent
341 Zone *zone = FindZoneByGUID(zoneId);
342 if (zone != NULL)
343 {
344 Node *proxy = (Node *)FindObjectById(zone->getProxyNodeId(), OBJECT_NODE);
345 if (proxy != NULL)
346 {
347 TCHAR query[256], buffer[128];
348 _sntprintf(query, 256, _T("Net.Resolver.AddressByName(%s)"), hostname);
349 if (proxy->getItemFromAgent(query, 128, buffer) == ERR_SUCCESS)
350 {
351 ipAddr = InetAddress::parse(buffer);
352 }
353 }
354 }
355 }
356
357 // Resolve address through local resolver
358 if (!ipAddr.isValid())
359 {
360 ipAddr = InetAddress::resolveHostName(hostname);
361 }
362 return ipAddr;
363 }
364
365 /**
366 * Create object URL from NXCP message
367 */
368 ObjectUrl::ObjectUrl(NXCPMessage *msg, UINT32 baseId)
369 {
370 m_id = msg->getFieldAsUInt32(baseId);
371 m_url = msg->getFieldAsString(baseId + 1);
372 m_description = msg->getFieldAsString(baseId + 2);
373 }
374
375 /**
376 * Create object URL from database result set
377 */
378 ObjectUrl::ObjectUrl(DB_RESULT hResult, int row)
379 {
380 m_id = DBGetFieldULong(hResult, row, 0);
381 m_url = DBGetField(hResult, row, 1, NULL, 0);
382 m_description = DBGetField(hResult, row, 2, NULL, 0);
383 }
384
385 /**
386 * Object URL destructor
387 */
388 ObjectUrl::~ObjectUrl()
389 {
390 free(m_url);
391 free(m_description);
392 }
393
394 /**
395 * Fill NXCP message
396 */
397 void ObjectUrl::fillMessage(NXCPMessage *msg, UINT32 baseId)
398 {
399 msg->setField(baseId, m_id);
400 msg->setField(baseId + 1, m_url);
401 msg->setField(baseId + 2, m_description);
402 }
403
404 /**
405 * Distance array sorting callback
406 */
407 int DistanceSortCallback(const void *obj1, const void *obj2)
408 {
409 return ((ObjectsDistance *)obj1)->m_distance - ((ObjectsDistance *)obj2)->m_distance;
410 }
411
412 /**
413 * Calculate nearest objects from current one
414 * Object ref count will be automatically decreased on array delete
415 */
416 ObjectArray<ObjectsDistance> *FindNearestObjects(UINT32 currObjectId, int maxDistance, bool (* filter)(NetObj *object, void *data), void *sortData, int (* calculateRealDistance)(GeoLocation &loc1, GeoLocation &loc2))
417 {
418 NetObj *currObj = FindObjectById(currObjectId);
419 currObj->incRefCount();
420 GeoLocation currLocation = currObj->getGeoLocation();
421 if (currLocation.getType() == GL_UNSET)
422 {
423 currObj->decRefCount();
424 return NULL;
425 }
426
427 ObjectArray<NetObj> *objects = g_idxObjectById.getObjects(true, filter, sortData);
428 ObjectArray<ObjectsDistance> *result = new ObjectArray<ObjectsDistance>(16, 16, true);
429 for(int i = 0; i < objects->size(); i++)
430 {
431 NetObj *object = objects->get(i);
432 GeoLocation location = object->getGeoLocation();
433 if (currLocation.getType() == GL_UNSET)
434 {
435 object->decRefCount();
436 continue;
437 }
438
439 // leave object only in given distance
440 int distance = currLocation.calculateDistance(location);
441 if (distance > maxDistance)
442 {
443 object->decRefCount();
444 continue;
445 }
446
447 // remove current object from list
448 if (object->getId() == currObjectId)
449 {
450 object->decRefCount();
451 continue;
452 }
453
454 // Filter objects by real path calculation
455 if (calculateRealDistance != NULL)
456 {
457 distance = calculateRealDistance(location, currLocation);
458 if(distance > maxDistance)
459 {
460 object->decRefCount();
461 continue;
462 }
463 }
464
465 result->add(new ObjectsDistance(object, distance));
466 }
467
468 // Sort filtered objects
469 result->sort(DistanceSortCallback);
470
471 currObj->decRefCount();
472 return result;
473 }