Windows compatibility fixes
[public/netxms.git] / src / libnetxms / tools.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Copyright (C) 2003, 2004 Victor Kirhenshtein
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
14 **
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 **
19 ** $module: tools.cpp
20 **
21 **/
22
23 #include "libnetxms.h"
24 #include <stdarg.h>
25 #include <nms_agent.h>
26
27 #if !defined(_WIN32) && !defined(UNDER_CE)
28 # include <sys/time.h>
29 #endif
30
31 #ifdef _WIN32
32 # ifndef __GNUC__
33 # define EPOCHFILETIME (116444736000000000i64)
34 # else
35 # define EPOCHFILETIME (116444736000000000LL)
36 # endif
37 #endif
38
39
40 //
41 // Static data
42 //
43
44 static void (* m_pLogFunction)(int, TCHAR *);
45
46
47 //
48 // Calculate number of bits in netmask (in host byte order)
49 //
50
51 int LIBNETXMS_EXPORTABLE BitsInMask(DWORD dwMask)
52 {
53 int bits;
54 DWORD dwTemp;
55
56 for(bits = 0, dwTemp = dwMask; dwTemp != 0; bits++, dwTemp <<= 1);
57 return bits;
58 }
59
60
61 //
62 // Convert IP address from binary form (host bytes order) to string
63 //
64
65 TCHAR LIBNETXMS_EXPORTABLE *IpToStr(DWORD dwAddr, TCHAR *szBuffer)
66 {
67 static TCHAR szInternalBuffer[32];
68 TCHAR *szBufPtr;
69
70 szBufPtr = szBuffer == NULL ? szInternalBuffer : szBuffer;
71 _stprintf(szBufPtr, _T("%ld.%ld.%ld.%ld"), dwAddr >> 24, (dwAddr >> 16) & 255,
72 (dwAddr >> 8) & 255, dwAddr & 255);
73 return szBufPtr;
74 }
75
76
77 //
78 // Duplicate memory block
79 //
80
81 void LIBNETXMS_EXPORTABLE *nx_memdup(const void *pData, DWORD dwSize)
82 {
83 void *pNewData;
84
85 pNewData = malloc(dwSize);
86 memcpy(pNewData, pData, dwSize);
87 return pNewData;
88 }
89
90
91 //
92 // Swap two memory blocks
93 //
94
95 void LIBNETXMS_EXPORTABLE nx_memswap(void *pBlock1, void *pBlock2, DWORD dwSize)
96 {
97 void *pTemp;
98
99 pTemp = malloc(dwSize);
100 memcpy(pTemp, pBlock1, dwSize);
101 memcpy(pBlock1, pBlock2, dwSize);
102 memcpy(pBlock2, pTemp, dwSize);
103 free(pTemp);
104 }
105
106
107 //
108 // Match string against pattern with * and ? metasymbols
109 //
110
111 static BOOL MatchStringEngine(const TCHAR *pattern, const TCHAR *string)
112 {
113 const TCHAR *SPtr, *MPtr, *BPtr, *EPtr;
114 BOOL bFinishScan;
115
116 SPtr = string;
117 MPtr = pattern;
118
119 while(*MPtr != 0)
120 {
121 switch(*MPtr)
122 {
123 case _T('?'):
124 if (*SPtr != 0)
125 {
126 SPtr++;
127 MPtr++;
128 }
129 else
130 return FALSE;
131 break;
132 case _T('*'):
133 while(*MPtr == _T('*'))
134 MPtr++;
135 if (*MPtr == 0)
136 return TRUE;
137 if (*MPtr == _T('?')) // Handle "*?" case
138 {
139 if (*SPtr != 0)
140 SPtr++;
141 else
142 return FALSE;
143 break;
144 }
145 BPtr = MPtr; // Text block begins here
146 while((*MPtr != 0) && (*MPtr != _T('?')) && (*MPtr != _T('*')))
147 MPtr++; // Find the end of text block
148 // Try to find rightmost matching block
149 EPtr = NULL;
150 bFinishScan = FALSE;
151 do
152 {
153 while(1)
154 {
155 while((*SPtr != 0) && (*SPtr != *BPtr))
156 SPtr++;
157 if (_tcslen(SPtr) < ((size_t)(MPtr - BPtr)) * sizeof(TCHAR))
158 {
159 if (EPtr == NULL)
160 {
161 return FALSE; // Length of remained text less than remaining pattern
162 }
163 else
164 {
165 SPtr = EPtr; // Revert back to last match
166 bFinishScan = TRUE;
167 break;
168 }
169 }
170 if (!memcmp(BPtr, SPtr, (MPtr - BPtr) * sizeof(TCHAR)))
171 break;
172 SPtr++;
173 }
174 if (!bFinishScan)
175 {
176 SPtr += (MPtr - BPtr); // Increment SPtr because we alredy match current fragment
177 EPtr = SPtr; // Remember current point
178 }
179 }
180 while(!bFinishScan);
181 break;
182 default:
183 if (*MPtr == *SPtr)
184 {
185 SPtr++;
186 MPtr++;
187 }
188 else
189 return FALSE;
190 break;
191 }
192 }
193
194 return *SPtr == 0 ? TRUE : FALSE;
195 }
196
197 BOOL LIBNETXMS_EXPORTABLE MatchString(const TCHAR *pattern,
198 const TCHAR *string,
199 BOOL matchCase)
200 {
201 if (matchCase)
202 return MatchStringEngine(pattern, string);
203 else
204 {
205 TCHAR *tp, *ts;
206 BOOL bResult;
207
208 tp = _tcsdup(pattern);
209 ts = _tcsdup(string);
210 _tcsupr(tp);
211 _tcsupr(ts);
212 bResult = MatchStringEngine(tp, ts);
213 free(tp);
214 free(ts);
215 return bResult;
216 }
217 }
218
219
220 //
221 // Strip whitespaces and tabs off the string
222 //
223
224 void LIBNETXMS_EXPORTABLE StrStrip(TCHAR *str)
225 {
226 int i;
227
228 for(i=0;(str[i]!=0)&&((str[i]==_T(' '))||(str[i]==_T('\t')));i++);
229 if (i>0)
230 memmove(str,&str[i],(_tcslen(&str[i])+1) * sizeof(TCHAR));
231 for(i=_tcslen(str)-1;(i>=0)&&((str[i]==_T(' '))||(str[i]==_T('\t')));i--);
232 str[i+1]=0;
233 }
234
235
236 //
237 // Add string to enumeration result set
238 //
239
240 void LIBNETXMS_EXPORTABLE NxAddResultString(NETXMS_VALUES_LIST *pList, TCHAR *pszString)
241 {
242 // FIXME
243 pList->ppStringList = (TCHAR **)realloc(pList->ppStringList, sizeof(TCHAR *) * (pList->dwNumStrings + 1));
244 pList->ppStringList[pList->dwNumStrings] = _tcsdup(pszString);
245 pList->dwNumStrings++;
246 }
247
248
249 //
250 // Get arguments for parameters like name(arg1,...)
251 // Returns FALSE on processing error
252 //
253
254 BOOL LIBNETXMS_EXPORTABLE NxGetParameterArg(TCHAR *param, int index, TCHAR *arg, int maxSize)
255 {
256 TCHAR *ptr1, *ptr2;
257 int state, currIndex, pos;
258 BOOL bResult = TRUE;
259
260 arg[0] = 0; // Default is empty string
261 ptr1 = _tcschr(param, _T('('));
262 if (ptr1 == NULL)
263 return TRUE; // No arguments at all
264 for(ptr2 = ptr1 + 1, currIndex = 1, state = 0, pos = 0; state != -1; ptr2++)
265 {
266 switch(state)
267 {
268 case 0: // Normal
269 switch(*ptr2)
270 {
271 case _T(')'):
272 if (currIndex == index)
273 arg[pos] = 0;
274 state = -1; // Finish processing
275 break;
276 case _T('"'):
277 state = 1; // String
278 break;
279 case _T('\''): // String, type 2
280 state = 2;
281 break;
282 case _T(','):
283 if (currIndex == index)
284 {
285 arg[pos] = 0;
286 state = -1;
287 }
288 else
289 {
290 currIndex++;
291 }
292 break;
293 case 0:
294 state = -1; // Finish processing
295 bResult = FALSE; // Set error flag
296 break;
297 default:
298 if ((currIndex == index) && (pos < maxSize - 1))
299 arg[pos++] = *ptr2;
300 }
301 break;
302 case 1: // String in ""
303 switch(*ptr2)
304 {
305 case _T('"'):
306 state = 0; // Normal
307 break;
308 case _T('\\'): // Escape
309 ptr2++;
310 if ((currIndex == index) && (pos < maxSize - 1))
311 arg[pos++] = *ptr2;
312 if (ptr2 == 0) // Unexpected EOL
313 {
314 bResult = FALSE;
315 state = -1;
316 }
317 break;
318 case 0:
319 state = -1; // Finish processing
320 bResult = FALSE; // Set error flag
321 break;
322 default:
323 if ((currIndex == index) && (pos < maxSize - 1))
324 arg[pos++] = *ptr2;
325 }
326 break;
327 case 2: // String in ''
328 switch(*ptr2)
329 {
330 case _T('\''):
331 state = 0; // Normal
332 break;
333 case _T('\\'): // Escape
334 ptr2++;
335 if ((currIndex == index) && (pos < maxSize - 1))
336 arg[pos++] = *ptr2;
337 if (ptr2 == 0) // Unexpected EOL
338 {
339 bResult = FALSE;
340 state = -1;
341 }
342 break;
343 case 0:
344 state = -1; // Finish processing
345 bResult = FALSE; // Set error flag
346 break;
347 default:
348 if ((currIndex == index) && (pos < maxSize - 1))
349 arg[pos++] = *ptr2;
350 }
351 break;
352 }
353 }
354
355 if (bResult)
356 StrStrip(arg);
357 return bResult;
358 }
359
360
361 //
362 // Get current time in milliseconds
363 // Based on timeval.h by Wu Yongwei
364 //
365
366 INT64 LIBNETXMS_EXPORTABLE GetCurrentTimeMs(void)
367 {
368 #ifdef _WIN32
369 FILETIME ft;
370 LARGE_INTEGER li;
371 __int64 t;
372
373 GetSystemTimeAsFileTime(&ft);
374 li.LowPart = ft.dwLowDateTime;
375 li.HighPart = ft.dwHighDateTime;
376 t = li.QuadPart; // In 100-nanosecond intervals
377 t -= EPOCHFILETIME; // Offset to the Epoch time
378 t /= 10000; // Convert to milliseconds
379 #else
380 struct timeval tv;
381 INT64 t;
382
383 gettimeofday(&tv, NULL);
384 t = (INT64)tv.tv_sec * 1000 + (INT64)(tv.tv_usec / 10000);
385 #endif
386
387 return t;
388 }
389
390
391 //
392 // Extract word from line. Extracted word will be placed in buffer.
393 // Returns pointer to the next word or to the null character if end
394 // of line reached.
395 //
396
397 TCHAR LIBNETXMS_EXPORTABLE *ExtractWord(TCHAR *line, TCHAR *buffer)
398 {
399 TCHAR *ptr,*bptr;
400
401 for(ptr=line;(*ptr==_T(' '))||(*ptr==_T('\t'));ptr++); // Skip initial spaces
402 // Copy word to buffer
403 for(bptr=buffer;(*ptr!=_T(' '))&&(*ptr!=_T('\t'))&&(*ptr!=0);ptr++,bptr++)
404 *bptr=*ptr;
405 *bptr=0;
406 return ptr;
407 }
408
409
410 //
411 // Get system error string by call to FormatMessage
412 // (Windows only)
413 //
414
415 #if defined(_WIN32)
416
417 TCHAR LIBNETXMS_EXPORTABLE *GetSystemErrorText(DWORD dwError, TCHAR *pszBuffer, int iBufSize)
418 {
419 TCHAR *msgBuf;
420
421 if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
422 FORMAT_MESSAGE_FROM_SYSTEM |
423 FORMAT_MESSAGE_IGNORE_INSERTS,
424 NULL, dwError,
425 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
426 (LPTSTR)&msgBuf, 0, NULL) > 0)
427 {
428 msgBuf[_tcscspn(msgBuf, _T("\r\n"))] = 0;
429 _tcsncpy(pszBuffer, msgBuf, iBufSize);
430 LocalFree(msgBuf);
431 }
432 else
433 {
434 _stprintf(pszBuffer, _T("MSG 0x%08X - Unable to find message text"), dwError);
435 }
436 return pszBuffer;
437 }
438
439 #endif
440
441
442 //
443 // daemon() implementation for systems which doesn't have one
444 //
445
446 #if !(HAVE_DAEMON) && !defined(_NETWARE) && !defined(_WIN32)
447
448 int LIBNETXMS_EXPORTABLE daemon(int nochdir, int noclose)
449 {
450 int pid;
451
452 if ((pid = fork()) < 0)
453 return -1;
454 if (pid != 0)
455 exit(0); // Terminate parent
456
457 setsid();
458
459 if (!nochdir)
460 chdir("/");
461
462 if (!noclose)
463 {
464 fclose(stdin); // don't need stdin, stdout, stderr
465 fclose(stdout);
466 fclose(stderr);
467 }
468
469 return 0;
470 }
471
472 #endif
473
474
475 //
476 // Check if given name is a valid object name
477 //
478
479 BOOL LIBNETXMS_EXPORTABLE IsValidObjectName(TCHAR *pszName)
480 {
481 static TCHAR szValidCharacters[] = _T("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_- @()./");
482 return (pszName[0] != 0) && (_tcsspn(pszName, szValidCharacters) == _tcslen(pszName));
483 }
484
485
486 //
487 // Convert byte array to text representation
488 //
489
490 void LIBNETXMS_EXPORTABLE BinToStr(BYTE *pData, DWORD dwSize, char *pStr)
491 {
492 DWORD i;
493 char *pCurr;
494
495 for(i = 0, pCurr = pStr; i < dwSize; i++)
496 {
497 *pCurr++ = bin2hex(pData[i] >> 4);
498 *pCurr++ = bin2hex(pData[i] & 15);
499 }
500 *pCurr = 0;
501 }
502
503
504 //
505 // Convert string of hexadecimal digits to byte array
506 //
507
508 DWORD LIBNETXMS_EXPORTABLE StrToBin(char *pStr, BYTE *pData, DWORD dwSize)
509 {
510 DWORD i;
511 char *pCurr;
512
513 memset(pData, 0, dwSize);
514 for(i = 0, pCurr = pStr; (i < dwSize) && (*pCurr != 0); i++)
515 {
516 pData[i] = hex2bin(*pCurr) << 4;
517 pCurr++;
518 pData[i] |= hex2bin(*pCurr);
519 pCurr++;
520 }
521 return i;
522 }
523
524
525 //
526 // Initialize loggin for subagents
527 //
528
529 void LIBNETXMS_EXPORTABLE InitSubAgentsLogger(void (* pFunc)(int, TCHAR *))
530 {
531 m_pLogFunction = pFunc;
532 }
533
534
535 //
536 // Write message to agent's log
537 //
538
539 void LIBNETXMS_EXPORTABLE NxWriteAgentLog(int iLevel, TCHAR *pszFormat, ...)
540 {
541 TCHAR szBuffer[4096];
542 va_list args;
543
544 if (m_pLogFunction != NULL)
545 {
546 va_start(args, pszFormat);
547 _vsntprintf(szBuffer, 4096, pszFormat, args);
548 va_end(args);
549 m_pLogFunction(iLevel, szBuffer);
550 }
551 }
552
553
554 //
555 // Translate string
556 // NOTE: replacement string shouldn't be longer than original
557 //
558
559 void LIBNETXMS_EXPORTABLE TranslateStr(TCHAR *pszString, TCHAR *pszSubStr, TCHAR *pszReplace)
560 {
561 TCHAR *pszSrc, *pszDst;
562 int iSrcLen, iRepLen;
563
564 iSrcLen = _tcslen(pszSubStr);
565 iRepLen = _tcslen(pszReplace);
566 for(pszSrc = pszString, pszDst = pszString; *pszSrc != 0;)
567 {
568 if (!_tcsncmp(pszSrc, pszSubStr, iSrcLen))
569 {
570 memcpy(pszDst, pszReplace, sizeof(TCHAR) * iRepLen);
571 pszSrc += iSrcLen;
572 pszDst += iRepLen;
573 }
574 else
575 {
576 *pszDst++ = *pszSrc++;
577 }
578 }
579 *pszDst = 0;
580 }
581
582
583 //
584 // Get size of file in bytes
585 //
586
587 QWORD LIBNETXMS_EXPORTABLE FileSize(TCHAR *pszFileName)
588 {
589 #ifdef _WIN32
590 HANDLE hFind;
591 WIN32_FIND_DATA fd;
592 #else
593 struct stat fileInfo;
594 #endif
595
596 #ifdef _WIN32
597 hFind = FindFirstFile(pszFileName, &fd);
598 if (hFind == INVALID_HANDLE_VALUE)
599 return 0;
600 FindClose(hFind);
601
602 return (unsigned __int64)fd.nFileSizeLow + ((unsigned __int64)fd.nFileSizeHigh << 32);
603 #else
604 if (stat(pszFileName, &fileInfo) == -1)
605 return 0;
606
607 return (QWORD)fileInfo.st_size;
608 #endif
609 }
610
611
612 //
613 // Get pointer to clean file name (without path specification)
614 //
615
616 TCHAR LIBNETXMS_EXPORTABLE *GetCleanFileName(TCHAR *pszFileName)
617 {
618 TCHAR *ptr;
619
620 ptr = pszFileName + _tcslen(pszFileName);
621 while((ptr >= pszFileName) && (*ptr != _T('/')) && (*ptr != _T('\\')) && (*ptr != _T(':')))
622 ptr--;
623 return (ptr + 1);
624 }
625
626
627 //
628 // Translate DCI data type from text form to code
629 //
630
631 int LIBNETXMS_EXPORTABLE NxDCIDataTypeFromText(TCHAR *pszText)
632 {
633 static TCHAR *m_pszValidTypes[] = { _T("INT"), _T("UINT"), _T("INT64"),
634 _T("UINT64"), _T("STRING"),
635 _T("FLOAT"), NULL };
636 int i;
637
638 for(i = 0; m_pszValidTypes[i] != NULL; i++)
639 if (!_tcsicmp(pszText, m_pszValidTypes[i]))
640 return i;
641 return -1; // Invalid data type
642 }
643
644
645 //
646 // Extended send() - send all data even if single call to send()
647 // cannot handle them all
648 //
649
650 int LIBNETXMS_EXPORTABLE SendEx(int nSocket, const void *pBuff,
651 size_t nSize, int nFlags)
652 {
653 int nLeft = nSize;
654 int nRet;
655
656 do
657 {
658 nRet = send(nSocket, ((char *)pBuff) + (nSize - nLeft), nLeft, nFlags);
659 if (nRet <= 0)
660 {
661 break;
662 }
663 nLeft -= nRet;
664 } while (nLeft > 0);
665
666 return nLeft == 0 ? nSize : nRet;
667 }