1 krisbash 1.1 /*
2 **==============================================================================
3 **
4 ** Open Management Infrastructure (OMI)
5 **
6 ** Copyright (c) Microsoft Corporation
7 **
8 ** Licensed under the Apache License, Version 2.0 (the "License"); you may not
9 ** use this file except in compliance with the License. You may obtain a copy
10 ** of the License at
11 **
12 ** http://www.apache.org/licenses/LICENSE-2.0
13 **
14 ** THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 ** KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
16 ** WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
17 ** MERCHANTABLITY OR NON-INFRINGEMENT.
18 **
19 ** See the Apache 2 License for the specific language governing permissions
20 ** and limitations under the License.
21 **
22 krisbash 1.1 **==============================================================================
23 */
24
25 #include <ctype.h>
26 #include <time.h>
27 #include <pal/file.h>
28 #include <pal/strings.h>
29 #include <pal/format.h>
30 #include <base/process.h>
31 #include <pal/sleep.h>
32 #include <base/paths.h>
33 #include <base/log.h>
34 #include <pal/dir.h>
35 #include <pal/lock.h>
36 #include "util.h"
37
38 #ifdef _MSC_VER
39 # include <windows.h>
40 # include <shlobj.h>
41 #else
42 # include <unistd.h>
43 krisbash 1.1 # include <sys/types.h>
44 # include <sys/time.h>
45 # include <pwd.h>
46 # include <unistd.h>
47 #endif
48
49 #ifdef _PREFAST_
50 #pragma prefast(push)
51 /* ANSI API ('SHGetFolderPathA') should not be called from Unicode modules. */
52 /* Calling ANSI APIs leads to unexpected results when handling multi-lingual */
53 /* text or Unicode-only languages. Use the Unicode version of this API instead.*/
54 #pragma prefast(disable:38020)
55 #endif
56
57 /* get user home directory */
58 const char* gethomedir()
59 {
60 #ifdef _MSC_VER
61 static char path[MAX_PATH];
62 if (SHGetFolderPathA( NULL, CSIDL_PROFILE, NULL, 0, path ) != S_OK)
63 {
64 krisbash 1.1 path[0] = '\0';
65 }
66 return (const char *)path;
67 #else
68 struct passwd *pw = getpwuid(getuid());
69 return pw->pw_dir;
70 #endif
71 }
72
73 MI_Char* ansiToMI(const char* src)
74 {
75 MI_Char* mistr = NULL;
76 if (g_batch && src)
77 {
78 MI_Uint32 size = strlen(src) + 1;
79 mistr = (MI_Char*) Batch_Get(g_batch, size*sizeof(MI_Char));
80 if (mistr)
81 {
82 MI_Uint32 i;
83 for (i = 0; i < size; i++)
84 {
85 krisbash 1.1 mistr[i] = (MI_Char)src[i];
86 }
87 }
88 }
89 return mistr;
90 }
91
92 char* MIToansi(const MI_Char* src)
93 {
94 #if (MI_CHAR_TYPE == 2)
95 char* str = NULL;
96 if (g_batch && src)
97 {
98 MI_Uint32 size = wcslen(src) + 1;
99 str = (char*) Batch_Get(g_batch, size*sizeof(char));
100 if (str)
101 {
102 MI_Uint32 i;
103 for (i = 0; i < size; i++)
104 {
105 str[i] = (char)src[i];
106 krisbash 1.1 }
107 }
108 }
109 return str;
110 #else
111 return (char*)src;
112 #endif
113 }
114
115 int StringCompare(const MI_Char* s1, const MI_Char* s2)
116 {
117 return Tcscasecmp(s1, s2);
118 }
119
120 int __GetCurrentTimeInUsec(MI_Uint64* usec)
121 {
122 #if defined(CONFIG_OS_WINDOWS)
123 FILETIME ft;
124 ULARGE_INTEGER tmp;
125
126 GetSystemTimeAsFileTime(&ft);
127 krisbash 1.1 tmp.u.LowPart = ft.dwLowDateTime;
128 tmp.u.HighPart = ft.dwHighDateTime;
129 tmp.QuadPart -= 0X19DB1DED53E8000;
130 *usec = tmp.QuadPart / (UINT64)10;
131
132 return 0;
133 #else
134 struct timeval tv;
135 struct timezone tz;
136 memset(&tv, 0, sizeof(tv));
137 memset(&tz, 0, sizeof(tz));
138
139 if (gettimeofday(&tv, &tz) != 0)
140 return -1;
141
142 *usec = (MI_Uint64)tv.tv_sec * (MI_Uint64)1000000 + (MI_Uint64)tv.tv_usec;
143 return 0;
144 #endif
145 }
146
147 #define TIMESTAMP_SIZE 256
148 krisbash 1.1 static int __GetTimeStamp(_Pre_writable_size_(TIMESTAMP_SIZE) char buf[TIMESTAMP_SIZE])
149 {
150 #if defined(CONFIG_OS_WINDOWS)
151 {
152 SYSTEMTIME systime;
153 GetLocalTime(&systime);
154
155 sprintf_s(
156 buf,
157 TIMESTAMP_SIZE,
158 "%02u/%02u/%02u %02u:%02u:%02u",
159 systime.wYear,
160 systime.wMonth,
161 systime.wDay,
162 systime.wHour,
163 systime.wMinute,
164 systime.wSecond);
165 }
166 #else
167 MI_Uint64 usec;
168
169 krisbash 1.1 if (__GetCurrentTimeInUsec(&usec) != 0)
170 {
171 buf[0] = '\0';
172 return -1;
173 }
174
175 {
176 time_t t = usec / 1000000;
177 struct tm tm;
178 localtime_r(&t, &tm);
179 sprintf(
180 buf,
181 "%02u/%02u/%02u %02u:%02u:%02u",
182 tm.tm_year + 1900,
183 tm.tm_mon + 1,
184 tm.tm_mday,
185 tm.tm_hour,
186 tm.tm_min,
187 tm.tm_sec);
188 }
189 #endif
190 krisbash 1.1
191 return 0;
192 }
193
194 static void _PrintThreadID(FILE* f)
195 {
196 ThreadID currentthread = Thread_ID();
197 #ifdef _MSC_VER
198 fprintf(f, "Thread: 0x%x", (unsigned int)(currentthread.__impl));
199 #else
200 size_t i;
201 size_t threadsize = sizeof(currentthread.__impl);
202 unsigned char *ptc = (unsigned char*)(void*)(¤tthread.__impl);
203 fprintf(f, "Thread: 0x");
204 for(i=0; i < threadsize; i++) {
205 fprintf(f, "%02x", (unsigned)(ptc[i]));
206 }
207 #endif
208 }
209
210 void _WriteHeader(
211 krisbash 1.1 const char* file,
212 unsigned int line)
213 {
214 char buf[256];
215 __GetTimeStamp(buf);
216 _PrintThreadID(g_logfile);
217 fprintf(g_logfile, " : %s : ", buf);
218 if (file)
219 fprintf(g_logfile, "%s(%u): ", file, line);
220 }
221
222 void WriteLog(const char* fmt, ...)
223 {
224 if (g_logfile)
225 {
226 va_list ap;
227 memset(&ap, 0, sizeof(ap));
228 va_start(ap, fmt);
229 Vfprintf(g_logfile, fmt, ap);
230 va_end(ap);
231
232 krisbash 1.1 fprintf(g_logfile, "\r\n");
233 FlushLog(g_logfile);
234 }
235 }
236
237 void FlushLog(FILE* file)
238 {
239 if (file)
240 {
241 fflush(file);
242 //File_Close(g_logfile);
243 //g_logfile = File_Open(g_logfilepath, "a+");
244 }
245 }
246
247
248 /*
249 **==============================================================================
250 **
251 ** Consume instance
252 **
253 krisbash 1.1 **==============================================================================
254 */
255 static MI_Result _ConsumeInstanceResults(
256 _In_ InstanceOperationStruct* ios,
257 _In_ MI_Operation *miOperation)
258 {
259 MI_Result miResult = MI_RESULT_FAILED;
260 MI_Boolean moreResults = MI_FALSE;
261
262 if (MI_TRUE == ios->sync)
263 {
264 do
265 {
266 const MI_Instance *miInstanceResult = NULL;
267 MI_Result _miResult;
268 const MI_Char *errorString = NULL;
269 const MI_Instance *errorDetails = NULL;
270
271 _miResult = MI_Operation_GetInstance(
272 miOperation,
273 &miInstanceResult,
274 krisbash 1.1 &moreResults,
275 &miResult,
276 &errorString,
277 &errorDetails);
278 if (_miResult != MI_RESULT_OK)
279 {
280 miResult = _miResult;
281 }
282 if (miInstanceResult)
283 ios->count++;
284 } while ((miResult == MI_RESULT_OK) && (moreResults == MI_TRUE));
285 }
286 else
287 {
288 ptrdiff_t finished;
289 finished = ios->finished;
290 while (!finished)
291 {
292 CondLock_Wait((ptrdiff_t)&ios->finished, &ios->finished, finished, CONDLOCK_DEFAULT_SPINCOUNT);
293 finished = ios->finished;
294 miResult = ios->finalResult;
295 krisbash 1.1 }
296 }
297
298 return miResult;
299 }
300
301 /*
302 **==============================================================================
303 **
304 ** Async operation, the callback to receive instance results
305 **
306 **==============================================================================
307 */
308 static MI_Result g_finalResult;
309 static MI_Boolean g_called;
310 static MI_Boolean g_moreResults;
311 void MI_CALL _InstanceResultCallback(
312 _In_opt_ MI_Operation *operation,
313 _In_ void *callbackContext,
314 _In_opt_ const MI_Instance *instance,
315 MI_Boolean moreResults,
316 krisbash 1.1 _In_ MI_Result resultCode,
317 _In_opt_z_ const MI_Char *errorString,
318 _In_opt_ const MI_Instance *errorDetails,
319 _In_opt_ MI_Result (MI_CALL * resultAcknowledgement)(_In_ MI_Operation *operation))
320 {
321 InstanceOperationStruct* ios = (InstanceOperationStruct*)callbackContext;
322 if (instance)
323 ios->count ++;
324 if (moreResults == MI_FALSE)
325 {
326 ios->finalResult = resultCode;
327 ios->finished = 1;
328 CondLock_Broadcast((ptrdiff_t)&ios->finished);
329 }
330 g_finalResult = resultCode;
331 g_called = MI_TRUE;
332 g_moreResults = moreResults;
333 }
334
335 /*
336 **==============================================================================
337 krisbash 1.1 **
338 ** Read instance for given class
339 **
340 **==============================================================================
341 */
342 _Use_decl_annotations_
343 MI_Result EnumerateInstance(
344 InstanceOperationStruct* ios,
345 const MI_Char* nameSpace,
346 const MI_Char* className)
347 {
348 MI_Result miResult;
349 MI_Operation miOperation = MI_OPERATION_NULL;
350 MI_OperationCallbacks _callbacks = MI_OPERATIONCALLBACKS_NULL;
351 MI_OperationCallbacks *callbacks = NULL;
352
353 if (ios->sync == MI_FALSE)
354 {
355 _callbacks.callbackContext = ios;
356 _callbacks.instanceResult = _InstanceResultCallback;
357 callbacks = &_callbacks;
358 krisbash 1.1 ios->finished = 0;
359 ios->finalResult = MI_RESULT_FAILED;
360 }
361
362 MI_Session_EnumerateInstances(&ios->session, 0, NULL, nameSpace, className, MI_FALSE, callbacks, &miOperation);
363 miResult = _ConsumeInstanceResults(ios, &miOperation);
364 MI_Operation_Close(&miOperation);
365 return miResult;
366 }
367
368
369 static Process serverProcess;
370 MI_Char s_socketFile[PAL_MAX_PATH_SIZE];
371 char s_socketFile_a[PAL_MAX_PATH_SIZE];
372
373 /*
374 **==============================================================================
375 **
376 ** Remember if the server is started or not
377 **
378 **==============================================================================
379 krisbash 1.1 */
380 int g_serverstarted = 0;
381
382 #if defined(_MSC_VER)
383 unsigned short _GetUnittestPortNumber()
384 {
385 return 21718;
386 }
387 #else
388 unsigned short _GetUnittestPortNumber()
389 {
390 return 10000 + ((geteuid() % 200) * 200);
391 }
392 #endif
393 unsigned short UnittestPortNumberWSMANHTTP()
394 {
395 return _GetUnittestPortNumber() + 198;
396 }
397 unsigned short UnittestPortNumberWSMANHTTPS()
398 {
399 return _GetUnittestPortNumber() + 197;
400 krisbash 1.1 }
401
402 MI_Result StartOmiserver()
403 {
404 const char* path = OMI_GetPath(ID_SERVERPROGRAM);
405 const char* argv[17];
406 char http[32];
407 char https[32];
408 int i;
409 InstanceOperationStruct ios;
410 MI_Result r;
411
412 memset(&ios, 0, sizeof(InstanceOperationStruct));
413
414 //
415 // kill running omiserver if have any
416 //
417 {
418 char buf[64];
419 sprintf(buf, "pkill -9 %s", "omiserver");
420 system(buf);
421 krisbash 1.1 Sleep_Milliseconds(10);
422 }
423
424 Snprintf(http, sizeof(http),"%d", UnittestPortNumberWSMANHTTP());
425 Snprintf(https, sizeof(https),"%d", UnittestPortNumberWSMANHTTPS());
426
427 Strlcpy(s_socketFile_a, OMI_GetPath(ID_SOCKETFILE), sizeof(s_socketFile_a)/sizeof(s_socketFile_a[0]));
428 TcsStrlcpy(s_socketFile, s_socketFile_a, sizeof(s_socketFile)/sizeof(s_socketFile[0]));
429
430 argv[0] = path;
431 argv[1] = "--rundir";
432 #if defined(CONFIG_OS_WINDOWS)
433 argv[2] = "..";
434 #else
435 argv[2] = OMI_GetPath(ID_PREFIX);
436 #endif
437 argv[3] = "--ignoreAuthentication";
438 argv[4] = "--socketfile";
439 argv[5] = s_socketFile_a;
440 argv[6] = "--httpport";
441 argv[7] = http;
442 krisbash 1.1 argv[8] = "--httpsport";
443 argv[9] = https;
444 argv[10] = "--livetime";
445 argv[11] = "1800";
446
447 argv[12] = "--loglevel";
448 argv[13] = Log_GetLevelString(Log_GetLevel());
449 argv[14] = NULL;
450
451 if (Process_StartChild(&serverProcess, path, (char**)argv) != 0)
452 {
453 NitsAssert(0, PAL_T("Failed to create omiserver process"));
454 return MI_RESULT_FAILED;
455 }
456
457 g_serverstarted = 1;
458
459 /* wait for server to start */
460 r = MI_Application_Initialize(0, NULL, NULL, &ios.app);
461 if (r != MI_RESULT_OK)
462 {
463 krisbash 1.1 NitsAssert(0, PAL_T("Failed to Initialize MI_Application"));
464 return r;
465 }
466
467 for (i = 0; i < 100; i++)
468 {
469 Sleep_Milliseconds(50);
470 r = MI_Application_NewSession(&ios.app, NULL, NULL, NULL, NULL, NULL, &ios.session);
471 if (r == MI_RESULT_OK)
472 {
473 break;
474 }
475 }
476
477 if (r != MI_RESULT_OK)
478 {
479 NitsAssert(0, PAL_T("Failed to create session"));
480 MI_Application_Close(&ios.app);
481 return r;
482 }
483
484 krisbash 1.1 /* TODO: Use MI_Session_TestConnection if it is implemented */
485 for (i = 0; i < 1000; i++)
486 {
487 g_finalResult = MI_RESULT_OK;
488 g_called = MI_FALSE;
489 g_moreResults = MI_FALSE;
490 r = EnumerateInstance(&ios, MI_T("root/omi"), MI_T("omi_identify"));
491 if (r == MI_RESULT_OK)
492 {
493 break;
494 }
495 Sleep_Milliseconds(50);
496 }
497 if (r != MI_RESULT_OK)
498 {
499 NitsAssert(0, PAL_T("Failed to enumerate omi_identify instance"));
500 }
501 MI_Session_Close(&ios.session, NULL, NULL);
502 MI_Application_Close(&ios.app);
503 return r;
504 }
505 krisbash 1.1
506 MI_Result StopOmiserver()
507 {
508 MI_Result r = MI_RESULT_OK;
509 if (g_serverstarted == 1)
510 {
511 /*
512 * For e2e test, server might be busy, and e2e cancellation is not complete yet,
513 * sleep two seconds to mitigate server hang issues during shutting down.
514 */
515 Sleep_Milliseconds(2000);
516 if (Process_StopChild(&serverProcess) != 0)
517 {
518 memset((void*)&serverProcess, 0, sizeof(Process));
519 r = MI_RESULT_FAILED;
520 }
521 g_serverstarted = 0;
522 }
523 return r;
524 }
525
526 krisbash 1.1
527 /*
528 **==============================================================================
529 **
530 ** string hash function
531 **
532 **==============================================================================
533 */
534
535 /* A number used to caculate hash value */
536 #define HASH_SEED_PRIME_NUMBER 1313038763
537
538 /* String hash algorithm */
539 PAL_EXTERN_C size_t StringHash(_In_ const HashBucket* bucket)
540 {
541 StringBucket* strbucket = (StringBucket*)bucket;
542 size_t hash = HASH_SEED_PRIME_NUMBER;
543 const MI_Char* p = strbucket->key;
544 while(*p)
545 {
546 hash ^= ((hash << 5) + tolower(*p) + (hash >> 2));
547 krisbash 1.1 p++;
548 }
549 return hash;
550 }
551
552 PAL_EXTERN_C int StringBucketEqual(
553 const HashBucket* bucket1_,
554 const HashBucket* bucket2_)
555 {
556 StringBucket* bucket1 = (StringBucket*)bucket1_;
557 StringBucket* bucket2 = (StringBucket*)bucket2_;
558 return Tcscasecmp(bucket1->key, bucket2->key) == 0;
559 }
560
561 PAL_EXTERN_C void StringBucketRelease(
562 HashBucket* bucket_)
563 {
564 StringBucket* bucket = (StringBucket*)bucket_;
565 PAL_Free(bucket->key);
566 PAL_Free(bucket);
567 }
568 krisbash 1.1
569 _Use_decl_annotations_
570 int StringHashMap_Init(
571 StringHashMap* self)
572 {
573 return HashMap_Init(&self->map, 32, StringHash, StringBucketEqual, StringBucketRelease);
574 }
575
576 _Use_decl_annotations_
577 void StringHashMap_Destroy(
578 StringHashMap* self)
579 {
580 HashMap_Destroy(&self->map);
581 }
582
583 /* returns:
584 - non-null : found
585 - null : key not present
586 */
587 _Use_decl_annotations_
588 StringBucket* StringHashMap_Find(
589 krisbash 1.1 StringHashMap* self,
590 const MI_Char* key)
591 {
592 StringBucket b;
593 b.key = (MI_Char*)key;
594 return (StringBucket*)HashMap_Find(&self->map, (const HashBucket*)&b);
595 }
596
597 /* returns:
598 - 0 : inserted the new item
599 - 1 : the item is already present (and HashMap was not modified)
600 - -1 : out of memory
601
602 (there are no failure paths / no other return value is possible)
603 */
604 _Use_decl_annotations_
605 int StringHashMap_Insert(
606 StringHashMap* self,
607 const MI_Char* key)
608 {
609 StringBucket *b;
610 krisbash 1.1 b = (StringBucket*)PAL_Calloc( 1, sizeof(StringBucket) );
611 if ( b )
612 {
613 b->key = PAL_Tcsdup( key );
614 b->count = 0;
615 return HashMap_Insert( &self->map, (HashBucket*)b );
616 }
617 return -1;
618 }
619
620 /* returns:
621 - 0 : success
622 - -1 : key not found - hashmap remains unchanged.
623 */
624 _Use_decl_annotations_
625 int StringHashMap_Remove(
626 StringHashMap* self,
627 const MI_Char* key)
628 {
629 StringBucket b;
630 b.key = (MI_Char*)key;
631 krisbash 1.1 return HashMap_Remove(&self->map, (const HashBucket*)&b);
632 }
633
634
635 /*
636 Returns one element from the hash table. May be invoked
637 multiple times (e.g. if you remove the element), returning
638 null when empty.
639
640 *iter should be zero initialized before first call
641 */
642 _Use_decl_annotations_
643 const StringBucket* StringHashMap_Top(
644 StringHashMap* self,
645 size_t *iter)
646 {
647 return (StringBucket*) HashMap_Top( &self->map, iter );
648 }
649
650 MI_Result StartServer_Assert()
651 {
652 krisbash 1.1 # if !defined(_MSC_VER)
653 MI_Result r = StartOmiserver();
654 NitsAssert(r == MI_RESULT_OK, PAL_T("Failed to start Omiserver"));
655 return r;
656 #else
657 return MI_RESULT_OK;
658 #endif
659 }
660
661 MI_Result StopServer_Assert()
662 {
663 # if !defined(_MSC_VER)
664 MI_Result r = StopOmiserver();
665 NitsAssert(r == MI_RESULT_OK, PAL_T("Failed to stop Omiserver"));
666 return r;
667 #else
668 return MI_RESULT_OK;
669 #endif
670 }
671
672 #ifdef _PREFAST_
673 krisbash 1.1 #pragma prefast(pop)
674 #endif
|