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