1 krisbash 1.1 /*============================================================================
2 * Copyright (C) Microsoft Corporation, All rights reserved.
3 *============================================================================
4 */
5
6 #include <base/paths.h>
7 #include "ProtocolHandlerCache.h"
8 #include "InteractionProtocolHandler.h"
9 #include <pal/atomic.h>
10 #include <base/conf.h>
11
12 struct _staticProtocolHandlers
13 {
14 MI_Char *name;
15 ProtocolHandlerInitializeV1 dllFunctionPointer;
16 MI_Boolean defaultLocal;
17 MI_Boolean defaultRemote;
18 } g_staticallyLoadedProtocolHandlers[] =
19 {
20 { MI_T("OMI_SOCKETS"), InteractionProtocolHandler_Application_Initialize, MI_TRUE, MI_FALSE}
21 };
22 krisbash 1.1
23 _Success_(return == MI_RESULT_OK)
24 MI_Result ProtocolHandlerCache_InsertProtocolEntries(_Inout_ ProtocolHandlerCache *cache,
25 _In_z_ const char *protocolHandlerName,
26 _In_z_ const char *protocolHandlerDLL,
27 _In_z_ const char *protocolHandlerDllEntryPoint,
28 MI_Uint32 protocolHandlerMajorVersion,
29 MI_Uint32 protocolHandlerMinorVersion,
30 _Outptr_ ProtocolHandlerCacheItem **cacheItem)
31 {
32 ProtocolHandlerCacheItem *item;
33
34 item = PAL_Malloc(sizeof(ProtocolHandlerCacheItem));
35 if (item == NULL)
36 {
37 return MI_RESULT_SERVER_LIMITS_EXCEEDED;
38 }
39
40 memset(item, 0, sizeof(ProtocolHandlerCacheItem));
41
42 TcsStrlcpy(item->name, protocolHandlerName, (sizeof(item->name)/sizeof(item->name[0])));
43 krisbash 1.1 Strlcpy(item->dllPath, protocolHandlerDLL, sizeof(item->dllPath)/sizeof(item->dllPath[0]));
44 Strlcpy(item->dllEntryPoint, protocolHandlerDllEntryPoint, sizeof(item->dllEntryPoint)/sizeof(item->dllEntryPoint[0]));
45
46 /* Plug this into the head of the list */
47 item->nextItem = cache->cacheList;
48 cache->cacheList = item;
49 *cacheItem = item;
50
51 return MI_RESULT_OK;
52 }
53
54 _Success_(return == MI_RESULT_OK)
55 MI_Result ProtocolHandlerCache_CreateAllProtocolEntries(_Inout_ ProtocolHandlerCache *cache)
56 {
57 char _path[PAL_MAX_PATH_SIZE];
58 const char *path = NULL;
59 Conf *configSubSystem;
60 MI_Result ret = MI_RESULT_OK;
61 int staticHandlerLoop;
62 MI_Char defaultremoteprotocolhandler[30] = { 0 };
63 MI_Char defaultlocalprotocolhandler[30] = { 0 };
64 krisbash 1.1 ProtocolHandlerCacheItem *cacheItem = NULL;
65
66 path = OMI_GetPath(ID_SYSCONFDIR);
67 if (path == NULL)
68 {
69 return MI_RESULT_FAILED;
70 }
71
72 /* Only need to copy on Windows as the paths are dynamic */
73 Strlcpy(_path, path, sizeof(_path)/sizeof(_path[0]));
74 Strlcat(_path, "/omicli.conf", PAL_MAX_PATH_SIZE);
75 path = _path;
76
77 configSubSystem = Conf_Open(path);
78 if (!configSubSystem)
79 {
80 /* err(ZT("failed to open configuration file: %s"), scs(path)); */
81 return MI_RESULT_FAILED;
82 }
83
84 for (;;)
85 krisbash 1.1 {
86 const char* key;
87 const char* value;
88 int r = Conf_Read(configSubSystem, &key, &value);
89
90 if (r == -1)
91 {
92 /* err(ZT("%s: %s\n"), path, scs(Conf_Error(conf)));*/
93 ret = MI_RESULT_FAILED;
94 break;
95 }
96
97 if (r == 1)
98 break;
99
100 if (strncmp(key, "protocolhandler", 15) == 0)
101 {
102 char *cursor;
103 const char *protocolHandlerName;
104 const char *protocolHandlerDLL;
105 const char *protocolHandlerDllEntryPoint;
106 krisbash 1.1 MI_Uint32 protocolHandlerMajorVersion;
107 MI_Uint32 protocolHandlerMinorVersion;
108
109 /* We found a protocol handler, need to */
110
111 /* First protocol handler name */
112 cursor = Strchr(value, ',');
113 if (cursor == NULL)
114 {
115 ret = MI_RESULT_FAILED;
116 break;
117 }
118 *cursor = '\0';
119 protocolHandlerName = value;
120
121 value = cursor+1; /* move past ',' */
122
123 /* Second DLL*/
124 cursor = Strchr(value, ',');
125 if (cursor == NULL)
126 {
127 krisbash 1.1 ret = MI_RESULT_FAILED;
128 break;
129 }
130
131 protocolHandlerDLL = value;
132 value = cursor+1; /* move past ',' */
133
134 /* Third DLL entry point */
135 cursor = Strchr(value, ',');
136 if (cursor == NULL)
137 {
138 ret = MI_RESULT_FAILED;
139 break;
140 }
141 protocolHandlerDllEntryPoint = value;
142 value = cursor+1; /* move past ',' */
143
144 /* Forth is major version */
145 protocolHandlerMajorVersion = Strtoul(value, &cursor, 10);
146 if (*cursor != ',')
147 {
148 krisbash 1.1 ret = MI_RESULT_FAILED;
149 break;
150 }
151 value = cursor+1; /* move past ',' */
152
153 /* Fifth is minor version */
154 protocolHandlerMinorVersion = Strtoul(value, &cursor, 10);
155 if (*cursor != '\0')
156 {
157 ret = MI_RESULT_FAILED;
158 break;
159 }
160 ret = ProtocolHandlerCache_InsertProtocolEntries(cache, protocolHandlerName, protocolHandlerDLL, protocolHandlerDllEntryPoint, protocolHandlerMajorVersion, protocolHandlerMinorVersion, &cacheItem);
161 if (ret != MI_RESULT_OK)
162 break;
163 }
164 else if (strncmp(key, "defaultlocalprotocolhandler", 27) == 0)
165 {
166 TcsStrlcpy(defaultlocalprotocolhandler, value, sizeof(defaultlocalprotocolhandler)/sizeof(defaultlocalprotocolhandler[0]));
167 }
168 else if (strncmp(key, "defaultremoteprotocolhandler", 28) == 0)
169 krisbash 1.1 {
170 TcsStrlcpy(defaultremoteprotocolhandler, value, sizeof(defaultremoteprotocolhandler)/sizeof(defaultremoteprotocolhandler[0]));
171 }
172 }
173 /* Close configuration file */
174 Conf_Close(configSubSystem);
175
176 /* Fix up the config-based default handlers */
177 if (defaultlocalprotocolhandler[0] || defaultremoteprotocolhandler[0])
178 {
179 cacheItem = cache->cacheList;
180 while (cacheItem)
181 {
182 if (Tcscmp(defaultlocalprotocolhandler, cacheItem->name) == 0)
183 {
184 cache->defaultLocalItem = cacheItem;
185 }
186 else if (Tcscmp(defaultremoteprotocolhandler, cacheItem->name) == 0)
187 {
188 cache->defaultRemoteItem = cacheItem;
189 }
190 krisbash 1.1 cacheItem = cacheItem->nextItem;
191 }
192 }
193
194 if (ret == MI_RESULT_OK)
195 {
196 /* Handler staticly compiled in ones now */
197 for (staticHandlerLoop = 0; staticHandlerLoop != sizeof(g_staticallyLoadedProtocolHandlers)/sizeof(g_staticallyLoadedProtocolHandlers[0]); staticHandlerLoop++)
198 {
199 ProtocolHandlerCacheItem *item;
200 if (g_staticallyLoadedProtocolHandlers[staticHandlerLoop].name == NULL)
201 break;
202
203 item = PAL_Malloc(sizeof(ProtocolHandlerCacheItem));
204 if (item == NULL)
205 {
206 ret = MI_RESULT_SERVER_LIMITS_EXCEEDED;
207 break;
208 }
209 memset(item, 0, sizeof(ProtocolHandlerCacheItem));
210
211 krisbash 1.1 Tcslcpy(item->name, g_staticallyLoadedProtocolHandlers[staticHandlerLoop].name, sizeof(item->name)/sizeof(item->name[0]));
212
213 item->dllFunctionPointer = g_staticallyLoadedProtocolHandlers[staticHandlerLoop].dllFunctionPointer;
214
215 /* Plug this into the head of the list */
216 item->nextItem = cache->cacheList;
217 cache->cacheList = item;
218
219 if (g_staticallyLoadedProtocolHandlers[staticHandlerLoop].defaultLocal && (cache->defaultLocalItem == NULL))
220 cache->defaultLocalItem = item;
221 if (g_staticallyLoadedProtocolHandlers[staticHandlerLoop].defaultRemote && (cache->defaultRemoteItem == NULL))
222 cache->defaultRemoteItem = item;
223 }
224 }
225
226 /* if there is an error, free all transports */
227 if (ret != MI_RESULT_OK)
228 {
229 while (cache->cacheList)
230 {
231 ProtocolHandlerCacheItem *item;
232 krisbash 1.1 item = cache->cacheList;
233
234 /* unlink this item from list */
235 cache->cacheList = item->nextItem;
236
237 PAL_Free(item);
238 }
239 }
240 return ret;
241 }
242
243 /* PUBLIC: ProtocolHandlerCache_Initialize
244 * Initializes the cache. Cache needs to be deinitialized when finished with.
245 *
246 * cache - Pointer to a ProtocolHandlerCache to be initialized
247 *
248 * Returns:
249 * ERROR_OUTOFMEMORY
250 * ERROR_SUCCESS
251 */
252 MI_EXTERN_C MI_Result ProtocolHandlerCache_Initialize(_In_opt_z_ const MI_Char *applicationId, _Out_ ProtocolHandlerCache *cache)
253 krisbash 1.1 {
254 memset(cache, 0, sizeof(ProtocolHandlerCache));
255
256 cache->applicationID = applicationId;
257
258 /* Initialize the cache lock */
259 if (CachedLock_Init(&cache->lock, CACHEDLOCK_FLAG_SHARED) != 0)
260 {
261 return MI_RESULT_SERVER_LIMITS_EXCEEDED;
262 }
263
264 return ProtocolHandlerCache_CreateAllProtocolEntries(cache);
265 }
266
267 /* PRIVATE: Unload a protocol handler. This will call MI_Application_Close on the handler
268 * if there is a function table, which is a blocking option... therefore
269 * it will try and cancel all active sessions and operations. The client
270 * will need to close any outstanding handles otherwise this will block forever.
271 * When closed it unloads the DLL, so if there are any active threads after the
272 * fact things will crash. It is the responsibility of the protocol handler
273 * to make sure this does not happen.
274 krisbash 1.1 */
275 void ProtocolHandlerCache_UnloadProtocolHandler(_Inout_ ProtocolHandlerCacheItem *itemPointer)
276 {
277 ptrdiff_t currentApiCount;
278
279 if (itemPointer->dllInitialized == MI_TRUE)
280 {
281 /*Wait for the API count to hit 0 */
282 currentApiCount = itemPointer->outstandingProtocolHandlerCalls;
283 while (currentApiCount != 0)
284 {
285 CondLock_Wait((ptrdiff_t)itemPointer, &(itemPointer)->outstandingProtocolHandlerCalls, currentApiCount, CONDLOCK_DEFAULT_SPINCOUNT);
286 currentApiCount = (itemPointer)->outstandingProtocolHandlerCalls;
287 }
288
289 if (itemPointer->application.ft)
290 {
291 MI_Application_Close(&itemPointer->application);
292 }
293 if (itemPointer->dllHandle != NULL)
294 {
295 krisbash 1.1 Shlib_Close(itemPointer->dllHandle);
296 }
297
298 itemPointer->dllInitialized = MI_FALSE;
299 }
300 }
301
302 /* PUBLIC: ProtocolHandlerCache_DeInitialize
303 * Shuts down the cache, unloading all cache entries in the process.
304 *
305 * cache - Pointer to a ProtocolHandlerCache to be deinitialized
306 *
307 * Returns:
308 * ERROR_SUCCESS
309 */
310 MI_EXTERN_C MI_Result ProtocolHandlerCache_DeInitialize(_Inout_ ProtocolHandlerCache *cache)
311 {
312 CachedLock_AcquireWrite(&cache->lock);
313
314 /* Free all transports */
315 while (cache->cacheList)
316 krisbash 1.1 {
317 ProtocolHandlerCacheItem *item = cache->cacheList;
318
319 /* unlink this item from list */
320 cache->cacheList = cache->cacheList->nextItem;
321
322 /* unload and free it */
323 ProtocolHandlerCache_UnloadProtocolHandler(item);
324 PAL_Free(item);
325 }
326
327 CachedLock_ReleaseWrite(&cache->lock);
328
329 /* Cleanup lock */
330 CachedLock_Destroy(&cache->lock);
331
332 return MI_RESULT_OK;
333 }
334
335 /* LOCK MUST ALREADY BE AQUIRED */
336 _Success_(return == MI_RESULT_OK)
337 krisbash 1.1 MI_Result ProtocolHandlerCache_FindProtocolHandler(_Inout_ ProtocolHandlerCache *cache, _In_z_ const MI_Char *name, _Outptr_ ProtocolHandlerCacheItem **cacheItem)
338 {
339 ProtocolHandlerCacheItem *currentItem = cache->cacheList;
340
341 while (currentItem)
342 {
343 if (Tcscmp(name, currentItem->name) == 0)
344 {
345 /* Found it! */
346 break;
347 }
348 currentItem = currentItem->nextItem;
349 }
350 if (currentItem)
351 {
352 *cacheItem = currentItem;
353 return MI_RESULT_OK;
354 }
355 else
356 {
357 /* Not there */
358 krisbash 1.1 return MI_RESULT_NOT_FOUND;
359 }
360 }
361
362
363 /* Assumption: Protocol handler has not already been loaded.
364 * This method will get the details of the protocol handler from config, load it
365 * and call the MI_Application_Initialize method on the handler.
366 */
367 _Success_(return == MI_RESULT_OK)
368 MI_Result ProtocolHandlerCache_LoadProtocolHandler(_Inout_ ProtocolHandlerCache *cache, _Inout_ ProtocolHandlerCacheItem *item)
369 {
370 MI_Result returnCode;
371 ProtocolHandlerInitializeV1 initializerFunction;
372
373 if (item->dllInitialized == MI_TRUE)
374 {
375 /* Already initialized */
376 return MI_RESULT_OK;
377 }
378
379 krisbash 1.1 if ((item->majorVersion != 1) && (item->minorVersion != 0))
380 {
381 /* Not a supported version */
382 return MI_RESULT_FAILED;
383 }
384
385 if (item->dllFunctionPointer)
386 {
387 initializerFunction = item->dllFunctionPointer;
388 }
389 else
390 {
391 TChar buf[PAL_MAX_PATH_SIZE];
392 if (TcsStrlcpy(buf, item->dllPath, PAL_MAX_PATH_SIZE) >= PAL_MAX_PATH_SIZE)
393 return MI_RESULT_FAILED;
394
395 item->dllHandle = Shlib_Open(buf);
396 if (item->dllHandle == NULL)
397 {
398 return MI_RESULT_FAILED;
399 }
400 krisbash 1.1
401 initializerFunction = (ProtocolHandlerInitializeV1)Shlib_Sym(item->dllHandle, item->dllEntryPoint);
402 if (initializerFunction == NULL)
403 {
404 Shlib_Close(item->dllHandle);
405 item->dllHandle = NULL;
406 return MI_RESULT_NOT_SUPPORTED;
407 }
408 }
409
410 /* Handle flags, and extendedError! */
411 returnCode = initializerFunction(0, cache->applicationID, NULL, &item->application);
412 if (returnCode != MI_RESULT_OK)
413 {
414 if (item->dllHandle)
415 {
416 Shlib_Close(item->dllHandle);
417 item->dllHandle = NULL;
418 }
419
420 if (item->dllEntryPoint)
421 krisbash 1.1 {
422 item->dllFunctionPointer = NULL;
423 }
424 return returnCode;
425 }
426
427 item->dllInitialized = MI_TRUE;
428 return MI_RESULT_OK;
429 }
430
431 /* Retrieves a protocol handler. If it has not been loaded and initialized it will do so.
432 * Fast case is when it has already been loaded and it grabs a fast read lock. If an update
433 * is being done this call will block until that is complete.
434 */
435 _Success_(return == MI_RESULT_OK)
436 MI_EXTERN_C MI_Result ProtocolHandlerCache_GetProtocolHandler(_Inout_ ProtocolHandlerCache *cache, _In_z_ const MI_Char *name, _Outptr_ ProtocolHandlerCacheItem **cacheItem)
437 {
438 MI_Result returnCode = MI_RESULT_OK;
439
440 *cacheItem = NULL;
441
442 krisbash 1.1 /* Read lock to determine if the object exists. This is the fast path. */
443 ReadWriteLock_AcquireRead(&cache->lock);
444
445 returnCode = ProtocolHandlerCache_FindProtocolHandler(cache, name, cacheItem);
446
447 ReadWriteLock_ReleaseRead(&cache->lock);
448
449 if (returnCode != MI_RESULT_OK)
450 {
451 return returnCode;
452 }
453
454 if ((*cacheItem)->dllInitialized == MI_FALSE)
455 {
456 ReadWriteLock_AcquireWrite(&cache->lock);
457 returnCode = ProtocolHandlerCache_LoadProtocolHandler(cache, *cacheItem);
458 ReadWriteLock_ReleaseWrite(&cache->lock);
459 }
460
461 return returnCode;
462 }
463 krisbash 1.1
464 MI_Result ProtocolHandlerCache_IncrementApiCount(_Inout_ ProtocolHandlerCacheItem *cacheItem)
465 {
466 Atomic_Inc(&cacheItem->outstandingProtocolHandlerCalls);
467 return MI_RESULT_OK;
468 }
469 MI_Result ProtocolHandlerCache_DecrementApiCount(_Inout_ ProtocolHandlerCacheItem *cacheItem)
470 {
471 if (Atomic_Dec(&cacheItem->outstandingProtocolHandlerCalls) == 0)
472 {
473 //Signal we are at 0 in case we are trying to shutdown this item
474 CondLock_Broadcast((ptrdiff_t)cacheItem);
475
476 }
477 return MI_RESULT_OK;
478 }
|