Added new agnet parameter: File.LineCount(*). Fixes #NX-1347
[public/netxms.git] / src / agent / core / sysinfo.cpp
1 /*
2 ** NetXMS multiplatform core agent
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: sysinfo.cpp
20 **/
21
22 #include "nxagentd.h"
23
24 #if HAVE_SYS_UTSNAME_H
25 #include <sys/utsname.h>
26 #endif
27
28 #include <nxstat.h>
29
30 /**
31 * Handler for System.CurrentTime parameter
32 */
33 LONG H_SystemTime(const TCHAR *cmd, const TCHAR *arg, TCHAR *value, AbstractCommSession *session)
34 {
35 ret_int64(value, (INT64)time(NULL));
36 return SYSINFO_RC_SUCCESS;
37 }
38
39 /**
40 * Handler for Agent.Uptime parameter
41 */
42 LONG H_AgentUptime(const TCHAR *cmd, const TCHAR *arg, TCHAR *value, AbstractCommSession *session)
43 {
44 ret_uint(value, (UINT32)(time(NULL) - g_tmAgentStartTime));
45 return SYSINFO_RC_SUCCESS;
46 }
47
48 /**
49 * File filter for GetDirInfo
50 */
51 static bool MatchFileFilter(const TCHAR *fileName, const NX_STAT_STRUCT &fileInfo, const TCHAR *pattern, int ageFilter, INT64 sizeFilter)
52 {
53 if (!MatchString(pattern, fileName, FALSE))
54 return false;
55
56 if (ageFilter != 0)
57 {
58 time_t now = time(NULL);
59 if (ageFilter < 0)
60 {
61 if (fileInfo.st_mtime < now + ageFilter)
62 return false;
63 }
64 else
65 {
66 if (fileInfo.st_mtime > now - ageFilter)
67 return false;
68 }
69 }
70
71 if (sizeFilter != 0)
72 {
73 if (sizeFilter < 0)
74 {
75 if (fileInfo.st_size > -sizeFilter)
76 return false;
77 }
78 else
79 {
80 if (fileInfo.st_size < sizeFilter)
81 return false;
82 }
83 }
84
85 return true;
86 }
87
88 /**
89 * Helper function for H_DirInfo
90 */
91 static LONG GetDirInfo(TCHAR *szPath, TCHAR *szPattern, bool bRecursive, unsigned int &uFileCount,
92 UINT64 &llFileSize, int ageFilter, INT64 sizeFilter, bool countFiles, bool countFolders)
93 {
94 _TDIR *pDir = NULL;
95 struct _tdirent *pFile;
96 NX_STAT_STRUCT fileInfo;
97 TCHAR szFileName[MAX_PATH];
98 LONG nRet = SYSINFO_RC_SUCCESS;
99
100 if (CALL_STAT(szPath, &fileInfo) == -1)
101 return SYSINFO_RC_ERROR;
102
103 // if this is just a file than simply return statistics
104 // Filters ignored in this case
105 if (!S_ISDIR(fileInfo.st_mode))
106 {
107 llFileSize += (UINT64)fileInfo.st_size;
108 uFileCount++;
109 return nRet;
110 }
111
112 // this is a dir
113 pDir = _topendir(szPath);
114 if (pDir != NULL)
115 {
116 while(1)
117 {
118 pFile = _treaddir(pDir);
119 if (pFile == NULL)
120 break;
121
122 if (!_tcscmp(pFile->d_name, _T(".")) || !_tcscmp(pFile->d_name, _T("..")))
123 continue;
124
125 size_t len = _tcslen(szPath) + _tcslen(pFile->d_name) + 2;
126 if (len > MAX_PATH)
127 continue; // Full file name is too long
128
129 _tcscpy(szFileName, szPath);
130 _tcscat(szFileName, FS_PATH_SEPARATOR);
131 _tcscat(szFileName, pFile->d_name);
132
133 // skip unaccessible entries
134 if (CALL_STAT(szFileName, &fileInfo) == -1)
135 continue;
136
137 // skip symlinks
138 #ifndef _WIN32
139 if (S_ISLNK(fileInfo.st_mode))
140 continue;
141 #endif
142
143 if (S_ISDIR(fileInfo.st_mode) && bRecursive)
144 {
145 nRet = GetDirInfo(szFileName, szPattern, bRecursive, uFileCount, llFileSize, ageFilter, sizeFilter, countFiles, countFolders);
146
147 if (nRet != SYSINFO_RC_SUCCESS)
148 break;
149 }
150
151 if (((countFiles && !S_ISDIR(fileInfo.st_mode)) || (countFolders && S_ISDIR(fileInfo.st_mode))) &&
152 MatchFileFilter(pFile->d_name, fileInfo, szPattern, ageFilter, sizeFilter))
153 {
154 llFileSize += (UINT64)fileInfo.st_size;
155 uFileCount++;
156 }
157 }
158 _tclosedir(pDir);
159 }
160
161 return nRet;
162 }
163
164 /**
165 * Handler for File.Size(*) and File.Count(*)
166 * Accepts the following arguments:
167 * path, pattern, recursive, size, age
168 * where
169 * path : path to directory or file to check
170 * pattern : pattern for file name matching
171 * recursive : recursion flag; if set to 1 or true, agent will scan subdirectories
172 * size : size filter; if < 0, only files with size less than abs(value) will match;
173 * if > 0, only files with size greater than value will match
174 * age : age filter; if < 0, only files created after now - abs(value) will match;
175 * if > 0, only files created before now - value will match
176 */
177 LONG H_DirInfo(const TCHAR *cmd, const TCHAR *arg, TCHAR *value, AbstractCommSession *session)
178 {
179 TCHAR szPath[MAX_PATH], szRealPath[MAX_PATH], szPattern[MAX_PATH], szRealPattern[MAX_PATH], szRecursive[10], szBuffer[128];
180 bool bRecursive = false;
181
182 unsigned int uFileCount = 0;
183 QWORD llFileSize = 0;
184 LONG nRet;
185
186 if (!AgentGetParameterArg(cmd, 1, szPath, MAX_PATH))
187 return SYSINFO_RC_UNSUPPORTED;
188 if (!AgentGetParameterArg(cmd, 2, szPattern, MAX_PATH))
189 return SYSINFO_RC_UNSUPPORTED;
190 if (!AgentGetParameterArg(cmd, 3, szRecursive, 10))
191 return SYSINFO_RC_UNSUPPORTED;
192
193 if (!AgentGetParameterArg(cmd, 4, szBuffer, 128))
194 return SYSINFO_RC_UNSUPPORTED;
195 INT64 sizeFilter = _tcstoll(szBuffer, NULL, 0);
196
197 if (!AgentGetParameterArg(cmd, 5, szBuffer, 128))
198 return SYSINFO_RC_UNSUPPORTED;
199 int ageFilter = _tcstoul(szBuffer, NULL, 0);
200
201 // Recursion flag
202 bRecursive = ((_tcstol(szRecursive, NULL, 0) != 0) || !_tcsicmp(szRecursive, _T("TRUE")));
203
204 // If pattern is omited use asterisk
205 if (szPattern[0] == 0)
206 _tcscpy(szPattern, _T("*"));
207
208 // Expand strftime macros in the path and in the pattern
209 if ((ExpandFileName(szPath, szRealPath, MAX_PATH, session == NULL ? false : session->isMasterServer()) == NULL) ||
210 (ExpandFileName(szPattern, szRealPattern, MAX_PATH, session == NULL ? false : session->isMasterServer()) == NULL))
211 return SYSINFO_RC_UNSUPPORTED;
212
213 int mode = CAST_FROM_POINTER(arg, int);
214 DebugPrintf(6, _T("H_DirInfo: path=\"%s\" pattern=\"%s\" recursive=%s mode=%d"), szRealPath, szRealPattern, bRecursive ? _T("true") : _T("false"), mode);
215
216 nRet = GetDirInfo(szRealPath, szRealPattern, bRecursive, uFileCount, llFileSize, ageFilter, sizeFilter, mode != DIRINFO_FOLDER_COUNT, mode == DIRINFO_FOLDER_COUNT);
217
218 switch(mode)
219 {
220 case DIRINFO_FILE_SIZE:
221 ret_uint64(value, llFileSize);
222 break;
223 case DIRINFO_FILE_COUNT:
224 case DIRINFO_FOLDER_COUNT:
225 ret_uint(value, uFileCount);
226 break;
227 default:
228 nRet = SYSINFO_RC_UNSUPPORTED;
229 break;
230 }
231
232 return nRet;
233 }
234
235 /**
236 * Calculate MD5 hash for file
237 */
238 LONG H_MD5Hash(const TCHAR *cmd, const TCHAR *arg, TCHAR *value, AbstractCommSession *session)
239 {
240 TCHAR szFileName[MAX_PATH], szRealFileName[MAX_PATH];
241 TCHAR szHashText[MD5_DIGEST_SIZE * 2 + 1];
242 BYTE hash[MD5_DIGEST_SIZE];
243 UINT32 i;
244
245 if (!AgentGetParameterArg(cmd, 1, szFileName, MAX_PATH))
246 return SYSINFO_RC_UNSUPPORTED;
247
248 // Expand strftime macros in the path
249 if (ExpandFileName(szFileName, szRealFileName, MAX_PATH, session == NULL ? false : session->isMasterServer()) == NULL)
250 return SYSINFO_RC_UNSUPPORTED;
251
252 if (!CalculateFileMD5Hash(szRealFileName, hash))
253 return SYSINFO_RC_UNSUPPORTED;
254
255 // Convert MD5 hash to text form
256 for(i = 0; i < MD5_DIGEST_SIZE; i++)
257 _sntprintf(&szHashText[i << 1], 4, _T("%02x"), hash[i]);
258
259 ret_string(value, szHashText);
260 return SYSINFO_RC_SUCCESS;
261 }
262
263 /**
264 * Calculate SHA1 hash for file
265 */
266 LONG H_SHA1Hash(const TCHAR *cmd, const TCHAR *arg, TCHAR *value, AbstractCommSession *session)
267 {
268 TCHAR szFileName[MAX_PATH], szRealFileName[MAX_PATH];
269 TCHAR szHashText[SHA1_DIGEST_SIZE * 2 + 1];
270 BYTE hash[SHA1_DIGEST_SIZE];
271 UINT32 i;
272
273 if (!AgentGetParameterArg(cmd, 1, szFileName, MAX_PATH))
274 return SYSINFO_RC_UNSUPPORTED;
275
276 // Expand strftime macros in the path
277 if (ExpandFileName(szFileName, szRealFileName, MAX_PATH, session == NULL ? false : session->isMasterServer()) == NULL)
278 return SYSINFO_RC_UNSUPPORTED;
279
280 if (!CalculateFileSHA1Hash(szRealFileName, hash))
281 return SYSINFO_RC_UNSUPPORTED;
282
283 // Convert SHA1 hash to text form
284 for(i = 0; i < SHA1_DIGEST_SIZE; i++)
285 _sntprintf(&szHashText[i << 1], 4, _T("%02x"), hash[i]);
286
287 ret_string(value, szHashText);
288 return SYSINFO_RC_SUCCESS;
289 }
290
291 /**
292 * Calculate CRC32 for file
293 */
294 LONG H_CRC32(const TCHAR *cmd, const TCHAR *arg, TCHAR *value, AbstractCommSession *session)
295 {
296 TCHAR szFileName[MAX_PATH], szRealFileName[MAX_PATH];
297 UINT32 dwCRC32;
298
299 if (!AgentGetParameterArg(cmd, 1, szFileName, MAX_PATH))
300 return SYSINFO_RC_UNSUPPORTED;
301
302 // Expand strftime macros in the path
303 if (ExpandFileName(szFileName, szRealFileName, MAX_PATH, session == NULL ? false : session->isMasterServer()) == NULL)
304 return SYSINFO_RC_UNSUPPORTED;
305
306 if (!CalculateFileCRC32(szRealFileName, &dwCRC32))
307 return SYSINFO_RC_UNSUPPORTED;
308
309 ret_uint(value, dwCRC32);
310 return SYSINFO_RC_SUCCESS;
311 }
312
313 /**
314 * Handler for System.PlatformName
315 */
316 LONG H_PlatformName(const TCHAR *cmd, const TCHAR *arg, TCHAR *value, AbstractCommSession *session)
317 {
318 LONG nResult = SYSINFO_RC_SUCCESS;
319
320 #if defined(_WIN32)
321
322 SYSTEM_INFO sysInfo;
323
324 GetSystemInfo(&sysInfo);
325 switch(sysInfo.wProcessorArchitecture)
326 {
327 case PROCESSOR_ARCHITECTURE_INTEL:
328 _tcscpy(value, _T("windows-i386"));
329 break;
330 case PROCESSOR_ARCHITECTURE_MIPS:
331 _tcscpy(value, _T("windows-mips"));
332 break;
333 case PROCESSOR_ARCHITECTURE_ALPHA:
334 _tcscpy(value, _T("windows-alpha"));
335 break;
336 case PROCESSOR_ARCHITECTURE_PPC:
337 _tcscpy(value, _T("windows-ppc"));
338 break;
339 case PROCESSOR_ARCHITECTURE_IA64:
340 _tcscpy(value, _T("windows-ia64"));
341 break;
342 case PROCESSOR_ARCHITECTURE_IA32_ON_WIN64:
343 _tcscpy(value, _T("windows-i386"));
344 break;
345 case PROCESSOR_ARCHITECTURE_AMD64:
346 _tcscpy(value, _T("windows-x64"));
347 break;
348 default:
349 _tcscpy(value, _T("windows-unknown"));
350 break;
351 }
352
353 #elif defined(_NETWARE)
354
355 // NetWare only exists on Intel x86 CPU,
356 // so there are nothing to detect
357 strcpy(value, "netware-i386");
358
359 #elif HAVE_UNAME
360
361 struct utsname info;
362
363 if (uname(&info) != -1)
364 {
365 #ifdef _AIX
366 // Assume that we are running on PowerPC
367 _sntprintf(value, MAX_RESULT_LENGTH, _T("%hs-powerpc"), info.sysname);
368 #else
369 _sntprintf(value, MAX_RESULT_LENGTH, _T("%hs-%hs"), info.sysname, info.machine);
370 #endif
371 }
372 else
373 {
374 DebugPrintf(2, _T("uname() failed: %s"), _tcserror(errno));
375 nResult = SYSINFO_RC_ERROR;
376 }
377
378 #else
379
380 // Finally, we don't know a way to detect platform
381 _tcscpy(value, _T("unknown"));
382
383 #endif
384
385 // Add user-configurable platform name suffix
386 if ((nResult == SYSINFO_RC_SUCCESS) && (g_szPlatformSuffix[0] != 0))
387 {
388 if (g_szPlatformSuffix[0] != _T('-'))
389 _tcscat(value, _T("-"));
390 _tcscat(value, g_szPlatformSuffix);
391 }
392
393 return nResult;
394 }
395
396 /**
397 * Handler for File.Time.* parameters
398 */
399 LONG H_FileTime(const TCHAR *cmd, const TCHAR *arg, TCHAR *value, AbstractCommSession *session)
400 {
401 TCHAR szFilePath[MAX_PATH], szRealFilePath[MAX_PATH];
402 LONG nRet = SYSINFO_RC_SUCCESS;
403 NX_STAT_STRUCT fileInfo;
404
405 if (!AgentGetParameterArg(cmd, 1, szFilePath, MAX_PATH))
406 return SYSINFO_RC_UNSUPPORTED;
407
408 // Expand strftime macros in the path
409 if (ExpandFileName(szFilePath, szRealFilePath, MAX_PATH, session == NULL ? false : session->isMasterServer()) == NULL)
410 return SYSINFO_RC_UNSUPPORTED;
411
412 if (CALL_STAT(szRealFilePath, &fileInfo) == -1)
413 return (errno == ENOENT) ? SYSINFO_RC_NO_SUCH_INSTANCE : SYSINFO_RC_ERROR;
414
415 switch(CAST_FROM_POINTER(arg, int))
416 {
417 case FILETIME_ATIME:
418 ret_uint64(value, fileInfo.st_atime);
419 break;
420 case FILETIME_MTIME:
421 ret_uint64(value, fileInfo.st_mtime);
422 break;
423 case FILETIME_CTIME:
424 ret_uint64(value, fileInfo.st_ctime);
425 break;
426 default:
427 nRet = SYSINFO_RC_UNSUPPORTED;
428 break;
429 }
430
431 return nRet;
432 }
433
434 /**
435 * Handler for Net.Resolver.AddressByName parameter
436 */
437 LONG H_ResolverAddrByName(const TCHAR *cmd, const TCHAR *arg, TCHAR *value, AbstractCommSession *session)
438 {
439 TCHAR name[256];
440 if (!AgentGetParameterArg(cmd, 1, name, 256))
441 return SYSINFO_RC_UNSUPPORTED;
442
443 InetAddress addr = InetAddress::resolveHostName(name);
444 addr.toString(value);
445 return SYSINFO_RC_SUCCESS;
446 }
447
448 /**
449 * Handler for Net.Resolver.NameByAddress parameter
450 */
451 LONG H_ResolverNameByAddr(const TCHAR *cmd, const TCHAR *arg, TCHAR *value, AbstractCommSession *session)
452 {
453 TCHAR name[256];
454 if (!AgentGetParameterArg(cmd, 1, name, 256))
455 return SYSINFO_RC_UNSUPPORTED;
456
457 InetAddress addr = InetAddress::parse(name);
458 if (!addr.isValid())
459 return SYSINFO_RC_UNSUPPORTED;
460
461 if (addr.getHostByAddr(value, MAX_RESULT_LENGTH) == NULL)
462 addr.toString(value); // return address itself if cannot be back resolved
463 return SYSINFO_RC_SUCCESS;
464 }
465
466 /**
467 * Get thread pool information
468 */
469 LONG H_ThreadPoolInfo(const TCHAR *param, const TCHAR *arg, TCHAR *value, AbstractCommSession *session)
470 {
471 TCHAR poolName[64], options[64];
472 if (!AgentGetParameterArg(param, 1, poolName, 64) ||
473 !AgentGetParameterArg(param, 2, options, 64))
474 return SYSINFO_RC_UNSUPPORTED;
475
476 ThreadPoolInfo info;
477 if (!ThreadPoolGetInfo(poolName, &info))
478 return SYSINFO_RC_UNSUPPORTED;
479
480 switch(CAST_FROM_POINTER(arg, int))
481 {
482 case THREAD_POOL_CURR_SIZE:
483 ret_int(value, info.curThreads);
484 break;
485 case THREAD_POOL_LOAD:
486 ret_int(value, info.load);
487 break;
488 case THREAD_POOL_LOADAVG_1:
489 if ((options[0] != 0) && _tcstol(options, NULL, 10))
490 ret_double(value, info.loadAvg[0] / info.maxThreads, 2);
491 else
492 ret_double(value, info.loadAvg[0], 2);
493 break;
494 case THREAD_POOL_LOADAVG_5:
495 if ((options[0] != 0) && _tcstol(options, NULL, 10))
496 ret_double(value, info.loadAvg[1] / info.maxThreads, 2);
497 else
498 ret_double(value, info.loadAvg[1], 2);
499 break;
500 case THREAD_POOL_LOADAVG_15:
501 if ((options[0] != 0) && _tcstol(options, NULL, 10))
502 ret_double(value, info.loadAvg[2] / info.maxThreads, 2);
503 else
504 ret_double(value, info.loadAvg[2], 2);
505 break;
506 case THREAD_POOL_MAX_SIZE:
507 ret_int(value, info.maxThreads);
508 break;
509 case THREAD_POOL_MIN_SIZE:
510 ret_int(value, info.minThreads);
511 break;
512 case THREAD_POOL_REQUESTS:
513 ret_int(value, info.activeRequests);
514 break;
515 case THREAD_POOL_USAGE:
516 ret_int(value, info.usage);
517 break;
518 default:
519 return SYSINFO_RC_UNSUPPORTED;
520 }
521 return SYSINFO_RC_SUCCESS;
522 }
523
524 /**
525 * Thread pool list
526 */
527 LONG H_ThreadPoolList(const TCHAR *cmd, const TCHAR *arg, StringList *value, AbstractCommSession *session)
528 {
529 StringList *pools = ThreadPoolGetAllPools();
530 value->addAll(pools);
531 delete pools;
532 return SYSINFO_RC_SUCCESS;
533 }
534
535 /**
536 * Handler for System.Hostname and System.FQDN parameters
537 */
538 LONG H_HostName(const TCHAR *metric, const TCHAR *arg, TCHAR *value, AbstractCommSession *session)
539 {
540 return (GetLocalHostName(value, MAX_RESULT_LENGTH, arg != NULL) != NULL) ? SYSINFO_RC_SUCCESS : SYSINFO_RC_ERROR;
541 }
542
543 /**
544 * Handler for File.LineCount parameter
545 */
546 LONG H_LineCount(const TCHAR *cmd, const TCHAR *arg, TCHAR *value, AbstractCommSession *session)
547 {
548 TCHAR szPath[MAX_PATH];
549 LONG nRet = SYSINFO_RC_ERROR;
550
551 if (!AgentGetParameterArg(cmd, 1, szPath, MAX_PATH))
552 return SYSINFO_RC_UNSUPPORTED;
553
554 if (szPath[0] != 0)
555 {
556 UINT32 lineCount = 0;
557 char buffer[4096];
558 int fd, in;
559 if ((fd = _topen(szPath, O_RDONLY)) != -1)
560 {
561 while((in = read(fd, buffer, 4096)) > 0)
562 {
563 buffer[in] = 0;
564 lineCount += NumCharsA(buffer, '\n');
565 }
566 close(fd);
567 lineCount++; // Last line will not have an '\n' char
568
569 ret_uint(value, lineCount);
570 nRet = SYSINFO_RC_SUCCESS;
571 }
572 }
573 return nRet;
574 }