/*============================================================================ * Copyright (C) Microsoft Corporation, All rights reserved. *============================================================================ */ #include #include "ProtocolHandlerCache.h" #include "InteractionProtocolHandler.h" #include #include struct _staticProtocolHandlers { MI_Char *name; ProtocolHandlerInitializeV1 dllFunctionPointer; MI_Boolean defaultLocal; MI_Boolean defaultRemote; } g_staticallyLoadedProtocolHandlers[] = { { MI_T("OMI_SOCKETS"), InteractionProtocolHandler_Application_Initialize, MI_TRUE, MI_FALSE} }; _Success_(return == MI_RESULT_OK) MI_Result ProtocolHandlerCache_InsertProtocolEntries(_Inout_ ProtocolHandlerCache *cache, _In_z_ const char *protocolHandlerName, _In_z_ const char *protocolHandlerDLL, _In_z_ const char *protocolHandlerDllEntryPoint, MI_Uint32 protocolHandlerMajorVersion, MI_Uint32 protocolHandlerMinorVersion, _Outptr_ ProtocolHandlerCacheItem **cacheItem) { ProtocolHandlerCacheItem *item; item = PAL_Malloc(sizeof(ProtocolHandlerCacheItem)); if (item == NULL) { return MI_RESULT_SERVER_LIMITS_EXCEEDED; } memset(item, 0, sizeof(ProtocolHandlerCacheItem)); TcsStrlcpy(item->name, protocolHandlerName, (sizeof(item->name)/sizeof(item->name[0]))); Strlcpy(item->dllPath, protocolHandlerDLL, sizeof(item->dllPath)/sizeof(item->dllPath[0])); Strlcpy(item->dllEntryPoint, protocolHandlerDllEntryPoint, sizeof(item->dllEntryPoint)/sizeof(item->dllEntryPoint[0])); /* Plug this into the head of the list */ item->nextItem = cache->cacheList; cache->cacheList = item; *cacheItem = item; return MI_RESULT_OK; } _Success_(return == MI_RESULT_OK) MI_Result ProtocolHandlerCache_CreateAllProtocolEntries(_Inout_ ProtocolHandlerCache *cache) { char _path[PAL_MAX_PATH_SIZE]; const char *path = NULL; Conf *configSubSystem; MI_Result ret = MI_RESULT_OK; int staticHandlerLoop; MI_Char defaultremoteprotocolhandler[30] = { 0 }; MI_Char defaultlocalprotocolhandler[30] = { 0 }; ProtocolHandlerCacheItem *cacheItem = NULL; path = OMI_GetPath(ID_SYSCONFDIR); if (path == NULL) { return MI_RESULT_FAILED; } /* Only need to copy on Windows as the paths are dynamic */ Strlcpy(_path, path, sizeof(_path)/sizeof(_path[0])); Strlcat(_path, "/omicli.conf", PAL_MAX_PATH_SIZE); path = _path; configSubSystem = Conf_Open(path); if (!configSubSystem) { /* err(ZT("failed to open configuration file: %s"), scs(path)); */ return MI_RESULT_FAILED; } for (;;) { const char* key; const char* value; int r = Conf_Read(configSubSystem, &key, &value); if (r == -1) { /* err(ZT("%s: %s\n"), path, scs(Conf_Error(conf)));*/ ret = MI_RESULT_FAILED; break; } if (r == 1) break; if (strncmp(key, "protocolhandler", 15) == 0) { char *cursor; const char *protocolHandlerName; const char *protocolHandlerDLL; const char *protocolHandlerDllEntryPoint; MI_Uint32 protocolHandlerMajorVersion; MI_Uint32 protocolHandlerMinorVersion; /* We found a protocol handler, need to */ /* First protocol handler name */ cursor = Strchr(value, ','); if (cursor == NULL) { ret = MI_RESULT_FAILED; break; } *cursor = '\0'; protocolHandlerName = value; value = cursor+1; /* move past ',' */ /* Second DLL*/ cursor = Strchr(value, ','); if (cursor == NULL) { ret = MI_RESULT_FAILED; break; } protocolHandlerDLL = value; value = cursor+1; /* move past ',' */ /* Third DLL entry point */ cursor = Strchr(value, ','); if (cursor == NULL) { ret = MI_RESULT_FAILED; break; } protocolHandlerDllEntryPoint = value; value = cursor+1; /* move past ',' */ /* Forth is major version */ protocolHandlerMajorVersion = Strtoul(value, &cursor, 10); if (*cursor != ',') { ret = MI_RESULT_FAILED; break; } value = cursor+1; /* move past ',' */ /* Fifth is minor version */ protocolHandlerMinorVersion = Strtoul(value, &cursor, 10); if (*cursor != '\0') { ret = MI_RESULT_FAILED; break; } ret = ProtocolHandlerCache_InsertProtocolEntries(cache, protocolHandlerName, protocolHandlerDLL, protocolHandlerDllEntryPoint, protocolHandlerMajorVersion, protocolHandlerMinorVersion, &cacheItem); if (ret != MI_RESULT_OK) break; } else if (strncmp(key, "defaultlocalprotocolhandler", 27) == 0) { TcsStrlcpy(defaultlocalprotocolhandler, value, sizeof(defaultlocalprotocolhandler)/sizeof(defaultlocalprotocolhandler[0])); } else if (strncmp(key, "defaultremoteprotocolhandler", 28) == 0) { TcsStrlcpy(defaultremoteprotocolhandler, value, sizeof(defaultremoteprotocolhandler)/sizeof(defaultremoteprotocolhandler[0])); } } /* Close configuration file */ Conf_Close(configSubSystem); /* Fix up the config-based default handlers */ if (defaultlocalprotocolhandler[0] || defaultremoteprotocolhandler[0]) { cacheItem = cache->cacheList; while (cacheItem) { if (Tcscmp(defaultlocalprotocolhandler, cacheItem->name) == 0) { cache->defaultLocalItem = cacheItem; } else if (Tcscmp(defaultremoteprotocolhandler, cacheItem->name) == 0) { cache->defaultRemoteItem = cacheItem; } cacheItem = cacheItem->nextItem; } } if (ret == MI_RESULT_OK) { /* Handler staticly compiled in ones now */ for (staticHandlerLoop = 0; staticHandlerLoop != sizeof(g_staticallyLoadedProtocolHandlers)/sizeof(g_staticallyLoadedProtocolHandlers[0]); staticHandlerLoop++) { ProtocolHandlerCacheItem *item; if (g_staticallyLoadedProtocolHandlers[staticHandlerLoop].name == NULL) break; item = PAL_Malloc(sizeof(ProtocolHandlerCacheItem)); if (item == NULL) { ret = MI_RESULT_SERVER_LIMITS_EXCEEDED; break; } memset(item, 0, sizeof(ProtocolHandlerCacheItem)); Tcslcpy(item->name, g_staticallyLoadedProtocolHandlers[staticHandlerLoop].name, sizeof(item->name)/sizeof(item->name[0])); item->dllFunctionPointer = g_staticallyLoadedProtocolHandlers[staticHandlerLoop].dllFunctionPointer; /* Plug this into the head of the list */ item->nextItem = cache->cacheList; cache->cacheList = item; if (g_staticallyLoadedProtocolHandlers[staticHandlerLoop].defaultLocal && (cache->defaultLocalItem == NULL)) cache->defaultLocalItem = item; if (g_staticallyLoadedProtocolHandlers[staticHandlerLoop].defaultRemote && (cache->defaultRemoteItem == NULL)) cache->defaultRemoteItem = item; } } /* if there is an error, free all transports */ if (ret != MI_RESULT_OK) { while (cache->cacheList) { ProtocolHandlerCacheItem *item; item = cache->cacheList; /* unlink this item from list */ cache->cacheList = item->nextItem; PAL_Free(item); } } return ret; } /* PUBLIC: ProtocolHandlerCache_Initialize * Initializes the cache. Cache needs to be deinitialized when finished with. * * cache - Pointer to a ProtocolHandlerCache to be initialized * * Returns: * ERROR_OUTOFMEMORY * ERROR_SUCCESS */ MI_EXTERN_C MI_Result ProtocolHandlerCache_Initialize(_In_opt_z_ const MI_Char *applicationId, _Out_ ProtocolHandlerCache *cache) { memset(cache, 0, sizeof(ProtocolHandlerCache)); cache->applicationID = applicationId; /* Initialize the cache lock */ if (CachedLock_Init(&cache->lock, CACHEDLOCK_FLAG_SHARED) != 0) { return MI_RESULT_SERVER_LIMITS_EXCEEDED; } return ProtocolHandlerCache_CreateAllProtocolEntries(cache); } /* PRIVATE: Unload a protocol handler. This will call MI_Application_Close on the handler * if there is a function table, which is a blocking option... therefore * it will try and cancel all active sessions and operations. The client * will need to close any outstanding handles otherwise this will block forever. * When closed it unloads the DLL, so if there are any active threads after the * fact things will crash. It is the responsibility of the protocol handler * to make sure this does not happen. */ void ProtocolHandlerCache_UnloadProtocolHandler(_Inout_ ProtocolHandlerCacheItem *itemPointer) { ptrdiff_t currentApiCount; if (itemPointer->dllInitialized == MI_TRUE) { /*Wait for the API count to hit 0 */ currentApiCount = itemPointer->outstandingProtocolHandlerCalls; while (currentApiCount != 0) { CondLock_Wait((ptrdiff_t)itemPointer, &(itemPointer)->outstandingProtocolHandlerCalls, currentApiCount, CONDLOCK_DEFAULT_SPINCOUNT); currentApiCount = (itemPointer)->outstandingProtocolHandlerCalls; } if (itemPointer->application.ft) { MI_Application_Close(&itemPointer->application); } if (itemPointer->dllHandle != NULL) { Shlib_Close(itemPointer->dllHandle); } itemPointer->dllInitialized = MI_FALSE; } } /* PUBLIC: ProtocolHandlerCache_DeInitialize * Shuts down the cache, unloading all cache entries in the process. * * cache - Pointer to a ProtocolHandlerCache to be deinitialized * * Returns: * ERROR_SUCCESS */ MI_EXTERN_C MI_Result ProtocolHandlerCache_DeInitialize(_Inout_ ProtocolHandlerCache *cache) { CachedLock_AcquireWrite(&cache->lock); /* Free all transports */ while (cache->cacheList) { ProtocolHandlerCacheItem *item = cache->cacheList; /* unlink this item from list */ cache->cacheList = cache->cacheList->nextItem; /* unload and free it */ ProtocolHandlerCache_UnloadProtocolHandler(item); PAL_Free(item); } CachedLock_ReleaseWrite(&cache->lock); /* Cleanup lock */ CachedLock_Destroy(&cache->lock); return MI_RESULT_OK; } /* LOCK MUST ALREADY BE AQUIRED */ _Success_(return == MI_RESULT_OK) MI_Result ProtocolHandlerCache_FindProtocolHandler(_Inout_ ProtocolHandlerCache *cache, _In_z_ const MI_Char *name, _Outptr_ ProtocolHandlerCacheItem **cacheItem) { ProtocolHandlerCacheItem *currentItem = cache->cacheList; while (currentItem) { if (Tcscmp(name, currentItem->name) == 0) { /* Found it! */ break; } currentItem = currentItem->nextItem; } if (currentItem) { *cacheItem = currentItem; return MI_RESULT_OK; } else { /* Not there */ return MI_RESULT_NOT_FOUND; } } /* Assumption: Protocol handler has not already been loaded. * This method will get the details of the protocol handler from config, load it * and call the MI_Application_Initialize method on the handler. */ _Success_(return == MI_RESULT_OK) MI_Result ProtocolHandlerCache_LoadProtocolHandler(_Inout_ ProtocolHandlerCache *cache, _Inout_ ProtocolHandlerCacheItem *item) { MI_Result returnCode; ProtocolHandlerInitializeV1 initializerFunction; if (item->dllInitialized == MI_TRUE) { /* Already initialized */ return MI_RESULT_OK; } if ((item->majorVersion != 1) && (item->minorVersion != 0)) { /* Not a supported version */ return MI_RESULT_FAILED; } if (item->dllFunctionPointer) { initializerFunction = item->dllFunctionPointer; } else { TChar buf[PAL_MAX_PATH_SIZE]; if (TcsStrlcpy(buf, item->dllPath, PAL_MAX_PATH_SIZE) >= PAL_MAX_PATH_SIZE) return MI_RESULT_FAILED; item->dllHandle = Shlib_Open(buf); if (item->dllHandle == NULL) { return MI_RESULT_FAILED; } initializerFunction = (ProtocolHandlerInitializeV1)Shlib_Sym(item->dllHandle, item->dllEntryPoint); if (initializerFunction == NULL) { Shlib_Close(item->dllHandle); item->dllHandle = NULL; return MI_RESULT_NOT_SUPPORTED; } } /* Handle flags, and extendedError! */ returnCode = initializerFunction(0, cache->applicationID, NULL, &item->application); if (returnCode != MI_RESULT_OK) { if (item->dllHandle) { Shlib_Close(item->dllHandle); item->dllHandle = NULL; } if (item->dllEntryPoint) { item->dllFunctionPointer = NULL; } return returnCode; } item->dllInitialized = MI_TRUE; return MI_RESULT_OK; } /* Retrieves a protocol handler. If it has not been loaded and initialized it will do so. * Fast case is when it has already been loaded and it grabs a fast read lock. If an update * is being done this call will block until that is complete. */ _Success_(return == MI_RESULT_OK) MI_EXTERN_C MI_Result ProtocolHandlerCache_GetProtocolHandler(_Inout_ ProtocolHandlerCache *cache, _In_z_ const MI_Char *name, _Outptr_ ProtocolHandlerCacheItem **cacheItem) { MI_Result returnCode = MI_RESULT_OK; *cacheItem = NULL; /* Read lock to determine if the object exists. This is the fast path. */ ReadWriteLock_AcquireRead(&cache->lock); returnCode = ProtocolHandlerCache_FindProtocolHandler(cache, name, cacheItem); ReadWriteLock_ReleaseRead(&cache->lock); if (returnCode != MI_RESULT_OK) { return returnCode; } if ((*cacheItem)->dllInitialized == MI_FALSE) { ReadWriteLock_AcquireWrite(&cache->lock); returnCode = ProtocolHandlerCache_LoadProtocolHandler(cache, *cacheItem); ReadWriteLock_ReleaseWrite(&cache->lock); } return returnCode; } MI_Result ProtocolHandlerCache_IncrementApiCount(_Inout_ ProtocolHandlerCacheItem *cacheItem) { Atomic_Inc(&cacheItem->outstandingProtocolHandlerCalls); return MI_RESULT_OK; } MI_Result ProtocolHandlerCache_DecrementApiCount(_Inout_ ProtocolHandlerCacheItem *cacheItem) { if (Atomic_Dec(&cacheItem->outstandingProtocolHandlerCalls) == 0) { //Signal we are at 0 in case we are trying to shutdown this item CondLock_Broadcast((ptrdiff_t)cacheItem); } return MI_RESULT_OK; }