added quote character escape in function AgentGetParameterArg
[public/netxms.git] / src / libnetxms / tools.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Copyright (C) 2003-2015 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
7 ** by 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: tools.cpp
20 **
21 **/
22
23 #include "libnetxms.h"
24 #include <stdarg.h>
25 #include <nms_agent.h>
26 #include <nms_threads.h>
27 #include <netxms-regex.h>
28 #include <nxstat.h>
29
30 #ifdef _WIN32
31 #include <psapi.h>
32 #define read _read
33 #define close _close
34 #define wcsicmp _wcsicmp
35 #endif
36
37 #if !defined(_WIN32) && !defined(UNDER_CE)
38 #include <sys/time.h>
39 #endif
40
41 #if HAVE_SYS_UTSNAME_H
42 #include <sys/utsname.h>
43 #endif
44
45 #if HAVE_POLL_H
46 #include <poll.h>
47 #endif
48
49 #if HAVE_MALLOC_H && !WITH_JEMALLOC
50 #include <malloc.h>
51 #endif
52
53 #if HAVE_LOCALE_H
54 #include <locale.h>
55 #endif
56
57 #ifdef _WIN32
58 # ifndef __GNUC__
59 # define EPOCHFILETIME (116444736000000000i64)
60 # else
61 # define EPOCHFILETIME (116444736000000000LL)
62 # endif
63 #endif
64
65 /**
66 * Common initialization for any NetXMS process
67 */
68 void LIBNETXMS_EXPORTABLE InitNetXMSProcess()
69 {
70 InitThreadLibrary();
71
72 // Set locale to C. It shouldn't be needed, according to
73 // documentation, but I've seen the cases when agent formats
74 // floating point numbers by sprintf inserting comma in place
75 // of a dot, as set by system's regional settings.
76 #if HAVE_SETLOCALE
77 setlocale(LC_NUMERIC, "C");
78 #if defined(UNICODE) && !defined(_WIN32)
79 const char *locale = getenv("LC_CTYPE");
80 if (locale == NULL)
81 locale = getenv("LC_ALL");
82 if (locale == NULL)
83 locale = getenv("LANG");
84 if (locale != NULL)
85 setlocale(LC_CTYPE, locale);
86 #endif
87 #endif
88
89 #ifdef NETXMS_MEMORY_DEBUG
90 InitMemoryDebugger();
91 #endif
92
93 #ifdef _WIN32
94 SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX);
95 #endif
96 }
97
98 /**
99 * Calculate number of bits in netmask (in host byte order)
100 */
101 int LIBNETXMS_EXPORTABLE BitsInMask(UINT32 dwMask)
102 {
103 int bits;
104 UINT32 dwTemp;
105
106 for(bits = 0, dwTemp = dwMask; dwTemp != 0; bits++, dwTemp <<= 1);
107 return bits;
108 }
109
110 /**
111 * Convert IP address from binary form (host bytes order) to string
112 */
113 TCHAR LIBNETXMS_EXPORTABLE *IpToStr(UINT32 dwAddr, TCHAR *szBuffer)
114 {
115 static TCHAR szInternalBuffer[32];
116 TCHAR *szBufPtr;
117
118 szBufPtr = (szBuffer == NULL) ? szInternalBuffer : szBuffer;
119 _sntprintf(szBufPtr, 32, _T("%d.%d.%d.%d"), (int)(dwAddr >> 24), (int)((dwAddr >> 16) & 255),
120 (int)((dwAddr >> 8) & 255), (int)(dwAddr & 255));
121 return szBufPtr;
122 }
123
124 #ifdef UNICODE
125
126 char LIBNETXMS_EXPORTABLE *IpToStrA(UINT32 dwAddr, char *szBuffer)
127 {
128 static char szInternalBuffer[32];
129 char *szBufPtr;
130
131 szBufPtr = (szBuffer == NULL) ? szInternalBuffer : szBuffer;
132 snprintf(szBufPtr, 32, "%d.%d.%d.%d", (int)(dwAddr >> 24), (int)((dwAddr >> 16) & 255),
133 (int)((dwAddr >> 8) & 255), (int)(dwAddr & 255));
134 return szBufPtr;
135 }
136
137 #endif
138
139 /**
140 * Universal IPv4/IPv6 to string converter
141 */
142 TCHAR LIBNETXMS_EXPORTABLE *SockaddrToStr(struct sockaddr *addr, TCHAR *buffer)
143 {
144 switch(addr->sa_family)
145 {
146 case AF_INET:
147 return IpToStr(ntohl(((struct sockaddr_in *)addr)->sin_addr.s_addr), buffer);
148 case AF_INET6:
149 return Ip6ToStr(((struct sockaddr_in6 *)addr)->sin6_addr.s6_addr, buffer);
150 default:
151 buffer[0] = 0;
152 return buffer;
153 }
154 }
155
156 /**
157 * Convert IPv6 address from binary form to string
158 */
159 TCHAR LIBNETXMS_EXPORTABLE *Ip6ToStr(const BYTE *addr, TCHAR *buffer)
160 {
161 static TCHAR internalBuffer[64];
162 TCHAR *bufPtr = (buffer == NULL) ? internalBuffer : buffer;
163
164 if (!memcmp(addr, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16))
165 {
166 _tcscpy(bufPtr, _T("::"));
167 return bufPtr;
168 }
169
170 TCHAR *out = bufPtr;
171 WORD *curr = (WORD *)addr;
172 bool hasNulls = false;
173 for(int i = 0; i < 8; i++)
174 {
175 WORD value = ntohs(*curr);
176 if ((value != 0) || hasNulls)
177 {
178 if (out != bufPtr)
179 *out++ = _T(':');
180 _sntprintf(out, 5, _T("%x"), value);
181 out = bufPtr + _tcslen(bufPtr);
182 curr++;
183 }
184 else
185 {
186 *out++ = _T(':');
187 do
188 {
189 i++;
190 curr++;
191 }
192 while((*curr == 0) && (i < 8));
193 if (i == 8)
194 {
195 *out++ = _T(':');
196 break;
197 }
198 i--;
199 hasNulls = true;
200 }
201 }
202 *out = 0;
203 return bufPtr;
204 }
205
206 #ifdef UNICODE
207
208 /**
209 * Convert IPv6 address from binary form to string
210 */
211 char LIBNETXMS_EXPORTABLE *Ip6ToStrA(const BYTE *addr, char *buffer)
212 {
213 static char internalBuffer[64];
214 char *bufPtr = (buffer == NULL) ? internalBuffer : buffer;
215
216 if (!memcmp(addr, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16))
217 {
218 strcpy(bufPtr, "::");
219 return bufPtr;
220 }
221
222 char *out = bufPtr;
223 WORD *curr = (WORD *)addr;
224 bool hasNulls = false;
225 for(int i = 0; i < 8; i++)
226 {
227 WORD value = ntohs(*curr);
228 if ((value != 0) || hasNulls)
229 {
230 if (out != bufPtr)
231 *out++ = ':';
232 snprintf(out, 5, "%x", value);
233 out = bufPtr + strlen(bufPtr);
234 curr++;
235 }
236 else
237 {
238 *out++ = ':';
239 do
240 {
241 i++;
242 curr++;
243 }
244 while((*curr == 0) && (i < 8));
245 if (i == 8)
246 {
247 *out++ = ':';
248 break;
249 }
250 i--;
251 hasNulls = true;
252 }
253 }
254 *out = 0;
255 return bufPtr;
256 }
257
258 #endif
259
260 /**
261 * Duplicate memory block
262 */
263 void LIBNETXMS_EXPORTABLE *nx_memdup(const void *data, size_t size)
264 {
265 void *newData;
266
267 newData = malloc(size);
268 memcpy(newData, data, size);
269 return newData;
270 }
271
272 /**
273 * Swap two memory blocks
274 */
275 void LIBNETXMS_EXPORTABLE nx_memswap(void *block1, void *block2, size_t size)
276 {
277 void *temp;
278
279 temp = malloc(size);
280 memcpy(temp, block1, size);
281 memcpy(block1, block2, size);
282 memcpy(block2, temp, size);
283 free(temp);
284 }
285
286 #if defined(_WIN32) && defined(USE_WIN32_HEAP)
287
288 /**
289 * Copy string
290 */
291 char LIBNETXMS_EXPORTABLE *nx_strdup(const char *src)
292 {
293 return (char *)nx_memdup(src, strlen(src) + 1);
294 }
295
296 /**
297 * Copy string
298 */
299 WCHAR LIBNETXMS_EXPORTABLE *nx_wcsdup(const WCHAR *src)
300 {
301 return (WCHAR *)nx_memdup(src, (wcslen(src) + 1) * sizeof(WCHAR));
302 }
303
304 #endif
305
306 #if !HAVE_WCSDUP && !defined(_WIN32)
307
308 /**
309 * Copy string
310 */
311 WCHAR LIBNETXMS_EXPORTABLE *wcsdup(const WCHAR *src)
312 {
313 return (WCHAR *)nx_memdup(src, (wcslen(src) + 1) * sizeof(WCHAR));
314 }
315
316 #elif defined(_AIX)
317
318 /**
319 * Copy string
320 */
321 WCHAR LIBNETXMS_EXPORTABLE *nx_wcsdup(const WCHAR *src)
322 {
323 return (WCHAR *)nx_memdup(src, (wcslen(src) + 1) * sizeof(WCHAR));
324 }
325
326 #endif
327
328 /**
329 * Compare pattern with possible ? characters with given text
330 */
331 static bool CompareTextBlocks(const TCHAR *pattern, const TCHAR *text, size_t size)
332 {
333 const TCHAR *p = pattern;
334 const TCHAR *t = text;
335 for(size_t i = size; i > 0; i--, p++, t++)
336 {
337 if (*p == _T('?'))
338 continue;
339 if (*p != *t)
340 return false;
341 }
342 return true;
343 }
344
345 /**
346 * Match string against pattern with * and ? metasymbols - implementation
347 */
348 static bool MatchStringEngine(const TCHAR *pattern, const TCHAR *string)
349 {
350 const TCHAR *SPtr, *MPtr, *BPtr, *EPtr;
351 size_t bsize;
352 bool finishScan;
353
354 SPtr = string;
355 MPtr = pattern;
356
357 while(*MPtr != 0)
358 {
359 switch(*MPtr)
360 {
361 case _T('?'):
362 if (*SPtr != 0)
363 {
364 SPtr++;
365 MPtr++;
366 }
367 else
368 {
369 return false;
370 }
371 break;
372 case _T('*'):
373 while(*MPtr == _T('*'))
374 MPtr++;
375 if (*MPtr == 0)
376 return true;
377 while(*MPtr == _T('?')) // Handle "*?" case
378 {
379 if (*SPtr != 0)
380 SPtr++;
381 else
382 return false;
383 MPtr++;
384 }
385 BPtr = MPtr; // Text block begins here
386 while((*MPtr != 0) && (*MPtr != _T('*')))
387 MPtr++; // Find the end of text block
388 // Try to find rightmost matching block
389 bsize = (size_t)(MPtr - BPtr);
390 EPtr = NULL;
391 finishScan = false;
392 do
393 {
394 while(1)
395 {
396 while((*SPtr != 0) && (*SPtr != *BPtr))
397 SPtr++;
398 if (_tcslen(SPtr) < bsize)
399 {
400 if (EPtr == NULL)
401 {
402 return false; // Length of remained text less than remaining pattern
403 }
404 else
405 {
406 SPtr = EPtr; // Revert back to point after last match
407 finishScan = true;
408 break;
409 }
410 }
411 if (CompareTextBlocks(BPtr, SPtr, bsize))
412 break;
413 SPtr++;
414 }
415 if (!finishScan)
416 {
417 EPtr = SPtr + bsize; // Remember point after last match
418 SPtr++; // continue scan at next character
419 }
420 }
421 while(!finishScan);
422 break;
423 default:
424 if (*MPtr == *SPtr)
425 {
426 SPtr++;
427 MPtr++;
428 }
429 else
430 {
431 return false;
432 }
433 break;
434 }
435 }
436
437 return *SPtr == 0;
438 }
439
440 /**
441 * Match string against pattern with * and ? metasymbols
442 *
443 * @param pattern pattern to match against
444 * @param str string to match
445 * @param matchCase set to true for case-sensetive match
446 * @return true if string matches given pattern
447 */
448 bool LIBNETXMS_EXPORTABLE MatchString(const TCHAR *pattern, const TCHAR *str, bool matchCase)
449 {
450 if (matchCase)
451 return MatchStringEngine(pattern, str);
452
453 TCHAR *tp, *ts;
454 bool bResult;
455
456 tp = _tcsdup(pattern);
457 ts = _tcsdup(str);
458 _tcsupr(tp);
459 _tcsupr(ts);
460 bResult = MatchStringEngine(tp, ts);
461 free(tp);
462 free(ts);
463 return bResult;
464 }
465
466 /**
467 * Strip whitespaces and tabs off the string
468 */
469 void LIBNETXMS_EXPORTABLE StrStripA(char *str)
470 {
471 int i;
472
473 for(i = 0; (str[i]!=0) && ((str[i] == ' ') || (str[i] == '\t')); i++);
474 if (i > 0)
475 memmove(str, &str[i], strlen(&str[i]) + 1);
476 for(i = (int)strlen(str) - 1; (i >= 0) && ((str[i] == ' ') || (str[i] == '\t')); i--);
477 str[i + 1] = 0;
478 }
479
480 /**
481 * Strip whitespaces and tabs off the string
482 */
483 void LIBNETXMS_EXPORTABLE StrStripW(WCHAR *str)
484 {
485 int i;
486
487 for(i = 0; (str[i]!=0) && ((str[i] == L' ') || (str[i] == L'\t')); i++);
488 if (i > 0)
489 memmove(str, &str[i], (wcslen(&str[i]) + 1) * sizeof(WCHAR));
490 for(i = (int)wcslen(str) - 1; (i >= 0) && ((str[i] == L' ') || (str[i] == L'\t')); i--);
491 str[i + 1] = 0;
492 }
493
494 /**
495 * Strip whitespaces and tabs off the string.
496 *
497 * @param str string to trim
498 * @return str for convenience
499 */
500 TCHAR LIBNETXMS_EXPORTABLE *Trim(TCHAR *str)
501 {
502 if (str == NULL)
503 return NULL;
504
505 int i;
506 for(i = 0; (str[i] != 0) && _istspace(str[i]); i++);
507 if (i > 0)
508 memmove(str, &str[i], (_tcslen(&str[i]) + 1) * sizeof(TCHAR));
509 for(i = (int)_tcslen(str) - 1; (i >= 0) && _istspace(str[i]); i--);
510 str[i + 1] = 0;
511 return str;
512 }
513
514 /**
515 * Remove trailing CR/LF or LF from string (multibyte version)
516 */
517 void LIBNETXMS_EXPORTABLE RemoveTrailingCRLFA(char *str)
518 {
519 if (*str == 0)
520 return;
521
522 char *p = str + strlen(str) - 1;
523 if (*p == '\n')
524 p--;
525 if (*p == '\r')
526 p--;
527 *(p + 1) = 0;
528 }
529
530 /**
531 * Remove trailing CR/LF or LF from string (UNICODE version)
532 */
533 void LIBNETXMS_EXPORTABLE RemoveTrailingCRLFW(WCHAR *str)
534 {
535 if (*str == 0)
536 return;
537
538 WCHAR *p = str + wcslen(str) - 1;
539 if (*p == L'\n')
540 p--;
541 if (*p == L'\r')
542 p--;
543 *(p + 1) = 0;
544 }
545
546 /**
547 * Expand file name. Source and destiation may point to same location.
548 * Can be used strftime placeholders and external commands enclosed in ``
549 */
550 const TCHAR LIBNETXMS_EXPORTABLE *ExpandFileName(const TCHAR *name, TCHAR *buffer, size_t bufSize, bool allowShellCommands)
551 {
552 time_t t;
553 struct tm *ltm;
554 #if HAVE_LOCALTIME_R
555 struct tm tmBuff;
556 #endif
557 TCHAR temp[8192], command[1024];
558 size_t outpos = 0;
559
560 t = time(NULL);
561 #if HAVE_LOCALTIME_R
562 ltm = localtime_r(&t, &tmBuff);
563 #else
564 ltm = localtime(&t);
565 #endif
566 if (_tcsftime(temp, 8192, name, ltm) <= 0)
567 return NULL;
568
569 for(int i = 0; (temp[i] != 0) && (outpos < bufSize - 1); i++)
570 {
571 if (temp[i] == _T('`') && allowShellCommands)
572 {
573 int j = ++i;
574 while((temp[j] != _T('`')) && (temp[j] != 0))
575 j++;
576 int len = min(j - i, 1023);
577 memcpy(command, &temp[i], len * sizeof(TCHAR));
578 command[len] = 0;
579
580 FILE *p = _tpopen(command, _T("r"));
581 if (p != NULL)
582 {
583 char result[1024];
584
585 int rc = (int)fread(result, 1, 1023, p);
586 pclose(p);
587
588 if (rc > 0)
589 {
590 result[rc] = 0;
591 char *lf = strchr(result, '\n');
592 if (lf != NULL)
593 *lf = 0;
594
595 len = (int)min(strlen(result), bufSize - outpos - 1);
596 #ifdef UNICODE
597 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, result, len, &buffer[outpos], len);
598 #else
599 memcpy(&buffer[outpos], result, len);
600 #endif
601 outpos += len;
602 }
603 }
604
605 i = j;
606 }
607 else if (temp[i] == _T('$') && temp[i + 1] == _T('{'))
608 {
609 i += 2;
610 int j = i;
611 while((temp[j] != _T('}')) && (temp[j] != 0))
612 j++;
613
614 int len = min(j - i, 1023);
615
616 TCHAR varName[256];
617 memcpy(varName, &temp[i], len * sizeof(TCHAR));
618 varName[len] = 0;
619
620 TCHAR *result = _tgetenv(varName);
621 if (result != NULL)
622 {
623 len = (int)min(_tcslen(result), bufSize - outpos - 1);
624 memcpy(&buffer[outpos], result, len * sizeof(TCHAR));
625 }
626 else
627 {
628 len = 0;
629 }
630
631 outpos += len;
632 i = j;
633 }
634 else
635 {
636 buffer[outpos++] = temp[i];
637 }
638 }
639
640 buffer[outpos] = 0;
641 return buffer;
642 }
643
644 /**
645 * Create folder
646 */
647 BOOL LIBNETXMS_EXPORTABLE CreateFolder(const TCHAR *directory)
648 {
649 NX_STAT_STRUCT st;
650 TCHAR *previous = _tcsdup(directory);
651 TCHAR *ptr = _tcsrchr(previous, FS_PATH_SEPARATOR_CHAR);
652 BOOL result = FALSE;
653 if (ptr != NULL)
654 {
655 *ptr = 0;
656 if (CALL_STAT(previous, &st) != 0)
657 {
658 result = CreateFolder(previous);
659 if (result)
660 {
661 result = (CALL_STAT(previous, &st) == 0);
662 }
663 }
664 else
665 {
666 if (S_ISDIR(st.st_mode))
667 {
668 result = TRUE;
669 }
670 }
671 }
672 else
673 {
674 result = true;
675 st.st_mode = 0700;
676 }
677 safe_free(previous);
678
679 if (result)
680 {
681 #ifdef _WIN32
682 result = CreateDirectory(directory, NULL);
683 #else
684 result = (_tmkdir(directory, st.st_mode) == 0);
685 #endif /* _WIN32 */
686 }
687
688 return result;
689 }
690
691 /**
692 * Get current time in milliseconds
693 * Based on timeval.h by Wu Yongwei
694 */
695 INT64 LIBNETXMS_EXPORTABLE GetCurrentTimeMs()
696 {
697 #ifdef _WIN32
698 FILETIME ft;
699 LARGE_INTEGER li;
700 __int64 t;
701
702 GetSystemTimeAsFileTime(&ft);
703 li.LowPart = ft.dwLowDateTime;
704 li.HighPart = ft.dwHighDateTime;
705 t = li.QuadPart; // In 100-nanosecond intervals
706 t -= EPOCHFILETIME; // Offset to the Epoch time
707 t /= 10000; // Convert to milliseconds
708 #else
709 struct timeval tv;
710 INT64 t;
711
712 gettimeofday(&tv, NULL);
713 t = (INT64)tv.tv_sec * 1000 + (INT64)(tv.tv_usec / 1000);
714 #endif
715
716 return t;
717 }
718
719 /**
720 * Extract word from line (UNICODE version). Extracted word will be placed in buffer.
721 * Returns pointer to the next word or to the null character if end
722 * of line reached.
723 */
724 const WCHAR LIBNETXMS_EXPORTABLE *ExtractWordW(const WCHAR *line, WCHAR *buffer)
725 {
726 const WCHAR *ptr;
727 WCHAR *bptr;
728
729 for(ptr = line; (*ptr == L' ') || (*ptr == L'\t'); ptr++); // Skip initial spaces
730 // Copy word to buffer
731 for(bptr = buffer; (*ptr != L' ') && (*ptr != L'\t') && (*ptr != 0); ptr++, bptr++)
732 *bptr = *ptr;
733 *bptr = 0;
734 return ptr;
735 }
736
737 /**
738 * Extract word from line (multibyte version). Extracted word will be placed in buffer.
739 * Returns pointer to the next word or to the null character if end
740 * of line reached.
741 */
742 const char LIBNETXMS_EXPORTABLE *ExtractWordA(const char *line, char *buffer)
743 {
744 const char *ptr;
745 char *bptr;
746
747 for(ptr = line; (*ptr == ' ') || (*ptr == '\t'); ptr++); // Skip initial spaces
748 // Copy word to buffer
749 for(bptr = buffer; (*ptr != ' ') && (*ptr != '\t') && (*ptr != 0); ptr++, bptr++)
750 *bptr = *ptr;
751 *bptr = 0;
752 return ptr;
753 }
754
755 #if defined(_WIN32)
756
757 /**
758 * Get system error string by call to FormatMessage
759 * (Windows only)
760 */
761 TCHAR LIBNETXMS_EXPORTABLE *GetSystemErrorText(UINT32 dwError, TCHAR *pszBuffer, size_t iBufSize)
762 {
763 TCHAR *msgBuf;
764
765 if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
766 FORMAT_MESSAGE_FROM_SYSTEM |
767 FORMAT_MESSAGE_IGNORE_INSERTS,
768 NULL, dwError,
769 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
770 (LPTSTR)&msgBuf, 0, NULL) > 0)
771 {
772 msgBuf[_tcscspn(msgBuf, _T("\r\n"))] = 0;
773 nx_strncpy(pszBuffer, msgBuf, iBufSize);
774 LocalFree(msgBuf);
775 }
776 else
777 {
778 _sntprintf(pszBuffer, iBufSize, _T("MSG 0x%08X - Unable to find message text"), dwError);
779 }
780 return pszBuffer;
781 }
782
783 #endif
784
785 #if (!HAVE_DAEMON || !HAVE_DECL_DAEMON) && !defined(_NETWARE) && !defined(_WIN32)
786
787 /**
788 * daemon() implementation for systems which doesn't have one
789 */
790 int LIBNETXMS_EXPORTABLE __daemon(int nochdir, int noclose)
791 {
792 int pid;
793
794 if ((pid = fork()) < 0)
795 return -1;
796 if (pid != 0)
797 exit(0); // Terminate parent
798
799 setsid();
800
801 if (!nochdir)
802 chdir("/");
803
804 if (!noclose)
805 {
806 fclose(stdin); // don't need stdin, stdout, stderr
807 fclose(stdout);
808 fclose(stderr);
809 }
810
811 return 0;
812 }
813
814 #endif
815
816 /**
817 * Check if given name is a valid object name
818 */
819 BOOL LIBNETXMS_EXPORTABLE IsValidObjectName(const TCHAR *pszName, BOOL bExtendedChars)
820 {
821 static TCHAR szValidCharacters[] = _T("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_- @()./");
822 static TCHAR szInvalidCharacters[] = _T("\x01\x02\x03\x04\x05\x06\x07")
823 _T("\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F")
824 _T("\x10\x11\x12\x13\x14\x15\x16\x17")
825 _T("\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F")
826 _T("|\"'*%#\\`;?<>=");
827
828 return (pszName[0] != 0) && (bExtendedChars ? (_tcscspn(pszName, szInvalidCharacters) == _tcslen(pszName)) : (_tcsspn(pszName, szValidCharacters) == _tcslen(pszName)));
829 }
830
831 /**
832 * Check if given name is a valid script name
833 */
834 BOOL LIBNETXMS_EXPORTABLE IsValidScriptName(const TCHAR *pszName)
835 {
836 static TCHAR szValidCharacters[] = _T("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_:");
837 return (pszName[0] != 0) && (!isdigit(pszName[0])) && (pszName[0] != ':') &&
838 (_tcsspn(pszName, szValidCharacters) == _tcslen(pszName));
839 }
840
841 /**
842 * Convert 6-byte MAC address to text representation
843 */
844 TCHAR LIBNETXMS_EXPORTABLE *MACToStr(const BYTE *pData, TCHAR *pStr)
845 {
846 UINT32 i;
847 TCHAR *pCurr;
848
849 for(i = 0, pCurr = pStr; i < 6; i++)
850 {
851 *pCurr++ = bin2hex(pData[i] >> 4);
852 *pCurr++ = bin2hex(pData[i] & 15);
853 *pCurr++ = _T(':');
854 }
855 *(pCurr - 1) = 0;
856 return pStr;
857 }
858
859 /**
860 * Convert byte array to text representation (wide character version)
861 */
862 WCHAR LIBNETXMS_EXPORTABLE *BinToStrW(const BYTE *pData, size_t size, WCHAR *pStr)
863 {
864 size_t i;
865 WCHAR *pCurr;
866
867 for(i = 0, pCurr = pStr; i < size; i++)
868 {
869 *pCurr++ = bin2hex(pData[i] >> 4);
870 *pCurr++ = bin2hex(pData[i] & 15);
871 }
872 *pCurr = 0;
873 return pStr;
874 }
875
876 /**
877 * Convert byte array to text representation (multibyte character version)
878 */
879 char LIBNETXMS_EXPORTABLE *BinToStrA(const BYTE *pData, size_t size, char *pStr)
880 {
881 size_t i;
882 char *pCurr;
883
884 for(i = 0, pCurr = pStr; i < size; i++)
885 {
886 *pCurr++ = bin2hex(pData[i] >> 4);
887 *pCurr++ = bin2hex(pData[i] & 15);
888 }
889 *pCurr = 0;
890 return pStr;
891 }
892
893 /**
894 * Convert string of hexadecimal digits to byte array (wide character version)
895 * Returns number of bytes written to destination
896 */
897 size_t LIBNETXMS_EXPORTABLE StrToBinW(const WCHAR *pStr, BYTE *pData, size_t size)
898 {
899 size_t i;
900 const WCHAR *pCurr;
901
902 memset(pData, 0, size);
903 for(i = 0, pCurr = pStr; (i < size) && (*pCurr != 0); i++)
904 {
905 pData[i] = hex2bin(*pCurr) << 4;
906 pCurr++;
907 if (*pCurr != 0)
908 {
909 pData[i] |= hex2bin(*pCurr);
910 pCurr++;
911 }
912 }
913 return i;
914 }
915
916 /**
917 * Convert string of hexadecimal digits to byte array (multibyte character version)
918 * Returns number of bytes written to destination
919 */
920 size_t LIBNETXMS_EXPORTABLE StrToBinA(const char *pStr, BYTE *pData, size_t size)
921 {
922 size_t i;
923 const char *pCurr;
924
925 memset(pData, 0, size);
926 for(i = 0, pCurr = pStr; (i < size) && (*pCurr != 0); i++)
927 {
928 pData[i] = hex2bin(*pCurr) << 4;
929 pCurr++;
930 if (*pCurr != 0)
931 {
932 pData[i] |= hex2bin(*pCurr);
933 pCurr++;
934 }
935 }
936 return i;
937 }
938
939 /**
940 * Translate string
941 * NOTE: replacement string shouldn't be longer than original
942 */
943 void LIBNETXMS_EXPORTABLE TranslateStr(TCHAR *pszString, const TCHAR *pszSubStr, const TCHAR *pszReplace)
944 {
945 TCHAR *pszSrc, *pszDst;
946 int iSrcLen, iRepLen;
947
948 iSrcLen = (int)_tcslen(pszSubStr);
949 iRepLen = (int)_tcslen(pszReplace);
950 for(pszSrc = pszString, pszDst = pszString; *pszSrc != 0;)
951 {
952 if (!_tcsncmp(pszSrc, pszSubStr, iSrcLen))
953 {
954 memcpy(pszDst, pszReplace, sizeof(TCHAR) * iRepLen);
955 pszSrc += iSrcLen;
956 pszDst += iRepLen;
957 }
958 else
959 {
960 *pszDst++ = *pszSrc++;
961 }
962 }
963 *pszDst = 0;
964 }
965
966 /**
967 * Get size of file in bytes
968 */
969 QWORD LIBNETXMS_EXPORTABLE FileSizeW(const WCHAR *pszFileName)
970 {
971 #ifdef _WIN32
972 HANDLE hFind;
973 WIN32_FIND_DATAW fd;
974 #else
975 struct stat fileInfo;
976 #endif
977
978 #ifdef _WIN32
979 hFind = FindFirstFileW(pszFileName, &fd);
980 if (hFind == INVALID_HANDLE_VALUE)
981 return 0;
982 FindClose(hFind);
983
984 return (unsigned __int64)fd.nFileSizeLow + ((unsigned __int64)fd.nFileSizeHigh << 32);
985 #else
986 if (wstat(pszFileName, &fileInfo) == -1)
987 return 0;
988
989 return (QWORD)fileInfo.st_size;
990 #endif
991 }
992
993 /**
994 * Get size of file in bytes
995 */
996 QWORD LIBNETXMS_EXPORTABLE FileSizeA(const char *pszFileName)
997 {
998 #ifdef _WIN32
999 HANDLE hFind;
1000 WIN32_FIND_DATAA fd;
1001 #else
1002 struct stat fileInfo;
1003 #endif
1004
1005 #ifdef _WIN32
1006 hFind = FindFirstFileA(pszFileName, &fd);
1007 if (hFind == INVALID_HANDLE_VALUE)
1008 return 0;
1009 FindClose(hFind);
1010
1011 return (unsigned __int64)fd.nFileSizeLow + ((unsigned __int64)fd.nFileSizeHigh << 32);
1012 #else
1013 if (stat(pszFileName, &fileInfo) == -1)
1014 return 0;
1015
1016 return (QWORD)fileInfo.st_size;
1017 #endif
1018 }
1019
1020 /**
1021 * Get pointer to clean file name (without path specification)
1022 */
1023 const TCHAR LIBNETXMS_EXPORTABLE *GetCleanFileName(const TCHAR *pszFileName)
1024 {
1025 const TCHAR *ptr;
1026
1027 ptr = pszFileName + _tcslen(pszFileName);
1028 while((ptr >= pszFileName) && (*ptr != _T('/')) && (*ptr != _T('\\')) && (*ptr != _T(':')))
1029 ptr--;
1030 return (ptr + 1);
1031 }
1032
1033 /**
1034 * Translate DCI data type from text form to code
1035 */
1036 int LIBNETXMS_EXPORTABLE NxDCIDataTypeFromText(const TCHAR *pszText)
1037 {
1038 static const TCHAR *m_pszValidTypes[] = { _T("INT"), _T("UINT"), _T("INT64"),
1039 _T("UINT64"), _T("STRING"),
1040 _T("FLOAT"), NULL };
1041 int i;
1042
1043 for(i = 0; m_pszValidTypes[i] != NULL; i++)
1044 if (!_tcsicmp(pszText, m_pszValidTypes[i]))
1045 return i;
1046 return -1; // Invalid data type
1047 }
1048
1049 /**
1050 * Extended send() - send all data even if single call to send()
1051 * cannot handle them all
1052 */
1053 int LIBNETXMS_EXPORTABLE SendEx(SOCKET hSocket, const void *data, size_t len, int flags, MUTEX mutex)
1054 {
1055 int nLeft = (int)len;
1056 int nRet;
1057
1058 if (mutex != NULL)
1059 MutexLock(mutex);
1060
1061 do
1062 {
1063 retry:
1064 #ifdef MSG_NOSIGNAL
1065 nRet = send(hSocket, ((char *)data) + (len - nLeft), nLeft, flags | MSG_NOSIGNAL);
1066 #else
1067 nRet = send(hSocket, ((char *)data) + (len - nLeft), nLeft, flags);
1068 #endif
1069 if (nRet <= 0)
1070 {
1071 if ((WSAGetLastError() == WSAEWOULDBLOCK)
1072 #ifndef _WIN32
1073 || (errno == EAGAIN)
1074 #endif
1075 )
1076 {
1077 // Wait until socket becomes available for writing
1078 struct timeval tv;
1079 fd_set wfds;
1080
1081 tv.tv_sec = 60;
1082 tv.tv_usec = 0;
1083 FD_ZERO(&wfds);
1084 FD_SET(hSocket, &wfds);
1085 nRet = select(SELECT_NFDS(hSocket + 1), NULL, &wfds, NULL, &tv);
1086 if ((nRet > 0) || ((nRet == -1) && (errno == EINTR)))
1087 goto retry;
1088 }
1089 break;
1090 }
1091 nLeft -= nRet;
1092 } while (nLeft > 0);
1093
1094 if (mutex != NULL)
1095 MutexUnlock(mutex);
1096
1097 return nLeft == 0 ? (int)len : nRet;
1098 }
1099
1100 /**
1101 * Extended recv() - receive data with timeout
1102 *
1103 * @param hSocket socket handle
1104 * @param data data buffer
1105 * @param len buffer length in bytes
1106 * @param flags flags to be passed to recv() call
1107 * @param timeout waiting timeout in milliseconds
1108 * @return number of bytes read on success, 0 if socket was closed, -1 on error, -2 on timeout
1109 */
1110 int LIBNETXMS_EXPORTABLE RecvEx(SOCKET hSocket, void *data, size_t len, int flags, UINT32 timeout)
1111 {
1112 int iErr;
1113 #if HAVE_POLL
1114 struct pollfd fds;
1115 #else
1116 struct timeval tv;
1117 fd_set rdfs;
1118 #endif
1119 #ifndef _WIN32
1120 QWORD qwStartTime;
1121 UINT32 dwElapsed;
1122 #endif
1123
1124 // I've seen on Linux that poll() may hang if fds.fd == -1,
1125 // so we check this ourselves
1126 if (hSocket == INVALID_SOCKET)
1127 return -1;
1128
1129 if (timeout != INFINITE)
1130 {
1131 #if HAVE_POLL
1132 fds.fd = hSocket;
1133 fds.events = POLLIN;
1134 fds.revents = POLLIN;
1135 do
1136 {
1137 qwStartTime = GetCurrentTimeMs();
1138 iErr = poll(&fds, 1, timeout);
1139 if ((iErr != -1) || (errno != EINTR))
1140 break;
1141 dwElapsed = GetCurrentTimeMs() - qwStartTime;
1142 timeout -= min(timeout, dwElapsed);
1143 } while(timeout > 0);
1144 #else
1145 FD_ZERO(&rdfs);
1146 FD_SET(hSocket, &rdfs);
1147 #ifdef _WIN32
1148 tv.tv_sec = timeout / 1000;
1149 tv.tv_usec = (timeout % 1000) * 1000;
1150 iErr = select(SELECT_NFDS(hSocket + 1), &rdfs, NULL, NULL, &tv);
1151 #else
1152 do
1153 {
1154 tv.tv_sec = timeout / 1000;
1155 tv.tv_usec = (timeout % 1000) * 1000;
1156 qwStartTime = GetCurrentTimeMs();
1157 iErr = select(SELECT_NFDS(hSocket + 1), &rdfs, NULL, NULL, &tv);
1158 if ((iErr != -1) || (errno != EINTR))
1159 break;
1160 dwElapsed = GetCurrentTimeMs() - qwStartTime;
1161 timeout -= min(timeout, dwElapsed);
1162 } while(timeout > 0);
1163 #endif
1164 #endif
1165 if (iErr > 0)
1166 {
1167 #ifdef _WIN32
1168 iErr = recv(hSocket, (char *)data, (int)len, flags);
1169 #else
1170 do
1171 {
1172 iErr = recv(hSocket, (char *)data, len, flags);
1173 } while((iErr == -1) && (errno == EINTR));
1174 #endif
1175 }
1176 else
1177 {
1178 iErr = -2;
1179 }
1180 }
1181 else
1182 {
1183 #ifdef _WIN32
1184 iErr = recv(hSocket, (char *)data, (int)len, flags);
1185 #else
1186 do
1187 {
1188 iErr = recv(hSocket, (char *)data, (int)len, flags);
1189 } while((iErr == -1) && (errno == EINTR));
1190 #endif
1191 }
1192
1193 return iErr;
1194 }
1195
1196 /**
1197 * Read exact number of bytes from socket
1198 */
1199 bool RecvAll(SOCKET s, void *buffer, size_t size, UINT32 timeout)
1200 {
1201 size_t bytes = 0;
1202 char *pos = (char *)buffer;
1203 while(bytes < size)
1204 {
1205 int b = RecvEx(s, pos, size - bytes, 0, timeout);
1206 if (b <= 0)
1207 return false;
1208 bytes += b;
1209 pos += b;
1210 }
1211 return true;
1212 }
1213
1214 /**
1215 * Connect with given timeout
1216 * Sets socket to non-blocking mode
1217 */
1218 int LIBNETXMS_EXPORTABLE ConnectEx(SOCKET s, struct sockaddr *addr, int len, UINT32 timeout)
1219 {
1220 SetSocketNonBlocking(s);
1221
1222 int rc = connect(s, addr, len);
1223 if (rc == -1)
1224 {
1225 if ((WSAGetLastError() == WSAEWOULDBLOCK) || (WSAGetLastError() == WSAEINPROGRESS))
1226 {
1227 #ifndef _WIN32
1228 QWORD qwStartTime;
1229 UINT32 dwElapsed;
1230 #endif
1231
1232 #if HAVE_POLL
1233 struct pollfd fds;
1234
1235 fds.fd = s;
1236 fds.events = POLLOUT;
1237 fds.revents = POLLOUT;
1238 do
1239 {
1240 qwStartTime = GetCurrentTimeMs();
1241 rc = poll(&fds, 1, timeout);
1242 if ((rc != -1) || (errno != EINTR))
1243 break;
1244 dwElapsed = GetCurrentTimeMs() - qwStartTime;
1245 timeout -= min(timeout, dwElapsed);
1246 } while(timeout > 0);
1247
1248 if (rc > 0)
1249 {
1250 if (fds.revents == POLLOUT)
1251 {
1252 rc = 0;
1253 }
1254 else
1255 {
1256 rc = -1;
1257 }
1258 }
1259 else if (rc == 0) // timeout, return error
1260 {
1261 rc = -1;
1262 }
1263 #else
1264 struct timeval tv;
1265 fd_set wrfs, exfs;
1266
1267 FD_ZERO(&wrfs);
1268 FD_SET(s, &wrfs);
1269
1270 FD_ZERO(&exfs);
1271 FD_SET(s, &exfs);
1272
1273 #ifdef _WIN32
1274 tv.tv_sec = timeout / 1000;
1275 tv.tv_usec = (timeout % 1000) * 1000;
1276 rc = select(SELECT_NFDS(s + 1), NULL, &wrfs, &exfs, &tv);
1277 #else
1278 do
1279 {
1280 tv.tv_sec = timeout / 1000;
1281 tv.tv_usec = (timeout % 1000) * 1000;
1282 qwStartTime = GetCurrentTimeMs();
1283 rc = select(SELECT_NFDS(s + 1), NULL, &wrfs, &exfs, &tv);
1284 if ((rc != -1) || (errno != EINTR))
1285 break;
1286 dwElapsed = GetCurrentTimeMs() - qwStartTime;
1287 timeout -= min(timeout, dwElapsed);
1288 } while(timeout > 0);
1289 #endif
1290 if (rc > 0)
1291 {
1292 if (FD_ISSET(s, &exfs))
1293 {
1294 #ifdef _WIN32
1295 int err, len = sizeof(int);
1296 if (getsockopt(s, SOL_SOCKET, SO_ERROR, (char *)&err, &len) == 0)
1297 WSASetLastError(err);
1298 #endif
1299 rc = -1;
1300 }
1301 else
1302 {
1303 rc = 0;
1304 }
1305 }
1306 else if (rc == 0) // timeout, return error
1307 {
1308 rc = -1;
1309 #ifdef _WIN32
1310 WSASetLastError(WSAETIMEDOUT);
1311 #endif
1312 }
1313 #endif
1314 }
1315 }
1316 return rc;
1317 }
1318
1319 /**
1320 * Connect to given host/port
1321 *
1322 * @return connected socket on success or INVALID_SOCKET on error
1323 */
1324 SOCKET LIBNETXMS_EXPORTABLE ConnectToHost(const InetAddress& addr, UINT16 port, UINT32 timeout)
1325 {
1326 SOCKET s = socket(addr.getFamily(), SOCK_STREAM, 0);
1327 if (s == INVALID_SOCKET)
1328 return INVALID_SOCKET;
1329
1330 SockAddrBuffer saBuffer;
1331 struct sockaddr *sa = addr.fillSockAddr(&saBuffer, port);
1332 if (ConnectEx(s, sa, SA_LEN(sa), timeout) == -1)
1333 {
1334 closesocket(s);
1335 s = INVALID_SOCKET;
1336 }
1337 return s;
1338 }
1339
1340 /**
1341 * Resolve host name to IP address (UNICODE version)
1342 *
1343 * @param name host name or IP address
1344 * @return IP address in network byte order
1345 */
1346 UINT32 LIBNETXMS_EXPORTABLE ResolveHostNameW(const WCHAR *name)
1347 {
1348 char mbName[256];
1349 WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR, name, -1, mbName, 256, NULL, NULL);
1350 return ResolveHostNameA(mbName);
1351 }
1352
1353 /**
1354 * Resolve host name to IP address (multibyte version)
1355 *
1356 * @param name host name or IP address
1357 * @return IP address in network byte order
1358 */
1359 UINT32 LIBNETXMS_EXPORTABLE ResolveHostNameA(const char *name)
1360 {
1361 UINT32 addr = inet_addr(name);
1362 if ((addr == INADDR_NONE) || (addr == INADDR_ANY))
1363 {
1364 #if HAVE_GETHOSTBYNAME2_R
1365 struct hostent h, *hs = NULL;
1366 char buffer[1024];
1367 int err;
1368 gethostbyname2_r(name, AF_INET, &h, buffer, 1024, &hs, &err);
1369 #else
1370 struct hostent *hs = gethostbyname(name);
1371 #endif
1372 if (hs != NULL)
1373 {
1374 memcpy(&addr, hs->h_addr, sizeof(UINT32));
1375 }
1376 else
1377 {
1378 addr = INADDR_NONE;
1379 }
1380 }
1381
1382 return addr;
1383 }
1384
1385 #ifndef VER_PLATFORM_WIN32_WINDOWS
1386 #define VER_PLATFORM_WIN32_WINDOWS 1
1387 #endif
1388 #ifndef VER_PLATFORM_WIN32_CE
1389 #define VER_PLATFORM_WIN32_CE 3
1390 #endif
1391 #ifndef SM_SERVERR2
1392 #define SM_SERVERR2 89
1393 #endif
1394
1395 /**
1396 * Get OS name and version
1397 */
1398 void LIBNETXMS_EXPORTABLE GetOSVersionString(TCHAR *pszBuffer, int nBufSize)
1399 {
1400 int nSize = nBufSize - 1;
1401
1402 memset(pszBuffer, 0, nBufSize * sizeof(TCHAR));
1403
1404 #if defined(_WIN32)
1405 OSVERSIONINFO ver;
1406
1407 ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
1408 GetVersionEx(&ver);
1409 switch(ver.dwPlatformId)
1410 {
1411 case VER_PLATFORM_WIN32_WINDOWS:
1412 _sntprintf(pszBuffer, nSize, _T("Win%s"), ver.dwMinorVersion == 0 ? _T("95") :
1413 (ver.dwMinorVersion == 10 ? _T("98") :
1414 (ver.dwMinorVersion == 90 ? _T("Me") : _T("9x"))));
1415 break;
1416 case VER_PLATFORM_WIN32_NT:
1417 _sntprintf(pszBuffer, nSize, _T("WinNT %d.%d"), ver.dwMajorVersion, ver.dwMinorVersion);
1418 break;
1419 #ifdef VER_PLATFORM_WIN32_CE
1420 case VER_PLATFORM_WIN32_CE:
1421 _sntprintf(pszBuffer, nSize, _T("WinCE %d.%d"), ver.dwMajorVersion, ver.dwMinorVersion);
1422 break;
1423 #endif
1424 default:
1425 _sntprintf(pszBuffer, nSize, _T("WinX %d.%d"), ver.dwMajorVersion, ver.dwMinorVersion);
1426 break;
1427 }
1428 #elif defined(_NETWARE)
1429 struct utsname un;
1430
1431 uname(&un);
1432 _sntprintf(pszBuffer, nSize, _T("NetWare %d.%d"), un.netware_major, un.netware_minor);
1433 if (un.servicepack > 0)
1434 {
1435 int nLen = (int)_tcslen(pszBuffer);
1436 nSize -= nLen;
1437 if (nSize > 0)
1438 _sntprintf(&pszBuffer[nLen], nSize, _T(" sp%d"), un.servicepack);
1439 }
1440 #else
1441 struct utsname un;
1442
1443 uname(&un);
1444 #ifdef UNICODE
1445 char buf[1024];
1446 snprintf(buf, 1024, "%s %s", un.sysname, un.release);
1447 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, buf, -1, pszBuffer, nSize);
1448 #else
1449 snprintf(pszBuffer, nSize, "%s %s", un.sysname, un.release);
1450 #endif
1451 #endif
1452 }
1453
1454 #ifdef _WIN32
1455
1456 /**
1457 * Get more specific Windows version string
1458 */
1459 BOOL LIBNETXMS_EXPORTABLE GetWindowsVersionString(TCHAR *versionString, int strSize)
1460 {
1461 OSVERSIONINFOEX ver;
1462 TCHAR buffer[256];
1463
1464 ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
1465 if (!GetVersionEx((OSVERSIONINFO *)&ver))
1466 return FALSE;
1467
1468 switch(ver.dwMajorVersion)
1469 {
1470 case 5:
1471 switch(ver.dwMinorVersion)
1472 {
1473 case 0:
1474 _tcscpy(buffer, _T("2000"));
1475 break;
1476 case 1:
1477 _tcscpy(buffer, _T("XP"));
1478 break;
1479 case 2:
1480 _tcscpy(buffer, (GetSystemMetrics(SM_SERVERR2) != 0) ? _T("Server 2003 R2") : _T("Server 2003"));
1481 break;
1482 default:
1483 _sntprintf(buffer, 256, _T("NT %d.%d"), ver.dwMajorVersion, ver.dwMinorVersion);
1484 break;
1485 }
1486 break;
1487 case 6:
1488 switch(ver.dwMinorVersion)
1489 {
1490 case 0:
1491 _tcscpy(buffer, (ver.wProductType == VER_NT_WORKSTATION) ? _T("Vista") : _T("Server 2008"));
1492 break;
1493 case 1:
1494 _tcscpy(buffer, (ver.wProductType == VER_NT_WORKSTATION) ? _T("7") : _T("Server 2008 R2"));
1495 break;
1496 case 2:
1497 _tcscpy(buffer, (ver.wProductType == VER_NT_WORKSTATION) ? _T("8") : _T("Server 2012"));
1498 break;
1499 case 3:
1500 _tcscpy(buffer, (ver.wProductType == VER_NT_WORKSTATION) ? _T("8.1") : _T("Server 2012 R2"));
1501 break;
1502 default:
1503 _sntprintf(buffer, 256, _T("NT %d.%d"), ver.dwMajorVersion, ver.dwMinorVersion);
1504 break;
1505 }
1506 break;
1507 case 10:
1508 switch(ver.dwMinorVersion)
1509 {
1510 case 0:
1511 _tcscpy(buffer, (ver.wProductType == VER_NT_WORKSTATION) ? _T("10") : _T("Server"));
1512 break;
1513 default:
1514 _sntprintf(buffer, 256, _T("NT %d.%d"), ver.dwMajorVersion, ver.dwMinorVersion);
1515 break;
1516 }
1517 break;
1518 default:
1519 _sntprintf(buffer, 256, _T("NT %d.%d"), ver.dwMajorVersion, ver.dwMinorVersion);
1520 break;
1521 }
1522
1523 _sntprintf(versionString, strSize, _T("Windows %s Build %d %s"), buffer, ver.dwBuildNumber, ver.szCSDVersion);
1524 StrStrip(versionString);
1525 return TRUE;
1526 }
1527
1528 #endif
1529
1530
1531 //
1532 // Count number of characters in string
1533 //
1534
1535 int LIBNETXMS_EXPORTABLE NumCharsW(const WCHAR *pszStr, WCHAR ch)
1536 {
1537 const WCHAR *p;
1538 int nCount;
1539
1540 for(p = pszStr, nCount = 0; *p != 0; p++)
1541 if (*p == ch)
1542 nCount++;
1543 return nCount;
1544 }
1545
1546 int LIBNETXMS_EXPORTABLE NumCharsA(const char *pszStr, char ch)
1547 {
1548 const char *p;
1549 int nCount;
1550
1551 for(p = pszStr, nCount = 0; *p != 0; p++)
1552 if (*p == ch)
1553 nCount++;
1554 return nCount;
1555 }
1556
1557 /**
1558 * Match string against regexp (UNICODE version)
1559 */
1560 BOOL LIBNETXMS_EXPORTABLE RegexpMatchW(const WCHAR *str, const WCHAR *expr, bool matchCase)
1561 {
1562 regex_t preg;
1563 bool result = false;
1564
1565 if (tre_regwcomp(&preg, expr, matchCase ? REG_EXTENDED | REG_NOSUB : REG_EXTENDED | REG_NOSUB | REG_ICASE) == 0)
1566 {
1567 if (tre_regwexec(&preg, str, 0, NULL, 0) == 0) // MATCH
1568 result = true;
1569 regfree(&preg);
1570 }
1571
1572 return result;
1573 }
1574
1575 /**
1576 * Match string against regexp (multibyte version)
1577 */
1578 BOOL LIBNETXMS_EXPORTABLE RegexpMatchA(const char *str, const char *expr, bool matchCase)
1579 {
1580 regex_t preg;
1581 bool result = false;
1582
1583 if (tre_regcomp(&preg, expr, matchCase ? REG_EXTENDED | REG_NOSUB : REG_EXTENDED | REG_NOSUB | REG_ICASE) == 0)
1584 {
1585 if (tre_regexec(&preg, str, 0, NULL, 0) == 0) // MATCH
1586 result = true;
1587 regfree(&preg);
1588 }
1589
1590 return result;
1591 }
1592
1593 /**
1594 * Translate given code to text
1595 */
1596 const TCHAR LIBNETXMS_EXPORTABLE *CodeToText(int iCode, CODE_TO_TEXT *pTranslator, const TCHAR *pszDefaultText)
1597 {
1598 int i;
1599
1600 for(i = 0; pTranslator[i].text != NULL; i++)
1601 if (pTranslator[i].code == iCode)
1602 return pTranslator[i].text;
1603 return pszDefaultText;
1604 }
1605
1606 /**
1607 * Extract option value from string of form option=value;option=value;... (UNICODE version)
1608 */
1609 bool LIBNETXMS_EXPORTABLE ExtractNamedOptionValueW(const WCHAR *optString, const WCHAR *option, WCHAR *buffer, int bufSize)
1610 {
1611 int state, pos;
1612 const WCHAR *curr, *start;
1613 WCHAR temp[256];
1614
1615 for(curr = start = optString, pos = 0, state = 0; *curr != 0; curr++)
1616 {
1617 switch(*curr)
1618 {
1619 case L';': // Next option
1620 if (state == 1)
1621 {
1622 buffer[pos] = 0;
1623 StrStripW(buffer);
1624 return true;
1625 }
1626 state = 0;
1627 start = curr + 1;
1628 break;
1629 case L'=':
1630 if (state == 0)
1631 {
1632 wcsncpy(temp, start, curr - start);
1633 temp[curr - start] = 0;
1634 StrStripW(temp);
1635 if (!wcsicmp(option, temp))
1636 state = 1;
1637 else
1638 state = 2;
1639 }
1640 else if ((state == 1) && (pos < bufSize - 1))
1641 {
1642 buffer[pos++] = L'=';
1643 }
1644 break;
1645 default:
1646 if ((state == 1) && (pos < bufSize - 1))
1647 buffer[pos++] = *curr;
1648 break;
1649 }
1650 }
1651
1652 if (state == 1)
1653 {
1654 buffer[pos] = 0;
1655 StrStripW(buffer);
1656 return true;
1657 }
1658
1659 return false;
1660 }
1661
1662 /**
1663 * Extract option value from string of form option=value;option=value;... (multibyte version)
1664 */
1665 bool LIBNETXMS_EXPORTABLE ExtractNamedOptionValueA(const char *optString, const char *option, char *buffer, int bufSize)
1666 {
1667 int state, pos;
1668 const char *curr, *start;
1669 char temp[256];
1670
1671 for(curr = start = optString, pos = 0, state = 0; *curr != 0; curr++)
1672 {
1673 switch(*curr)
1674 {
1675 case ';': // Next option
1676 if (state == 1)
1677 {
1678 buffer[pos] = 0;
1679 StrStripA(buffer);
1680 return true;
1681 }
1682 state = 0;
1683 start = curr + 1;
1684 break;
1685 case '=':
1686 if (state == 0)
1687 {
1688 strncpy(temp, start, curr - start);
1689 temp[curr - start] = 0;
1690 StrStripA(temp);
1691 if (!stricmp(option, temp))
1692 state = 1;
1693 else
1694 state = 2;
1695 }
1696 else if ((state == 1) && (pos < bufSize - 1))
1697 {
1698 buffer[pos++] = '=';
1699 }
1700 break;
1701 default:
1702 if ((state == 1) && (pos < bufSize - 1))
1703 buffer[pos++] = *curr;
1704 break;
1705 }
1706 }
1707
1708 if (state == 1)
1709 {
1710 buffer[pos] = 0;
1711 StrStripA(buffer);
1712 return true;
1713 }
1714
1715 return false;
1716 }
1717
1718 /**
1719 * Extract named option value as boolean (UNICODE version)
1720 */
1721 bool LIBNETXMS_EXPORTABLE ExtractNamedOptionValueAsBoolW(const WCHAR *optString, const WCHAR *option, bool defVal)
1722 {
1723 WCHAR buffer[256];
1724 if (ExtractNamedOptionValueW(optString, option, buffer, 256))
1725 return !wcsicmp(buffer, L"yes") || !wcsicmp(buffer, L"true");
1726 return defVal;
1727 }
1728
1729 /**
1730 * Extract named option value as boolean (multibyte version)
1731 */
1732 bool LIBNETXMS_EXPORTABLE ExtractNamedOptionValueAsBoolA(const char *optString, const char *option, bool defVal)
1733 {
1734 char buffer[256];
1735 if (ExtractNamedOptionValueA(optString, option, buffer, 256))
1736 return !stricmp(buffer, "yes") || !stricmp(buffer, "true");
1737 return defVal;
1738 }
1739
1740 /**
1741 * Extract named option value as integer (UNICODE version)
1742 */
1743 long LIBNETXMS_EXPORTABLE ExtractNamedOptionValueAsIntW(const WCHAR *optString, const WCHAR *option, long defVal)
1744 {
1745 WCHAR buffer[256], *eptr;
1746 long val;
1747
1748 if (ExtractNamedOptionValueW(optString, option, buffer, 256))
1749 {
1750 val = wcstol(buffer, &eptr, 0);
1751 if (*eptr == 0)
1752 return val;
1753 }
1754 return defVal;
1755 }
1756
1757 /**
1758 * Extract named option value as integer (multibyte version)
1759 */
1760 long LIBNETXMS_EXPORTABLE ExtractNamedOptionValueAsIntA(const char *optString, const char *option, long defVal)
1761 {
1762 char buffer[256], *eptr;
1763 long val;
1764
1765 if (ExtractNamedOptionValueA(optString, option, buffer, 256))
1766 {
1767 val = strtol(buffer, &eptr, 0);
1768 if (*eptr == 0)
1769 return val;
1770 }
1771 return defVal;
1772 }
1773
1774 /**
1775 * Split string
1776 */
1777 TCHAR LIBNETXMS_EXPORTABLE **SplitString(const TCHAR *source, TCHAR sep, int *numStrings)
1778 {
1779 TCHAR **strings;
1780
1781 *numStrings = NumChars(source, sep) + 1;
1782 strings = (TCHAR **)malloc(sizeof(TCHAR *) * (*numStrings));
1783 for(int n = 0, i = 0; n < *numStrings; n++, i++)
1784 {
1785 int start = i;
1786 while((source[i] != sep) && (source[i] != 0))
1787 i++;
1788 int len = i - start;
1789 strings[n] = (TCHAR *)malloc(sizeof(TCHAR) * (len + 1));
1790 memcpy(strings[n], &source[start], len * sizeof(TCHAR));
1791 strings[n][len] = 0;
1792 }
1793 return strings;
1794 }
1795
1796 /**
1797 * Get step size for "%" and "/" crontab cases
1798 */
1799 static int GetStepSize(TCHAR *str)
1800 {
1801 int step = 0;
1802 if (str != NULL)
1803 {
1804 *str = 0;
1805 str++;
1806 step = *str == _T('\0') ? 1 : _tcstol(str, NULL, 10);
1807 }
1808
1809 if (step <= 0)
1810 {
1811 step = 1;
1812 }
1813
1814 return step;
1815 }
1816
1817 /**
1818 * Get last day of current month
1819 */
1820 int LIBNETXMS_EXPORTABLE GetLastMonthDay(struct tm *currTime)
1821 {
1822 switch(currTime->tm_mon)
1823 {
1824 case 1: // February
1825 if (((currTime->tm_year % 4) == 0) && (((currTime->tm_year % 100) != 0) || (((currTime->tm_year + 1900) % 400) == 0)))
1826 return 29;
1827 return 28;
1828 case 0: // January
1829 case 2: // March
1830 case 4: // May
1831 case 6: // July
1832 case 7: // August
1833 case 9: // October
1834 case 11: // December
1835 return 31;
1836 default:
1837 return 30;
1838 }
1839 }
1840
1841 /**
1842 * Match schedule element
1843 * NOTE: We assume that pattern can be modified during processing
1844 */
1845 bool LIBNETXMS_EXPORTABLE MatchScheduleElement(TCHAR *pszPattern, int nValue, int maxValue, struct tm *localTime, time_t currTime)
1846 {
1847 TCHAR *ptr, *curr;
1848 int nStep, nCurr, nPrev;
1849 bool bRun = true, bRange = false;
1850
1851 // Check for "last" pattern
1852 if (*pszPattern == _T('L'))
1853 return nValue == maxValue;
1854
1855 // Check if time() step was specified (% - special syntax)
1856 ptr = _tcschr(pszPattern, _T('%'));
1857 if (ptr != NULL)
1858 return (currTime % GetStepSize(ptr)) != 0;
1859
1860 // Check if step was specified
1861 ptr = _tcschr(pszPattern, _T('/'));
1862 nStep = GetStepSize(ptr);
1863
1864 if (*pszPattern == _T('*'))
1865 goto check_step;
1866
1867 for(curr = pszPattern; bRun; curr = ptr + 1)
1868 {
1869 for(ptr = curr; (*ptr != 0) && (*ptr != '-') && (*ptr != ','); ptr++);
1870 switch(*ptr)
1871 {
1872 case '-':
1873 if (bRange)
1874 return false; // Form like 1-2-3 is invalid
1875 bRange = true;
1876 *ptr = 0;
1877 nPrev = _tcstol(curr, NULL, 10);
1878 break;
1879 case 'L': // special case for last day ow week in a month (like 5L - last Friday)
1880 if (bRange || (localTime == NULL))
1881 return false; // Range with L is not supported; nL form supported only for day of week
1882 *ptr = 0;
1883 nCurr = _tcstol(curr, NULL, 10);
1884 if ((nValue == nCurr) && (localTime->tm_mday + 7 > GetLastMonthDay(localTime)))
1885 return true;
1886 ptr++;
1887 if (*ptr != ',')
1888 bRun = false;
1889 break;
1890 case 0:
1891 bRun = false;
1892 /* no break */
1893 case ',':
1894 *ptr = 0;
1895 nCurr = _tcstol(curr, NULL, 10);
1896 if (bRange)
1897 {
1898 if ((nValue >= nPrev) && (nValue <= nCurr))
1899 goto check_step;
1900 bRange = false;
1901 }
1902 else
1903 {
1904 if (nValue == nCurr)
1905 return true;
1906 }
1907 break;
1908 }
1909 }
1910
1911 return false;
1912
1913 check_step:
1914 return (nValue % nStep) == 0;
1915 }
1916
1917 /**
1918 * Failure handler for DecryptPassword
1919 */
1920 inline bool DecryptPasswordFail(const TCHAR *encryptedPasswd, TCHAR *decryptedPasswd, size_t bufferLenght)
1921 {
1922 if (decryptedPasswd != encryptedPasswd)
1923 nx_strncpy(decryptedPasswd, encryptedPasswd, bufferLenght);
1924 return false;
1925 }
1926
1927 /**
1928 * Decrypt password encrypted with nxencpassw.
1929 * In case when it was not possible to decrypt password as the decrypted password will be set the original one.
1930 * The buffer length for encryptedPasswd and decryptedPasswd should be the same.
1931 */
1932 bool LIBNETXMS_EXPORTABLE DecryptPassword(const TCHAR *login, const TCHAR *encryptedPasswd, TCHAR *decryptedPasswd, size_t bufferLenght)
1933 {
1934 //check that lenght is correct
1935 if (_tcslen(encryptedPasswd) != 44)
1936 return DecryptPasswordFail(encryptedPasswd, decryptedPasswd, bufferLenght);
1937
1938 // check that password contains only allowed symbols
1939 int invalidSymbolIndex = (int)_tcsspn(encryptedPasswd, _T("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"));
1940 if ((invalidSymbolIndex < 42) || ((invalidSymbolIndex != 44) && ((encryptedPasswd[invalidSymbolIndex] != _T('=')) || ((invalidSymbolIndex == 42) && (encryptedPasswd[43] != _T('='))))))
1941 return DecryptPasswordFail(encryptedPasswd, decryptedPasswd, bufferLenght);
1942
1943 #ifdef UNICODE
1944 char *mbencrypted = MBStringFromWideString(encryptedPasswd);
1945 char *mblogin = MBStringFromWideString(login);
1946 #else
1947 const char *mbencrypted = encryptedPasswd;
1948 const char *mblogin = login;
1949 #endif
1950
1951 BYTE encrypted[32], decrypted[32], key[16];
1952 size_t encSize = 32;
1953 base64_decode(mbencrypted, strlen(mbencrypted), (char *)encrypted, &encSize);
1954 if (encSize != 32)
1955 return DecryptPasswordFail(encryptedPasswd, decryptedPasswd, bufferLenght);
1956
1957 CalculateMD5Hash((BYTE *)mblogin, strlen(mblogin), key);
1958 ICEDecryptData(encrypted, 32, decrypted, key);
1959 decrypted[31] = 0;
1960
1961 #ifdef UNICODE
1962 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, (char *)decrypted, -1, decryptedPasswd, (int)bufferLenght);
1963 decryptedPasswd[bufferLenght - 1] = 0;
1964 free(mbencrypted);
1965 free(mblogin);
1966 #else
1967 nx_strncpy(decryptedPasswd, (char *)decrypted, bufferLenght);
1968 #endif
1969
1970 return true;
1971 }
1972
1973 #ifndef UNDER_CE
1974
1975 /**
1976 * Load file content into memory
1977 */
1978 static BYTE *LoadFileContent(int fd, UINT32 *pdwFileSize)
1979 {
1980 int iBufPos, iNumBytes, iBytesRead;
1981 BYTE *pBuffer = NULL;
1982 struct stat fs;
1983
1984 if (fstat(fd, &fs) != -1)
1985 {
1986 pBuffer = (BYTE *)malloc(fs.st_size + 1);
1987 if (pBuffer != NULL)
1988 {
1989 *pdwFileSize = fs.st_size;
1990 for(iBufPos = 0; iBufPos < fs.st_size; iBufPos += iBytesRead)
1991 {
1992 iNumBytes = min(16384, fs.st_size - iBufPos);
1993 if ((iBytesRead = read(fd, &pBuffer[iBufPos], iNumBytes)) < 0)
1994 {
1995 free(pBuffer);
1996 pBuffer = NULL;
1997 break;
1998 }
1999 }
2000 if (pBuffer != NULL)
2001 pBuffer[fs.st_size] = 0;
2002 }
2003 }
2004 close(fd);
2005 return pBuffer;
2006 }
2007
2008 BYTE LIBNETXMS_EXPORTABLE *LoadFile(const TCHAR *pszFileName, UINT32 *pdwFileSize)
2009 {
2010 int fd;
2011 BYTE *pBuffer = NULL;
2012
2013 fd = _topen(pszFileName, O_RDONLY | O_BINARY);
2014 if (fd != -1)
2015 {
2016 pBuffer = LoadFileContent(fd, pdwFileSize);
2017 }
2018 return pBuffer;
2019 }
2020
2021 #ifdef UNICODE
2022
2023 BYTE LIBNETXMS_EXPORTABLE *LoadFileA(const char *pszFileName, UINT32 *pdwFileSize)
2024 {
2025 int fd;
2026 BYTE *pBuffer = NULL;
2027
2028 #ifdef _WIN32
2029 fd = _open(pszFileName, O_RDONLY | O_BINARY);
2030 #else
2031 fd = open(pszFileName, O_RDONLY | O_BINARY);
2032 #endif
2033 if (fd != -1)
2034 {
2035 pBuffer = LoadFileContent(fd, pdwFileSize);
2036 }
2037 return pBuffer;
2038 }
2039
2040 #endif
2041
2042 #endif
2043
2044 #ifdef _WIN32
2045
2046 /**
2047 * Get memory consumed by current process
2048 */
2049 INT64 LIBNETXMS_EXPORTABLE GetProcessRSS()
2050 {
2051 PROCESS_MEMORY_COUNTERS pmc;
2052
2053 if (GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc)))
2054 return pmc.WorkingSetSize;
2055 return 0;
2056 }
2057
2058 #define BG_MASK 0xF0
2059 #define FG_MASK 0x0F
2060
2061 /**
2062 * Apply terminal attributes to console - Win32 API specific
2063 */
2064 static WORD ApplyTerminalAttribute(HANDLE out, WORD currAttr, long code)
2065 {
2066 WORD attr = currAttr;
2067 switch(code)
2068 {
2069 case 0: // reset attribute
2070 attr = 0x07;
2071 break;
2072 case 1: // bold/bright
2073 attr = currAttr | FOREGROUND_INTENSITY;
2074 break;
2075 case 22: // normal color
2076 attr = currAttr & ~FOREGROUND_INTENSITY;
2077 break;
2078 case 30: // black foreground
2079 attr = (currAttr & BG_MASK);
2080 break;
2081 case 31: // red foreground
2082 attr = (currAttr & BG_MASK) | 0x04;
2083 break;
2084 case 32: // green foreground
2085 attr = (currAttr & BG_MASK) | 0x02;
2086 break;
2087 case 33: // yellow foreground
2088 attr = (currAttr & BG_MASK) | 0x06;
2089 break;
2090 case 34: // blue foreground
2091 attr = (currAttr & BG_MASK) | 0x01;
2092 break;
2093 case 35: // magenta foreground
2094 attr = (currAttr & BG_MASK) | 0x05;
2095 break;
2096 case 36: // cyan foreground
2097 attr = (currAttr & BG_MASK) | 0x03;
2098 break;
2099 case 37: // white (gray) foreground
2100 attr = (currAttr & BG_MASK) | 0x07;
2101 break;
2102 case 40: // black background
2103 attr = (currAttr & FG_MASK);
2104 break;
2105 case 41: // red background
2106 attr = (currAttr & FG_MASK) | 0x40;
2107 break;
2108 case 42: // green background
2109 attr = (currAttr & FG_MASK) | 0x20;
2110 break;
2111 case 43: // yellow background
2112 attr = (currAttr & FG_MASK) | 0x60;
2113 break;
2114 case 44: // blue background
2115 attr = (currAttr & FG_MASK) | 0x10;
2116 break;
2117 case 45: // magenta background
2118 attr = (currAttr & FG_MASK) | 0x50;
2119 break;
2120 case 46: // cyan background
2121 attr = (currAttr & FG_MASK) | 0x30;
2122 break;
2123 case 47: // white (gray) background
2124 attr = (currAttr & FG_MASK) | 0x70;
2125 break;
2126 default:
2127 break;
2128 }
2129 SetConsoleTextAttribute(out, attr);
2130 return attr;
2131 }
2132
2133 #endif
2134
2135 /**
2136 * Write to terminal with support for ANSI color codes
2137 */
2138 void LIBNETXMS_EXPORTABLE WriteToTerminal(const TCHAR *text)
2139 {
2140 #ifdef _WIN32
2141 HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);
2142
2143 DWORD mode;
2144 if (!GetConsoleMode(out, &mode))
2145 {
2146 // Assume output is redirected
2147 #ifdef UNICODE
2148 char *mbText = MBStringFromWideString(text);
2149 WriteFile(out, mbText, (UINT32)strlen(mbText), &mode, NULL);
2150 free(mbText);
2151 #else
2152 WriteFile(out, text, (UINT32)strlen(text), &mode, NULL);
2153 #endif
2154 return;
2155 }
2156
2157 CONSOLE_SCREEN_BUFFER_INFO csbi;
2158 GetConsoleScreenBufferInfo(out, &csbi);
2159
2160 const TCHAR *curr = text;
2161 while(*curr != 0)
2162 {
2163 const TCHAR *esc = _tcschr(curr, 27); // Find ESC
2164 if (esc != NULL)
2165 {
2166 esc++;
2167 if (*esc == _T('['))
2168 {
2169 // write everything up to ESC char
2170 DWORD chars;
2171 WriteConsole(out, curr, (UINT32)(esc - curr - 1), &chars, NULL);
2172
2173 esc++;
2174
2175 TCHAR code[64];
2176 int pos = 0;
2177 while((*esc != 0) && (*esc != _T('m')))
2178 {
2179 if (*esc == _T(';'))
2180 {
2181 code[pos] = 0;
2182 csbi.wAttributes = ApplyTerminalAttribute(out, csbi.wAttributes, _tcstol(code, NULL, 10));
2183 pos = 0;
2184 }
2185 else
2186 {
2187 if (pos < 63)
2188 code[pos++] = *esc;
2189 }
2190 esc++;
2191 }
2192 if (pos > 0)
2193 {
2194 code[pos] = 0;
2195 csbi.wAttributes = ApplyTerminalAttribute(out, csbi.wAttributes, _tcstol(code, NULL, 10));
2196 }
2197 esc++;
2198 }
2199 else
2200 {
2201 DWORD chars;
2202 WriteConsole(out, curr, (UINT32)(esc - curr), &chars, NULL);
2203 }
2204 curr = esc;
2205 }
2206 else
2207 {
2208 DWORD chars;
2209 WriteConsole(out, curr, (UINT32)_tcslen(curr), &chars, NULL);
2210 break;
2211 }
2212 }
2213 #else
2214 #ifdef UNICODE
2215 #if HAVE_FPUTWS
2216 fputws(text, stdout);
2217 #else
2218 char *mbtext = MBStringFromWideStringSysLocale(text);
2219 fputs(mbtext, stdout);
2220 free(mbtext);
2221 #endif
2222 #else
2223 fputs(text, stdout);
2224 #endif
2225 #endif
2226 }
2227
2228 /**
2229 * Write to terminal with support for ANSI color codes
2230 */
2231 void LIBNETXMS_EXPORTABLE WriteToTerminalEx(const TCHAR *format, ...)
2232 {
2233 TCHAR buffer[8192];
2234 va_list args;
2235
2236 va_start(args, format);
2237 _vsntprintf(buffer, 8192, format, args);
2238 va_end(args);
2239 WriteToTerminal(buffer);
2240 }
2241
2242 /**
2243 * mkstemp() implementation for Windows
2244 */
2245 #ifdef _WIN32
2246
2247 int LIBNETXMS_EXPORTABLE mkstemp(char *tmpl)
2248 {
2249 char *name = _mktemp(tmpl);
2250 if (name == NULL)
2251 return -1;
2252 return _open(name, O_RDWR | O_BINARY | O_CREAT | O_EXCL| _O_SHORT_LIVED, _S_IREAD | _S_IWRITE);
2253 }
2254
2255 int LIBNETXMS_EXPORTABLE wmkstemp(WCHAR *tmpl)
2256 {
2257 WCHAR *name = _wmktemp(tmpl);
2258 if (name == NULL)
2259 return -1;
2260 return _wopen(name, O_RDWR | O_BINARY | O_CREAT | O_EXCL| _O_SHORT_LIVED, _S_IREAD | _S_IWRITE);
2261 }
2262
2263 #endif
2264
2265 #ifndef _WIN32
2266
2267 /**
2268 * strcat_s implementation
2269 */
2270 int LIBNETXMS_EXPORTABLE strcat_s(char *dst, size_t dstSize, const char *src)
2271 {
2272 if (strlen(dst) + strlen(src) + 1 >= dstSize)
2273 return EINVAL;
2274 strcat(dst, src);
2275 return 0;
2276 }
2277
2278 /**
2279 * wcscat_s implementation
2280 */
2281 int LIBNETXMS_EXPORTABLE wcscat_s(WCHAR *dst, size_t dstSize, const WCHAR *src)
2282 {
2283 if (wcslen(dst) + wcslen(src) + 1 >= dstSize)
2284 return EINVAL;
2285 wcscat(dst, src);
2286 return 0;
2287 }
2288
2289 #endif
2290
2291 /**
2292 * Destructor for RefCountObject
2293 */
2294 RefCountObject::~RefCountObject()
2295 {
2296 }
2297
2298 /**
2299 * Safe _fgetts implementation which will work
2300 * with handles opened by popen
2301 */
2302 TCHAR LIBNETXMS_EXPORTABLE *safe_fgetts(TCHAR *buffer, int len, FILE *f)
2303 {
2304 #ifdef UNICODE
2305 #if SAFE_FGETWS_WITH_POPEN
2306 return fgetws(buffer, len, f);
2307 #else
2308 char *mbBuffer = (char *)alloca(len);
2309 char *s = fgets(mbBuffer, len, f);
2310 if (s == NULL)
2311 return NULL;
2312 mbBuffer[len - 1] = 0;
2313 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, mbBuffer, -1, buffer, len);
2314 return buffer;
2315 #endif
2316 #else
2317 return fgets(buffer, len, f);
2318 #endif
2319 }
2320
2321 #if !HAVE_WCSLWR && !defined(_WIN32)
2322
2323 /**
2324 * Convert UNICODE string to lowercase
2325 */
2326 WCHAR LIBNETXMS_EXPORTABLE *wcslwr(WCHAR *str)
2327 {
2328 for(WCHAR *p = str; *p != 0; p++)
2329 {
2330 #if HAVE_TOWLOWER
2331 *p = towlower(*p);
2332 #else
2333 if ((*p >= 'a') && (*p <= 'z'))
2334 *p = *p - ('a' - 'A');
2335 #endif
2336 }
2337 return str;
2338 }
2339
2340 #endif
2341
2342 #if !defined(_WIN32) && (!HAVE_WCSFTIME || !WORKING_WCSFTIME)
2343
2344 /**
2345 * wide char version of strftime
2346 */
2347 size_t LIBNETXMS_EXPORTABLE nx_wcsftime(WCHAR *buffer, size_t bufsize, const WCHAR *format, const struct tm *t)
2348 {
2349 #if HAVE_ALLOCA
2350 char *mbuf = (char *)alloca(bufsize);
2351 size_t flen = wcslen(format) + 1;
2352 char *mfmt = (char *)alloca(flen);
2353 WideCharToMultiByte(CP_ACP, WC_DEFAULTCHAR | WC_COMPOSITECHECK, format, -1, mfmt, flen, NULL, NULL);
2354 #else
2355 char *mbuf = (char *)malloc(bufsize);
2356 char *mfmt = MBStringFromWideString(format);
2357 #endif
2358 size_t rc = strftime(mbuf, bufsize, mfmt, t);
2359 if (rc > 0)
2360 {
2361 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, mbuf, -1, buffer, (int)bufsize);
2362 buffer[bufsize - 1] = 0;
2363 }
2364 else
2365 {
2366 buffer[0] = 0;
2367 }
2368 #if !HAVE_ALLOCA
2369 free(mbuf);
2370 free(mfmt);
2371 #endif
2372 return rc;
2373 }
2374
2375 #endif
2376
2377 #if !HAVE__ITOA && !defined(_WIN32)
2378
2379 /**
2380 * _itoa() implementation
2381 */
2382 char LIBNETXMS_EXPORTABLE *_itoa(int value, char *str, int base)
2383 {
2384 char *p = str;
2385 if (value < 0)
2386 {
2387 *p++ = '-';
2388 value = -value;
2389 }
2390
2391 char buffer[64];
2392 char *t = buffer;
2393 do
2394 {
2395 int rem = value % base;
2396 *t++ = (rem < 10) ? (rem + '0') : (rem - 10 + 'a');
2397 value = value / base;
2398 } while(value > 0);
2399
2400 t--;
2401 while(t >= buffer)
2402 *p++ = *t--;
2403 *p = 0;
2404 return str;
2405 }
2406
2407 #endif
2408
2409 #if !HAVE__ITOA && !defined(_WIN32)
2410
2411 /**
2412 * _itow() implementation
2413 */
2414 WCHAR LIBNETXMS_EXPORTABLE *_itow(int value, WCHAR *str, int base)
2415 {
2416 WCHAR *p = str;
2417 if (value < 0)
2418 {
2419 *p++ = '-';
2420 value = -value;
2421 }
2422
2423 WCHAR buffer[64];
2424 WCHAR *t = buffer;
2425 do
2426 {
2427 int rem = value % base;
2428 *t++ = (rem < 10) ? (rem + '0') : (rem - 10 + 'a');
2429 value = value / base;
2430 } while(value > 0);
2431
2432 t--;
2433 while(t >= buffer)
2434 *p++ = *t--;
2435 *p = 0;
2436 return str;
2437 }
2438
2439 #endif
2440
2441 /**
2442 * Get sleep time until specific time
2443 */
2444 int LIBNETXMS_EXPORTABLE GetSleepTime(int hour, int minute, int second)
2445 {
2446 time_t now = time(NULL);
2447
2448 struct tm localTime;
2449 #if HAVE_LOCALTIME_R
2450 localtime_r(&now, &localTime);
2451 #else
2452 memcpy(&localTime, localtime(&now), sizeof(struct tm));
2453 #endif
2454
2455 int target = hour * 3600 + minute * 60 + second;
2456 int curr = localTime.tm_hour * 3600 + localTime.tm_min * 60 + localTime.tm_sec;
2457 return (target >= curr) ? target - curr : 86400 - (curr - target);
2458 }
2459
2460 /**
2461 * Parse timestamp (should be in form YYMMDDhhmmss or YYYYMMDDhhmmss), local time
2462 * If timestamp string is invalid returns default value
2463 */
2464 time_t LIBNETXMS_EXPORTABLE ParseDateTimeA(const char *text, time_t defaultValue)
2465 {
2466 int len = (int)strlen(text);
2467 if ((len != 12) && (len != 14))
2468 return defaultValue;
2469
2470 struct tm t;
2471 char buffer[16], *curr;
2472
2473 strncpy(buffer, text, 16);
2474 curr = &buffer[len - 2];
2475
2476 memset(&t, 0, sizeof(struct tm));
2477 t.tm_isdst = -1;
2478
2479 t.tm_sec = strtol(curr, NULL, 10);
2480 *curr = 0;
2481 curr -= 2;
2482
2483 t.tm_min = strtol(curr, NULL, 10);
2484 *curr = 0;
2485 curr -= 2;
2486
2487 t.tm_hour = strtol(curr, NULL, 10);
2488 *curr = 0;
2489 curr -= 2;
2490
2491 t.tm_mday = strtol(curr, NULL, 10);
2492 *curr = 0;
2493 curr -= 2;
2494
2495 t.tm_mon = strtol(curr, NULL, 10) - 1;
2496 *curr = 0;
2497
2498 if (len == 12)
2499 {
2500 curr -= 2;
2501 t.tm_year = strtol(curr, NULL, 10) + 100; // Assuming XXI century
2502 }
2503 else
2504 {
2505 curr -= 4;
2506 t.tm_year = strtol(curr, NULL, 10) - 1900;
2507 }
2508
2509 return mktime(&t);
2510 }
2511
2512 /**
2513 * Parse timestamp (should be in form YYMMDDhhmmss or YYYYMMDDhhmmss), local time
2514 * If timestamp string is invalid returns default value
2515 * (UNICODE version)
2516 */
2517 time_t LIBNETXMS_EXPORTABLE ParseDateTimeW(const WCHAR *text, time_t defaultValue)
2518 {
2519 char buffer[16];
2520 WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR, text, -1, buffer, 16, NULL, NULL);
2521 buffer[15] = 0;
2522 return ParseDateTimeA(buffer, defaultValue);
2523 }
2524
2525 /**
2526 * Get NetXMS directory
2527 */
2528 void LIBNETXMS_EXPORTABLE GetNetXMSDirectory(nxDirectoryType type, TCHAR *dir)
2529 {
2530 *dir = 0;
2531
2532 const TCHAR *homeDir = _tgetenv(_T("NETXMS_HOME"));
2533 if (homeDir != NULL)
2534 {
2535 #ifdef _WIN32
2536 switch(type)
2537 {
2538 case nxDirBin:
2539 _sntprintf(dir, MAX_PATH, _T("%s\\bin"), homeDir);
2540 break;
2541 case nxDirData:
2542 _sntprintf(dir, MAX_PATH, _T("%s\\var"), homeDir);
2543 break;
2544 case nxDirEtc:
2545 _sntprintf(dir, MAX_PATH, _T("%s\\etc"), homeDir);
2546 break;
2547 case nxDirLib:
2548 _sntprintf(dir, MAX_PATH, _T("%s\\lib"), homeDir);
2549 break;
2550 case nxDirShare:
2551 _sntprintf(dir, MAX_PATH, _T("%s\\share"), homeDir);
2552 break;
2553 default:
2554 nx_strncpy(dir, homeDir, MAX_PATH);
2555 break;
2556 }
2557 #else
2558 switch(type)
2559 {
2560 case nxDirBin:
2561 _sntprintf(dir, MAX_PATH, _T("%s/bin"), homeDir);
2562 break;
2563 case nxDirData:
2564 _sntprintf(dir, MAX_PATH, _T("%s/var/lib/netxms"), homeDir);
2565 break;
2566 case nxDirEtc:
2567 _sntprintf(dir, MAX_PATH, _T("%s/etc"), homeDir);
2568 break;
2569 case nxDirLib:
2570 _sntprintf(dir, MAX_PATH, _T("%s/lib/netxms"), homeDir);
2571 break;
2572 case nxDirShare:
2573 _sntprintf(dir, MAX_PATH, _T("%s/share/netxms"), homeDir);
2574 break;
2575 default:
2576 nx_strncpy(dir, homeDir, MAX_PATH);
2577 break;
2578 }
2579 #endif
2580 return;
2581 }
2582
2583 #ifdef _WIN32
2584 TCHAR installPath[MAX_PATH] = _T("");
2585 HKEY hKey;
2586 bool found = false;
2587 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("Software\\NetXMS\\Server"), 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS)
2588 {
2589 DWORD size = MAX_PATH * sizeof(TCHAR);
2590 found = (RegQueryValueEx(hKey, _T("InstallPath"), NULL, NULL, (BYTE *)installPath, &size) == ERROR_SUCCESS);
2591 RegCloseKey(hKey);
2592 }
2593
2594 if (!found)
2595 {
2596 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("Software\\NetXMS\\Agent"), 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS)
2597 {
2598 DWORD size = MAX_PATH * sizeof(TCHAR);
2599 found = (RegQueryValueEx(hKey, _T("InstallPath"), NULL, NULL, (BYTE *)installPath, &size) == ERROR_SUCCESS);
2600 RegCloseKey(hKey);
2601 }
2602 }
2603
2604 if (!found && (GetModuleFileName(NULL, installPath, MAX_PATH) > 0))
2605 {
2606 TCHAR *p = _tcsrchr(installPath, _T('\\'));
2607 if (p != NULL)
2608 {
2609 *p = 0;
2610 p = _tcsrchr(installPath, _T('\\'));
2611 if (p != NULL)
2612 {
2613 *p = 0;
2614 found = true;
2615 }
2616 }
2617 }
2618
2619 if (!found)
2620 {
2621 _tcscpy(installPath, _T("C:\\NetXMS"));
2622 }
2623
2624 switch(type)
2625 {
2626 case nxDirBin:
2627 _sntprintf(dir, MAX_PATH, _T("%s\\bin"), installPath);
2628 break;
2629 case nxDirData:
2630 _sntprintf(dir, MAX_PATH, _T("%s\\var"), installPath);
2631 break;
2632 case nxDirEtc:
2633 _sntprintf(dir, MAX_PATH, _T("%s\\etc"), installPath);
2634 break;
2635 case nxDirLib:
2636 _sntprintf(dir, MAX_PATH, _T("%s\\lib"), installPath);
2637 break;
2638 case nxDirShare:
2639 _sntprintf(dir, MAX_PATH, _T("%s\\share"), installPath);
2640 break;
2641 default:
2642 nx_strncpy(dir, installPath, MAX_PATH);
2643 break;
2644 }
2645 #else
2646 switch(type)
2647 {
2648 case nxDirBin:
2649 #ifdef PREFIX
2650 _tcscpy(dir, PREFIX _T("/bin"));
2651 #else
2652 _tcscpy(dir, _T("/usr/bin"));
2653 #endif
2654 break;
2655 case nxDirData:
2656 #ifdef STATEDIR
2657 _tcscpy(dir, STATEDIR);
2658 #else
2659 _tcscpy(dir, _T("/var/lib/netxms"));
2660 #endif
2661 break;
2662 case nxDirEtc:
2663 #ifdef PREFIX
2664 _tcscpy(dir, PREFIX _T("/etc"));
2665 #else
2666 _tcscpy(dir, _T("/etc"));
2667 #endif
2668 break;
2669 case nxDirLib:
2670 #ifdef PKGLIBDIR
2671 _tcscpy(dir, PKGLIBDIR);
2672 #else
2673 _tcscpy(dir, _T("/usr/lib/netxms"));
2674 #endif
2675 break;
2676 case nxDirShare:
2677 #ifdef DATADIR
2678 _tcscpy(dir, DATADIR);
2679 #else
2680 _tcscpy(dir, _T("/usr/share/netxms"));
2681 #endif
2682 break;
2683 default:
2684 _tcscpy(dir, _T("/usr"));
2685 break;
2686 }
2687 #endif
2688 }
2689
2690 #if WITH_JEMALLOC
2691
2692 /**
2693 * Callback for jemalloc's malloc_stats_print
2694 */
2695 static void jemalloc_stats_cb(void *arg, const char *text)
2696 {
2697 fwrite(text, 1, strlen(text), (FILE *)arg);
2698 }
2699
2700 #endif
2701
2702 /**
2703 * Get heap information using system-specific functions (if available)
2704 */
2705 TCHAR LIBNETXMS_EXPORTABLE *GetHeapInfo()
2706 {
2707 #if WITH_JEMALLOC || HAVE_MALLOC_INFO
2708 char *buffer = NULL;
2709 size_t size = 0;
2710 FILE *f = open_memstream(&buffer, &size);
2711 if (f == NULL)
2712 return NULL;
2713 #if WITH_JEMALLOC
2714 malloc_stats_print(jemalloc_stats_cb, f, NULL);
2715 #else
2716 malloc_info(0, f);
2717 #endif
2718 fclose(f);
2719 #ifdef UNICODE
2720 WCHAR *wtext = WideStringFromMBString(buffer);
2721 free(buffer);
2722 return wtext;
2723 #else
2724 return buffer;
2725 #endif
2726 #else
2727 return _tcsdup(_T("No heap information API available"));
2728 #endif
2729 }
2730
2731 /**
2732 * Destructor for abstract iterator
2733 */
2734 AbstractIterator::~AbstractIterator()
2735 {
2736 }
2737
2738 /**
2739 * Escape string for JSON
2740 */
2741 String LIBNETXMS_EXPORTABLE EscapeStringForJSON(const TCHAR *s)
2742 {
2743 String js;
2744 if (s == NULL)
2745 return js;
2746 for(const TCHAR *p = s; *p != 0; p++)
2747 {
2748 if (*p == _T('"') || *p == _T('\\'))
2749 js.append(_T('\\'));
2750 js.append(*p);
2751 }
2752 return js;
2753 }
2754
2755 /**
2756 * Escape string for agent parameter
2757 */
2758 String LIBNETXMS_EXPORTABLE EscapeStringForAgent(const TCHAR *s)
2759 {
2760 String out;
2761 if (s == NULL)
2762 return out;
2763 for(const TCHAR *p = s; *p != 0; p++)
2764 {
2765 if (*p == _T('"'))
2766 out.append(_T('"'));
2767 out.append(*p);
2768 }
2769 return out;
2770 }