diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..ec9b47b9 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,8 @@ +root = true + +[*] +insert_final_newline = true # A newline ending in every file +indent_style = space # We use spaces. Period. +indent_size = 4 # And 4 of them. +charset = utf-8 # Save files as utf-8 +trim_trailing_whitespace = true # no trailing whitespace in lines diff --git a/bindings/CMakeLists.txt b/bindings/CMakeLists.txt index b0a3da15..0a4dee67 100644 --- a/bindings/CMakeLists.txt +++ b/bindings/CMakeLists.txt @@ -10,7 +10,7 @@ endif() if(${enable_java_binding}) add_subdirectory(java) endif() - + if(${enable_nodejs_binding}) add_subdirectory(nodejs) -endif() \ No newline at end of file +endif() diff --git a/bindings/nodejs/inc/nodejs.h b/bindings/nodejs/inc/nodejs.h index c63af9f8..0d43be66 100644 --- a/bindings/nodejs/inc/nodejs.h +++ b/bindings/nodejs/inc/nodejs.h @@ -5,6 +5,7 @@ #define NODEJS_H #include "module.h" +#include "azure_c_shared_utility/strings.h" #ifdef __cplusplus extern "C" @@ -13,8 +14,8 @@ extern "C" typedef struct NODEJS_MODULE_CONFIG_TAG { - const char* main_path; - const char* configuration_json; + STRING_HANDLE main_path; + STRING_HANDLE configuration_json; }NODEJS_MODULE_CONFIG; MODULE_EXPORT const MODULE_API* MODULE_STATIC_GETAPI(NODEJS_MODULE)(const MODULE_API_VERSION gateway_api_version); diff --git a/bindings/nodejs/src/nodejs.cpp b/bindings/nodejs/src/nodejs.cpp index 082d7c88..7324642d 100644 --- a/bindings/nodejs/src/nodejs.cpp +++ b/bindings/nodejs/src/nodejs.cpp @@ -31,6 +31,7 @@ #include "azure_c_shared_utility/xlogging.h" #include "azure_c_shared_utility/threadapi.h" #include "azure_c_shared_utility/agenttime.h" +#include "azure_c_shared_utility/strings.h" #include "parson.h" #include "module.h" @@ -97,8 +98,8 @@ static MODULE_HANDLE NODEJS_Create(BROKER_HANDLE broker, const void* configurati NODEJS_MODULE_HANDLE_DATA handle_data_input { broker, - module_config->main_path, - module_config->configuration_json, + STRING_c_str(module_config->main_path), + STRING_c_str(module_config->configuration_json), on_module_start }; @@ -129,80 +130,21 @@ static MODULE_HANDLE NODEJS_Create(BROKER_HANDLE broker, const void* configurati return result; } -static MODULE_HANDLE NODEJS_CreateFromJson(BROKER_HANDLE broker, const char* configuration) +static void* NODEJS_ParseConfigurationFromJson(const char* configuration) { - MODULE_HANDLE result; + return (void*)STRING_construct(configuration); +} - /*Codes_SRS_NODEJS_05_001: [ NODEJS_CreateFromJson shall return NULL if broker is NULL. ]*/ - /*Codes_SRS_NODEJS_05_002: [ NODEJS_CreateFromJson shall return NULL if configuration is NULL. ]*/ - if (broker == NULL || configuration == NULL) +static void NODEJS_FreeConfiguration(void* configuration) +{ + if (configuration == NULL) { - LogError("NULL parameter detected broker=%p configuration=%p", broker, configuration); - result = NULL; + LogError("configuration is NULL"); } else { - /*Codes_SRS_NODEJS_05_012: [ NODEJS_CreateFromJson shall parse configuration as a JSON string. ]*/ - JSON_Value* json = json_parse_string((const char*)configuration); - if (json == NULL) - { - /*Codes_SRS_NODEJS_05_003: [ NODEJS_CreateFromJson shall return NULL if configuration is not a valid JSON string. ]*/ - LogError("unable to json_parse_string"); - result = NULL; - } - else - { - JSON_Object* obj = json_value_get_object(json); - if (obj == NULL) - { - /*Codes_SRS_NODEJS_05_014: [ NODEJS_CreateFromJson shall return NULL if the configuration JSON does not start with an object at the root. ]*/ - LogError("unable to json_value_get_object"); - result = NULL; - } - else - { - /*Codes_SRS_NODEJS_05_013: [ NODEJS_CreateFromJson shall extract the value of the main_path property from the configuration JSON. ]*/ - const char* main_path = json_object_get_string(obj, "main_path"); - if (main_path == NULL) - { - /*Codes_SRS_NODEJS_05_004: [ NODEJS_CreateFromJson shall return NULL if the configuration JSON does not contain a string property called main_path. ]*/ - LogError("json_object_get_string failed"); - result = NULL; - } - else - { - /*Codes_SRS_NODEJS_05_006: [ NODEJS_CreateFromJson shall extract the value of the args property from the configuration JSON. ]*/ - JSON_Value* args = json_object_get_value(obj, "args"); // args is allowed to be NULL - char* args_str = json_serialize_to_string(args); - - NODEJS_MODULE_CONFIG config = - { - main_path, args_str - }; - - /*Codes_SRS_NODEJS_05_005: [ NODEJS_CreateFromJson shall populate a NODEJS_MODULE_CONFIG object with the values of the main_path and args properties and invoke NODEJS_Create passing the broker handle and the config object. ]*/ - result = NODEJS_Create(broker, (const void*)&config); - if (result == NULL) - { - /*Codes_SRS_NODEJS_05_008: [ If NODEJS_Create fail then the value NULL shall be returned. ]*/ - LogError("Unable to create Node JS module"); - // return 'result' as-is - } - else - { - /*Codes_SRS_NODEJS_05_007: [ If NODEJS_Create succeeds then a valid MODULE_HANDLE shall be returned. ]*/ - // return 'result' as-is - } - - free(args_str); - } - } - - json_value_free(json); - } + STRING_delete((STRING_HANDLE)configuration); } - - return result; } static bool validate_input(BROKER_HANDLE broker, const NODEJS_MODULE_CONFIG* module_config) @@ -223,7 +165,7 @@ static bool validate_input(BROKER_HANDLE broker, const NODEJS_MODULE_CONFIG* mod else if ( module_config->configuration_json != NULL && - (json = json_parse_string(module_config->configuration_json)) == NULL + (json = json_parse_string(STRING_c_str(module_config->configuration_json))) == NULL ) { LogError("Unable to parse configuration JSON"); @@ -231,13 +173,13 @@ static bool validate_input(BROKER_HANDLE broker, const NODEJS_MODULE_CONFIG* mod } else if( #ifdef WIN32 - _access(module_config->main_path, 4) != 0 + _access(STRING_c_str(module_config->main_path), 4) != 0 #else - access(module_config->main_path, 4) != 0 + access(STRING_c_str(module_config->main_path), 4) != 0 #endif ) { - LogError("Unable to access the JavaScript file at path '%s'", module_config->main_path); + LogError("Unable to access the JavaScript file at path '%s'", STRING_c_str(module_config->main_path)); result = false; } else @@ -1359,14 +1301,14 @@ static const MODULE_API_1 NODEJS_APIS_all = { {MODULE_API_VERSION_1}, - NODEJS_CreateFromJson, + NODEJS_ParseConfigurationFromJson, + NODEJS_FreeConfiguration, NODEJS_Create, NODEJS_Destroy, NODEJS_Receive, NODEJS_Start }; - #ifdef BUILD_MODULE_TYPE_STATIC MODULE_EXPORT const MODULE_API* MODULE_STATIC_GETAPI(NODEJS_MODULE)(const MODULE_API_VERSION gateway_api_version) #else diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 2fe7efe7..244f3d26 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -177,4 +177,4 @@ install( "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}/${PROJECT_NAME}ConfigVersion.cmake" DESTINATION ${package_location} -) \ No newline at end of file +) diff --git a/core/devdoc/module_loaders.md b/core/devdoc/module_loaders.md index 73ce4518..8e3810c4 100644 --- a/core/devdoc/module_loaders.md +++ b/core/devdoc/module_loaders.md @@ -10,24 +10,24 @@ design goals of the Gateway SDK is the idea that the SDK will allow for flexibility with respect to how a module is packaged and distributed and loaded in the gateway. This document describes at a high level how this works. Gateway modules can be written using different technology stacks. At the time of writing -modules can be written using C, .NET, Node.js and Java. The responsibility of +modules can be written using C, .NET, Node.js or Java. The responsibility of bootstrapping the respective runtimes (in case of stacks that have a runtime -that is) and loading the module code lies with the module loader. +that is) and loading the module code lies with the *module loader*. What is a Module Loader? ------------------------ The primary duty of a module loader in the gateway is to locate and load a module - or in other words, to abstract away from the gateway the details of -locating and loading a module. A gateway module maybe native or managed (.NET, +locating and loading a module. A gateway module may be native or managed (.NET, Node.js or Java). If its a managed module then the loader is responsible for ensuring that the runtime needed to successfully load and run the module is loaded and initialized first. From the perspective of the gateway, a module loader is a piece of code that -knows how to load a module implementation and hand the gateway a table of -function pointers that contain the module implementation. The gateway doesn’t -really care how the module loader goes about acquiring the said pointers. +knows how to load a module instance and hand the gateway a table of function +pointers used to control it. The gateway doesn’t really care how the module +loader goes about acquiring the said pointers. Loader Configuration -------------------- @@ -38,22 +38,23 @@ A loader is defined by the following attributes: - **Name**: A string that can be used to reference this particular loader -- **Module Path**: Optional path to a language binding implementation for - non-native loaders +- **Binding Module Path**: Optional path to a language binding implementation + for non-native loaders - **Configuration**: Optional additional configuration parameters that may be - used to configure the runtime that is to be loaded + used to configure the runtime that is to be loaded. For example, one might + specify custom runtime options for the Java Virtual Machine here. When initializing a gateway from a JSON configuration file via the -`Gateway_CreateFromJson` API, the loader configuration can be specified via the -top level `loaders` array. For example: +`Gateway_CreateFromJson` function, the loader configuration can be specified via +the top level `loaders` array. For example: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ { "loaders": [ { "type": "java", - "name": "java_loader", + "name": "java", "jvm.options": { ... jvm options here ... } @@ -85,16 +86,16 @@ The Gateway SDK ships with a set of loaders that support dynamically loaded modules and language binding loaders for .NET, Node.js and Java. They can be referenced using the following loader names: -- `native_loader`: This implements loading of native modules - that is, plain - C modules. +- `native`: This implements loading of native modules - that is, plain C + modules. -- `java_loader`: This implements loading of modules implemented using the Java - programming language. +- `java`: This implements loading of modules implemented using the Java + programming language jand compiled to JAR files. -- `node_loader`: This implements loading of modules implemented using Node.js. +- `node`: This implements loading of modules implemented using Node.js. -- `dotnet_loader`: This implements loading of modules implemented using a .NET - language. +- `dotnet`: This implements loading of modules implemented using a .NET + language and compiled as a .NET assembly. It is legal to completely omit specifying the `loaders` array in which case the default loaders specified above will be made available using default options. @@ -106,7 +107,7 @@ Here’s an example of a module configuration that makes use of the Java loader: { "name": "printer", "loader": { - "name": "java_loader", + "name": "java", "class.name": "org.contoso.gateway.module.Printer", "class.path": "./printer.jar" } @@ -171,6 +172,8 @@ Briefly, here’s how the gateway bootstraps itself: 2. Module loaders for a given language binding may or may not be available depending on what *CMake* build options were used when the source was built. + For example if CMake was run with the `enable_dotnet_binding` variable set + to `false`/`off` then the corresponding loader becomes unavailable for use. 3. Every module loader implementation provides a global function that is responsible for returning a pointer to a `MODULE_LOADER` instance. The @@ -212,11 +215,11 @@ Briefly, here’s how the gateway bootstraps itself: loader then it has the effect of overriding the default loader. 6. When initializing custom loaders, if the caller has provided a value in - `MODULE_LOADER::binding_module_path` then we proceed to make use of it. If - there’s no `binding_module_path` provided then the gateway looks for a - DLL/SO with a well-known name by invoking the underlying operating system’s - default DLL/SO search algorithm. We also search in the folder where the - gateway binary was run from and from the `./bin` folder. If the module + `MODULE_LOADER::binding_module_path` then the gateway proceeds to make use + of it. If there’s no `binding_module_path` provided then the gateway looks + for a DLL/SO with a well-known name by invoking the underlying operating + system’s default DLL/SO search algorithm. We also search in the folder where + the gateway binary was run from and from the `./bin` folder. If the module cannot be located at the end of this search then it is an error. 7. In case of a *native* module loader, there is no binding module. In order to @@ -224,20 +227,13 @@ Briefly, here’s how the gateway bootstraps itself: *binding* module where the module API implementation simply delegates to the *real* module’s API. -8. The gateway calls the module loader’s `Load` callback to acquire a +8. The gateway calls the module loader’s `Load` function to acquire a `MODULE_LIBRARY_HANDLE`. Each module loader’s implementation of this function would do what’s necessary to also load the associated runtime (in case of language binding modules). 9. The language binding module loader implementations will use the dynamic - DLL/SO loading API to load the binding module DLLs/SOs. + loader to load the binding module DLLs/SOs. 10. The gateway then proceeds to acquire the module’s function pointer table and calls `Module_Create` or `Module_CreateFromJson` to instantiate the module. - In case of language binding modules this callback would be on the binding - module itself. - -  -- - -  diff --git a/core/inc/module.h b/core/inc/module.h index 235f5c8d..6dd854d7 100644 --- a/core/inc/module.h +++ b/core/inc/module.h @@ -49,18 +49,30 @@ extern "C" MODULE_HANDLE module_handle; }; - /** @brief Creates an instance of a module. + /** @brief Translates module configuration from a JSON string to a + * module specific data structure. * - * @details This function is optional. + * @details This function must be implemented by modules that support + * configuration options. * - * @param broker The #BROKER_HANDLE to which this module - * will publish messages. * @param configuration A JSON string which describes any needed * configuration to create this module. * - * @return A non-NULL #MODULE_HANDLE upon success, otherwise @c NULL. + * @return A void pointer containing a parsed representation of the + * module's configuration. */ - typedef MODULE_HANDLE(*pfModule_CreateFromJson)(BROKER_HANDLE broker, const char* configuration); + typedef void*(*pfModule_ParseConfigurationFromJson)(const char* configuration); + + /** @brief Frees the configuration object returned by the + * ParseConfigurationFromJson function. + * + * @details This function must be implemented by modules that support + * configuration options. + * + * @param configuration A void pointer containing a parsed representation + * of the module's configuration. + */ + typedef void(*pfModule_FreeConfiguration)(void* configuration); /** @brief Creates an instance of a module. * @@ -137,9 +149,13 @@ extern "C" /** @brief Always the first element on a Module's API*/ MODULE_API base; - /** @brief Function pointer to the #Module_CreateFromJson function - * (optional). */ - pfModule_CreateFromJson Module_CreateFromJson; + /** @brief Function pointer to the #Module_ParseConfigurationFromJson + * function. */ + pfModule_ParseConfigurationFromJson Module_ParseConfigurationFromJson; + + /** @brief Function pointer to the #Module_FreeConfiguration + * function. */ + pfModule_FreeConfiguration Module_FreeConfiguration; /** @brief Function pointer to the #Module_Create function. */ pfModule_Create Module_Create; diff --git a/core/inc/module_access.h b/core/inc/module_access.h index 7c520347..ee191d17 100644 --- a/core/inc/module_access.h +++ b/core/inc/module_access.h @@ -18,14 +18,21 @@ extern "C" { #endif -/** @brief Macro to get the Module_Create from a MODULES_API pointer */ -#define MODULE_CREATE_FROM_JSON(module_api_ptr) ( ((const MODULE_API_1*)(module_api_ptr))->Module_CreateFromJson) +/** @brief Macro to get the Module_ParseConfigurationFromJson function from a MODULES_API pointer */ +#define MODULE_PARSE_CONFIGURATION_FROM_JSON(module_api_ptr) (((const MODULE_API_1*)(module_api_ptr))->Module_ParseConfigurationFromJson) + +/** @brief Macro to get the Module_FreeConfiguration function from a MODULES_API pointer */ +#define MODULE_FREE_CONFIGURATION(module_api_ptr) (((const MODULE_API_1*)(module_api_ptr))->Module_FreeConfiguration) + /** @brief Macro to get the Module_Create from a MODULES_API pointer */ #define MODULE_CREATE(module_api_ptr) (((const MODULE_API_1*)(module_api_ptr))->Module_Create) + /** @brief Macro to get the Module_Destroy from a MODULES_API pointer */ #define MODULE_DESTROY(module_api_ptr) (((const MODULE_API_1*)(module_api_ptr))->Module_Destroy) + /** @brief Macro to get the Module_Start from a MODULES_API pointer */ #define MODULE_START(module_api_ptr) (((const MODULE_API_1*)(module_api_ptr))->Module_Start) + /** @brief Macro to get the Module_Receive from a MODULES_API pointer */ #define MODULE_RECEIVE(module_api_ptr) (((const MODULE_API_1*)(module_api_ptr))->Module_Receive) diff --git a/core/inc/module_loader.h b/core/inc/module_loader.h index 1cd9ae9b..e6478c65 100644 --- a/core/inc/module_loader.h +++ b/core/inc/module_loader.h @@ -15,7 +15,10 @@ #ifndef MODULE_LOADER_H #define MODULE_LOADER_H +#include "azure_c_shared_utility/macro_utils.h" + #include "module.h" +#include "parson.h" #ifdef __cplusplus extern "C" @@ -25,23 +28,175 @@ extern "C" /** @brief handle for a module library */ typedef void* MODULE_LIBRARY_HANDLE; -typedef MODULE_LIBRARY_HANDLE(*pfModuleLoader_Load)(const void * config); +struct MODULE_LOADER_TAG; + +/** + * Configuration that is common to all module loaders goes here. The expectation + * is that a given module loader will define its own configuration struct but + * always have an instance of this struct as the first field. + */ +typedef struct MODULE_LOADER_BASE_CONFIGURATION_TAG +{ + STRING_HANDLE binding_path; +} MODULE_LOADER_BASE_CONFIGURATION; + +typedef MODULE_LIBRARY_HANDLE(*pfModuleLoader_Load)(const struct MODULE_LOADER_TAG* loader, const void* entrypoint); typedef void(*pfModuleLoader_Unload)(MODULE_LIBRARY_HANDLE handle); typedef const MODULE_API*(*pfModuleLoader_GetApi)(MODULE_LIBRARY_HANDLE handle); +typedef void*(*pfModuleLoader_ParseEntrypointFromJson)(const JSON_Value* json); +typedef void(*pfModuleLoader_FreeEntrypoint)(void* entrypoint); + +typedef MODULE_LOADER_BASE_CONFIGURATION*(*pfModuleLoader_ParseConfigurationFromJson)(const JSON_Value* json); +typedef void(*pfModuleLoader_FreeConfiguration)(MODULE_LOADER_BASE_CONFIGURATION* configuration); + +typedef void*(*pfModuleLoader_BuildModuleConfiguration)(const struct MODULE_LOADER_TAG* loader, const void* entrypoint, const void* module_configuration); +typedef void(*pfModuleLoader_FreeModuleConfiguration)(const void* module_configuration); /** @brief Function table for loading modules into a gateway */ typedef struct MODULE_LOADER_API_TAG { /** @brief Load function, loads module for gateway, returns a valid handle - * on success */ + * on success */ pfModuleLoader_Load Load; - /** @brief Unload function, unloads the library from the gateway */ + + /** @brief Unload function, unloads the library from the gateway */ pfModuleLoader_Unload Unload; - /** @brief GetApi function, gets the MODULE_API for the loaded module */ + + /** @brief GetApi function, gets the MODULE_API for the loaded module */ pfModuleLoader_GetApi GetApi; + + pfModuleLoader_ParseEntrypointFromJson ParseEntrypointFromJson; + pfModuleLoader_FreeEntrypoint FreeEntrypoint; + + pfModuleLoader_ParseConfigurationFromJson ParseConfigurationFromJson; + pfModuleLoader_FreeConfiguration FreeConfiguration; + + pfModuleLoader_BuildModuleConfiguration BuildModuleConfiguration; + pfModuleLoader_FreeModuleConfiguration FreeModuleConfiguration; } MODULE_LOADER_API; +/** + * Module loader type enumeration. + */ +typedef enum MODULE_LOADER_TYPE_TAG +{ + UNKNOWN, + NATIVE, + JAVA, + DOTNET, + NODEJS +} MODULE_LOADER_TYPE; + +/** + * The Module Loader. + */ +typedef struct MODULE_LOADER_TAG +{ + MODULE_LOADER_TYPE type; + const char* name; + MODULE_LOADER_BASE_CONFIGURATION* configuration; + MODULE_LOADER_API* api; +} MODULE_LOADER; + +#define MODULE_LOADER_RESULT_VALUES \ + MODULE_LOADER_SUCCESS, \ + MODULE_LOADER_ERROR + +DEFINE_ENUM(MODULE_LOADER_RESULT, MODULE_LOADER_RESULT_VALUES); + +/** + * Utility function for parsing a JSON object that has a property called + * "binding.path" into a MODULE_LOADER_BASE_CONFIGURATION instance. + */ +MODULE_LOADER_RESULT ModuleLoader_ParseBaseConfigurationFromJson( + MODULE_LOADER_BASE_CONFIGURATION* configuration, + const JSON_Value* json +); + +/** + * Utility function for freeing a MODULE_LOADER_BASE_CONFIGURATION* instance + * from a previous call to ModuleLoader_ParseBaseConfigurationFromJson. + */ +void ModuleLoader_FreeBaseConfiguration(MODULE_LOADER_BASE_CONFIGURATION* configuration); + +/** + * This function creates the default set of module loaders that the gateway supports. + */ +MODULE_LOADER_RESULT ModuleLoader_Initialize(void); + +/** + * This function frees resources allocated for tracking module loaders. + */ +void ModuleLoader_Destroy(void); + +/** + * Adds a new module loader to the gateway's collection of module loaders. + */ +MODULE_LOADER_RESULT ModuleLoader_Add(const MODULE_LOADER* loader); + +/** + * Safely replaces the module loader configuration for the given loader. + */ +MODULE_LOADER_RESULT ModuleLoader_UpdateConfiguration( + MODULE_LOADER* loader, + MODULE_LOADER_BASE_CONFIGURATION* configuration +); + +/** + * Searches the module loader collection given the loader's name. + */ +MODULE_LOADER* ModuleLoader_FindByName(const char* name); + +/** + * Given a module loader type - returns the default loader. + */ +MODULE_LOADER* ModuleLoader_GetDefaultLoaderForType(MODULE_LOADER_TYPE type); + +/** + * Given a string representation of a module loader type, returns the corresponding + * enum value. + */ +MODULE_LOADER_TYPE ModuleLoader_ParseType(const char* type); + +/** + * Given a module name, determines if it is a default module loader or a custom one. + */ +bool ModuleLoader_IsDefaultLoader(const char* name); + +/** + * Updates the global loaders array from a JSON that looks like this: + * "loaders": [ + * { + * "type": "node", + * "name": "node", + * "configuration": { + * "binding.path": "./bin/libnode_binding.so" + * } + * }, + * { + * "type": "java", + * "name": "java", + * "configuration": { + * "jvm.options": { + * "memory": 1073741824 + * }, + * "gateway.class.path": "./bin/gateway.jar", + * "binding.path": "./bin/libjava_binding.so" + * } + * }, + * { + * "type": "dotnet", + * "name": "dotnet", + * "configuration": { + * "gateway.assembly.path": "./bin/gateway.dll", + * "binding.path": "./bin/libdotnet_binding.so" + * } + * } + * ] + */ +MODULE_LOADER_RESULT ModuleLoader_InitializeFromJson(const JSON_Value* loaders); + #ifdef __cplusplus } #endif diff --git a/core/inc/module_loaders/dotnet_loader.h b/core/inc/module_loaders/dotnet_loader.h new file mode 100644 index 00000000..e69de29b diff --git a/core/inc/dynamic_loader.h b/core/inc/module_loaders/dynamic_loader.h similarity index 63% rename from core/inc/dynamic_loader.h rename to core/inc/module_loaders/dynamic_loader.h index f1ebfcec..6b461b02 100644 --- a/core/inc/dynamic_loader.h +++ b/core/inc/module_loaders/dynamic_loader.h @@ -12,6 +12,8 @@ #ifndef DYNAMIC_LOADER_H #define DYNAMIC_LOADER_H +#include "azure_c_shared_utility/strings.h" + #include "module.h" #include "module_loader.h" @@ -20,15 +22,25 @@ extern "C" { #endif +#define DYNAMIC_LOADER_NAME "native" + +#if WIN32 +#define NATIVE_BINDING_MODULE_NAME "native_binding.dll" +#elif __linux__ +#define NATIVE_BINDING_MODULE_NAME "libnative_binding.so" +#else +#error Cannot build a default binding module name for your platform. +#endif + /** @brief Structure to load a dynamically linked module */ -typedef struct DYNAMIC_LOADER_CONFIG_TAG +typedef struct DYNAMIC_LOADER_ENTRYPOINT_TAG { /** @brief file name, path to shared library */ - const char * moduleLibraryFileName; -} DYNAMIC_LOADER_CONFIG; + STRING_HANDLE moduleLibraryFileName; +} DYNAMIC_LOADER_ENTRYPOINT; /** @brief The API for the dynamically linked module loader. */ -extern const MODULE_LOADER_API * DynamicLoader_GetApi(void); +extern const MODULE_LOADER* DynamicLoader_Get(void); #ifdef __cplusplus } diff --git a/core/inc/module_loaders/java_loader.h b/core/inc/module_loaders/java_loader.h new file mode 100644 index 00000000..e69de29b diff --git a/core/inc/module_loaders/node_loader.h b/core/inc/module_loaders/node_loader.h new file mode 100644 index 00000000..392e9134 --- /dev/null +++ b/core/inc/module_loaders/node_loader.h @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef NODE_LOADER_H +#define NODE_LOADER_H + +#include "azure_c_shared_utility/strings.h" + +#include "module.h" +#include "module_loader.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define NODE_LOADER_NAME "NODE" + +#if WIN32 +#define NODE_BINDING_MODULE_NAME "nodejs_binding.dll" +#elif __linux__ +#define NODE_BINDING_MODULE_NAME "libnodejs_binding.so" +#error Cannot build a default binding module name for your platform. +#endif + +typedef struct NODE_LOADER_ENTRYPOINT_TAG +{ + STRING_HANDLE mainPath; +} NODE_LOADER_ENTRYPOINT; + +extern const MODULE_LOADER* NodeLoader_Get(void); + +#ifdef __cplusplus +} +#endif + +#endif // NODE_LOADER_H diff --git a/core/src/dynamic_loader.c b/core/src/dynamic_loader.c deleted file mode 100644 index 28f22d83..00000000 --- a/core/src/dynamic_loader.c +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. -#include -#ifdef _CRTDBG_MAP_ALLOC -#include -#endif -#include "azure_c_shared_utility/gballoc.h" -#include - -#include "azure_c_shared_utility/xlogging.h" - -#include "module.h" -#include "module_access.h" -#include "module_loader.h" -#include "dynamic_loader.h" -#include "dynamic_library.h" - -typedef struct MODULE_LIBRARY_HANDLE_DATA_TAG -{ - void* library; - const MODULE_API* api; -}MODULE_LIBRARY_HANDLE_DATA; - - - -static MODULE_LIBRARY_HANDLE DynamicModuleLoader_Load(const void* loader_config) -{ - MODULE_LIBRARY_HANDLE_DATA* result; - - // configuration cannot be null/empty - if (loader_config == NULL) - { - /*Codes_SRS_MODULE_LOADER_17_001: [DynamicModuleLoader_Load shall validate the moduleLibraryFileName, if it is NULL, it will return NULL.]*/ - result = NULL; - LogError("loader_config is NULL"); - } - else - { - DYNAMIC_LOADER_CONFIG* dynamic_loader_config = (DYNAMIC_LOADER_CONFIG*)loader_config; - if (dynamic_loader_config->moduleLibraryFileName == NULL) - { - result = NULL; - LogError("moduleLibraryFileName is NULL"); - } - else - { - const char * moduleLibraryFileName = dynamic_loader_config->moduleLibraryFileName; - /* Codes_SRS_MODULE_LOADER_17_005: [DynamicModuleLoader_Load shall allocate memory for the structure MODULE_LIBRARY_HANDLE.] */ - result = (MODULE_LIBRARY_HANDLE_DATA*)malloc(sizeof(MODULE_LIBRARY_HANDLE_DATA)); - if (result == NULL) - { - /*Codes_SRS_MODULE_LOADER_17_014: [If memory allocation is not successful, the load shall fail, and it shall return NULL.]*/ - LogError("malloc(sizeof(MODULE_LIBRARY_HANDLE_DATA)) failed"); - } - else - { - /* load the DLL */ - /* Codes_SRS_MODULE_LOADER_17_002: [DynamicModuleLoader_Load shall load the library as a file, the filename given by the moduleLibraryFileName.]*/ - result->library = DynamicLibrary_LoadLibrary(moduleLibraryFileName); - if (result->library == NULL) - { - /* Codes_SRS_MODULE_LOADER_17_012: [If the attempt is not successful, the load shall fail, and it shall return NULL.]*/ - free(result); - result = NULL; - LogError("DynamicLibrary_LoadLibrary() failed to load [%s]", moduleLibraryFileName); - } - else - { - /* Codes_SRS_MODULE_LOADER_17_003: [DynamicModuleLoader_Load shall locate the function defined by MODULE_GETAPI=_NAME in the open library.] */ - pfModule_GetApi pfnGetAPI = (pfModule_GetApi)DynamicLibrary_FindSymbol(result->library, MODULE_GETAPI_NAME); - if (pfnGetAPI == NULL) - { - /* Codes_SRS_MODULE_LOADER_17_013: [If locating the function is not successful, the load shall fail, and it shall return NULL.]*/ - DynamicLibrary_UnloadLibrary(result->library); - free(result); - result = NULL; - LogError("DynamicLibrary_FindSymbol() returned NULL"); - } - else - { - /* Codes_SRS_MODULE_LOADER_17_004: [DynamicModuleLoader_Load shall call the function defined by MODULE_GETAPI_NAME in the open library.]*/ - result->api = pfnGetAPI(Module_ApiGatewayVersion); - - /* if any of the required functions is NULL then we have a misbehaving module */ - if (result->api == NULL || - result->api->version > Module_ApiGatewayVersion || - MODULE_CREATE(result->api) == NULL || - MODULE_DESTROY(result->api) == NULL || - MODULE_RECEIVE(result->api) == NULL) - { - /*Codes_SRS_MODULE_LOADER_26_001: [ If the get API call doesn't set required functions, the load shall fail and it shall return `NULL`. ]*/ - DynamicLibrary_UnloadLibrary(result->library); - free(result); - result = NULL; - LogError("pfnGetapi() returned NULL"); - } - } - } - } - } - } - /*Codes_SRS_MODULE_LOADER_17_006: [DynamicModuleLoader_Load shall return a non-NULL handle to a MODULE_LIBRARY_DATA_TAG upon success.]*/ - return result; -} - -static const MODULE_API* DynamicModuleLoader_GetModuleApi(MODULE_LIBRARY_HANDLE moduleLibraryHandle) -{ - const MODULE_API* result; - - if (moduleLibraryHandle == NULL) - { - /*Codes_SRS_MODULE_LOADER_17_007: [DynamicModuleLoader_GetModuleApi shall return NULL if the moduleLibraryHandle is NULL.]*/ - result = NULL; - LogError("DynamicModuleLoader_GetModuleApi() - moduleLibraryHandle is NULL"); - } - else - { - /*Codes_SRS_MODULE_LOADER_17_008: [DynamicModuleLoader_GetModuleApi shall return a valid pointer to MODULE_API on success.]*/ - MODULE_LIBRARY_HANDLE_DATA* loader_data = moduleLibraryHandle; - result = loader_data->api; - } - - return result; -} - - -/*Codes_SRS_MODULE_LOADER_17_009: [DynamicModuleLoader_Unload shall do nothing if the moduleLibraryHandle is NULL.]*/ -/*Codes_SRS_MODULE_LOADER_17_010: [DynamicModuleLoader_Unload shall attempt to unload the library.]*/ -/*Codes_SRS_MODULE_LOADER_17_011: [ModuleLoader_UnLoad shall deallocate memory for the structure MODULE_LIBRARY_HANDLE.]*/ - -static void DynamicModuleLoader_Unload(MODULE_LIBRARY_HANDLE moduleLibraryHandle) -{ - if (moduleLibraryHandle != NULL) - { - MODULE_LIBRARY_HANDLE_DATA* loader_data = moduleLibraryHandle; - DynamicLibrary_UnloadLibrary(loader_data->library); - free(loader_data); - } - else - { - LogError("DynamicModuleLoader_Unload() - moduleLibraryHandle is NULL"); - } -} - -/* Codes_SRS_MODULE_LOADER_17_015: [ DynamicLoader_GetApi shall set all the fields of the MODULE_LOADER_API structure. ]*/ -const MODULE_LOADER_API Dynamic_Module_Loader_API = -{ - .Load = DynamicModuleLoader_Load, - .Unload = DynamicModuleLoader_Unload, - .GetApi = DynamicModuleLoader_GetModuleApi -}; - -const MODULE_LOADER_API * DynamicLoader_GetApi(void) -{ - /* Codes_SRS_MODULE_LOADER_17_020: [ DynamicLoader_GetApi shall return a non-NULL pointer to a MODULER_LOADER structure. ] */ - return &Dynamic_Module_Loader_API; -} diff --git a/core/src/gateway.c b/core/src/gateway.c index f5c367f9..3508ea3f 100644 --- a/core/src/gateway.c +++ b/core/src/gateway.c @@ -153,7 +153,18 @@ void Gateway_AddEventCallback(GATEWAY_HANDLE gw, GATEWAY_EVENT event_type, GATEW GATEWAY_HANDLE Gateway_Create(const GATEWAY_PROPERTIES* properties) { - return gateway_create_internal(properties, false); + GATEWAY_HANDLE result; + if (ModuleLoader_Initialize() != MODULE_LOADER_SUCCESS) + { + LogError("Gateway_Create() - ModuleLoader_Initialize failed"); + result = NULL; + } + else + { + result = gateway_create_internal(properties, false); + } + + return result; } GATEWAY_START_RESULT Gateway_Start(GATEWAY_HANDLE gw) @@ -169,7 +180,7 @@ GATEWAY_START_RESULT Gateway_Start(GATEWAY_HANDLE gw) for (m = 0; m < module_count; m++) { MODULE_DATA** module_data = VECTOR_element(gateway_handle->modules, m); - pfModule_Start pfStart = MODULE_START((*module_data)->module_loader->GetApi((*module_data)->module_library_handle)); + pfModule_Start pfStart = MODULE_START((*module_data)->module_loader->api->GetApi((*module_data)->module_library_handle)); if (pfStart != NULL) { /*Codes_SRS_GATEWAY_17_010: [ This function shall call Module_Start for every module which defines the start function. ]*/ @@ -192,6 +203,7 @@ GATEWAY_START_RESULT Gateway_Start(GATEWAY_HANDLE gw) void Gateway_Destroy(GATEWAY_HANDLE gw) { gateway_destroy_internal(gw); + ModuleLoader_Destroy(); } MODULE_HANDLE Gateway_AddModule(GATEWAY_HANDLE gw, const GATEWAY_MODULES_ENTRY* entry) @@ -200,7 +212,7 @@ MODULE_HANDLE Gateway_AddModule(GATEWAY_HANDLE gw, const GATEWAY_MODULES_ENTRY* /*Codes_SRS_GATEWAY_14_011: [ If gw, entry, or GATEWAY_MODULES_ENTRY's loader_configuration or loader_api is NULL the function shall return NULL. ]*/ if (gw != NULL && entry != NULL) { - module = gateway_addmodule_internal(gw, entry->loader_configuration, entry->loader_api, entry->module_configuration, entry->module_name, false); + module = gateway_addmodule_internal(gw, entry, false); if (module == NULL) { @@ -229,7 +241,7 @@ extern void Gateway_StartModule(GATEWAY_HANDLE gw, MODULE_HANDLE module) MODULE_DATA** module_data = (MODULE_DATA**)VECTOR_find_if(gateway_handle->modules, module_data_find, module); if (module_data != NULL) { - pfModule_Start pfStart = MODULE_START((*module_data)->module_loader->GetApi((*module_data)->module_library_handle)); + pfModule_Start pfStart = MODULE_START((*module_data)->module_loader->api->GetApi((*module_data)->module_library_handle)); if (pfStart != NULL) { /*Codes_SRS_GATEWAY_17_008: [ When module is found, if the Module_Start function is defined for this module, the Module_Start function shall be called. ]*/ diff --git a/core/src/gateway_createfromjson.c b/core/src/gateway_createfromjson.c index 4cf64091..8d69934c 100644 --- a/core/src/gateway_createfromjson.c +++ b/core/src/gateway_createfromjson.c @@ -6,12 +6,14 @@ #include "azure_c_shared_utility/macro_utils.h" #include "gateway.h" #include "parson.h" -#include "dynamic_loader.h" #define MODULES_KEY "modules" -#define LOADER_KEY "loading args" -#define MODULE_NAME_KEY "module name" -#define MODULE_PATH_KEY "module path" +#define LOADERS_KEY "loaders" +#define LOADER_KEY "loader" +#define MODULE_NAME_KEY "name" +#define LOADER_NAME_KEY "name" +#define LOADER_ENTRYPOINT_KEY "entrypoint" +#define MODULE_PATH_KEY "module.path" #define ARG_KEY "args" #define LINKS_KEY "links" @@ -27,7 +29,7 @@ DEFINE_ENUM(PARSE_JSON_RESULT, PARSE_JSON_RESULT_VALUES); -GATEWAY_HANDLE gateway_create_internal(const GATEWAY_PROPERTIES* properties, const MODULE_LOADER_API * module_loader, bool use_json); +GATEWAY_HANDLE gateway_create_internal(const GATEWAY_PROPERTIES* properties, bool use_json); static PARSE_JSON_RESULT parse_json_internal(GATEWAY_PROPERTIES* out_properties, JSON_Value *root); static void destroy_properties_internal(GATEWAY_PROPERTIES* properties); void gateway_destroy_internal(GATEWAY_HANDLE gw); @@ -39,7 +41,7 @@ GATEWAY_HANDLE Gateway_CreateFromJson(const char* file_path) if (file_path != NULL) { JSON_Value *root_value; - + /*Codes_SRS_GATEWAY_JSON_14_002: [The function shall use parson to read the file and parse the JSON string to a parson JSON_Value structure.]*/ root_value = json_parse_file(file_path); if (root_value != NULL) @@ -55,7 +57,7 @@ GATEWAY_HANDLE Gateway_CreateFromJson(const char* file_path) { /*Codes_SRS_GATEWAY_JSON_14_007: [The function shall use the GATEWAY_PROPERTIES instance to create and return a GATEWAY_HANDLE using the lower level API.]*/ /*Codes_SRS_GATEWAY_JSON_17_004: [ The function shall set the module loader to the default dynamically linked library module loader. ]*/ - gw = gateway_create_internal(properties, DynamicLoader_GetApi(), true); + gw = gateway_create_internal(properties, true); if (gw == NULL) { @@ -118,14 +120,14 @@ static void destroy_properties_internal(GATEWAY_PROPERTIES* properties) for (size_t element_index = 0; element_index < vector_size; ++element_index) { GATEWAY_MODULES_ENTRY* element = (GATEWAY_MODULES_ENTRY*)VECTOR_element(properties->gateway_modules, element_index); + element->module_loader_info.loader->api->FreeEntrypoint(element->module_loader_info.entrypoint); json_free_serialized_string((char*)(element->module_configuration)); - free((void*)element->loader_configuration); } VECTOR_destroy(properties->gateway_modules); properties->gateway_modules = NULL; } - + if (properties->gateway_links != NULL) { VECTOR_destroy(properties->gateway_links); @@ -133,6 +135,51 @@ static void destroy_properties_internal(GATEWAY_PROPERTIES* properties) } } +static PARSE_JSON_RESULT parse_loader(JSON_Object* loader_json, GATEWAY_MODULE_LOADER_INFO* loader_info) +{ + PARSE_JSON_RESULT result; + + // get loader name + const char* loader_name = json_object_get_string(loader_json, LOADER_NAME_KEY); + if (loader_name == NULL || strlen(loader_name) == 0) + { + LogError("Loader JSON does not have a valid 'name' attribute."); + result = PARSE_JSON_MISSING_OR_MISCONFIGURED_CONFIG; + } + else + { + // locate the loader + const MODULE_LOADER* loader = ModuleLoader_FindByName(loader_name); + if (loader == NULL) + { + LogError("Loader JSON has a non-existent loader 'name' specified - %s.", loader_name); + result = PARSE_JSON_MISSING_OR_MISCONFIGURED_CONFIG; + } + else + { + loader_info->loader = loader; + + // get entrypoint + JSON_Value* entrypoint_json = json_object_get_value(loader_json, LOADER_ENTRYPOINT_KEY); + loader_info->entrypoint = entrypoint_json == NULL ? NULL : + loader->api->ParseEntrypointFromJson(entrypoint_json); + + // if entrypoint_json is not NULL then loader_info->entrypoint must not be NULL + if (entrypoint_json != NULL && loader_info->entrypoint == NULL) + { + LogError("An error occurred when parsing the entrypoint for loader - %s.", loader_name); + result = PARSE_JSON_MISSING_OR_MISCONFIGURED_CONFIG; + } + else + { + result = PARSE_JSON_SUCCESS; + } + } + } + + return result; +} + static PARSE_JSON_RESULT parse_json_internal(GATEWAY_PROPERTIES* out_properties, JSON_Value *root) { PARSE_JSON_RESULT result; @@ -140,143 +187,142 @@ static PARSE_JSON_RESULT parse_json_internal(GATEWAY_PROPERTIES* out_properties, JSON_Object *json_document = json_value_get_object(root); if (json_document != NULL) { - JSON_Array *modules_array = json_object_get_array(json_document, MODULES_KEY); - JSON_Array *links_array = json_object_get_array(json_document, LINKS_KEY); - - if (modules_array != NULL && links_array != NULL) + // initialize the module loader configuration + JSON_Value *loaders = json_object_get_value(json_document, LOADERS_KEY); + if (loaders != NULL && ModuleLoader_InitializeFromJson(loaders) == MODULE_LOADER_SUCCESS) { - out_properties->gateway_modules = VECTOR_create(sizeof(GATEWAY_MODULES_ENTRY)); - if (out_properties->gateway_modules != NULL) + JSON_Array *modules_array = json_object_get_array(json_document, MODULES_KEY); + JSON_Array *links_array = json_object_get_array(json_document, LINKS_KEY); + + if (modules_array != NULL && links_array != NULL) { - JSON_Object *module; - size_t module_count = json_array_get_count(modules_array); - for (size_t module_index = 0; module_index < module_count; ++module_index) + out_properties->gateway_modules = VECTOR_create(sizeof(GATEWAY_MODULES_ENTRY)); + if (out_properties->gateway_modules != NULL) { - DYNAMIC_LOADER_CONFIG * loader_config = (DYNAMIC_LOADER_CONFIG*)malloc(sizeof(DYNAMIC_LOADER_CONFIG)); - if (loader_config != NULL) + JSON_Object *module; + size_t module_count = json_array_get_count(modules_array); + for (size_t module_index = 0; module_index < module_count; ++module_index) { module = json_array_get_object(modules_array, module_index); - /*Codes_SRS_GATEWAY_JSON_17_005: [ The function shall parse the "loading args" for "module path" and fill a DYNAMIC_LOADER_CONFIG structure with the module path information. ]*/ - JSON_Object * loader_args = json_object_get_object(module, LOADER_KEY); - const char* module_name = json_object_get_string(module, MODULE_NAME_KEY); - const char* module_path; - if (loader_args != NULL) - { - module_path = json_object_get_string(loader_args, MODULE_PATH_KEY); - } - else - { - module_path = NULL; - } - if (module_name != NULL && module_path != NULL) - { - /*Codes_SRS_GATEWAY_JSON_14_005: [The function shall set the value of const void* module_properties in the GATEWAY_PROPERTIES instance to a char* representing the serialized args value for the particular module.]*/ - /*Codes_SRS_GATEWAY_JSON_17_003: [ The function shall set the value of const void * loader_configuration in the GATEWAY_PROPERTIES instance to a pointer of the module's DYNAMIC_LOADER_CONFIG structure. ]*/ - JSON_Value *args = json_object_get_value(module, ARG_KEY); - char* args_str = json_serialize_to_string(args); - loader_config->moduleLibraryFileName = module_path; - - GATEWAY_MODULES_ENTRY entry = { - module_name, - loader_config, - DynamicLoader_GetApi(), - args_str - }; - - /*Codes_SRS_GATEWAY_JSON_14_006: [The function shall return NULL if the JSON_Value contains incomplete information.]*/ - if (VECTOR_push_back(out_properties->gateway_modules, &entry, 1) == 0) - { - result = PARSE_JSON_SUCCESS; - } - else - { - free(loader_config); - json_free_serialized_string(args_str); - result = PARSE_JSON_VECTOR_FAILURE; - LogError("Failed to push data into properties vector."); - break; - } - } - /*Codes_SRS_GATEWAY_JSON_14_006: [The function shall return NULL if the JSON_Value contains incomplete information.]*/ - else + /*Codes_SRS_GATEWAY_JSON_17_005: [ The function shall parse the "loading args" for "module path" and fill a DYNAMIC_LOADER_ENTRYPOINT structure with the module path information. ]*/ + JSON_Object* loader_args = json_object_get_object(module, LOADER_KEY); + GATEWAY_MODULE_LOADER_INFO loader_info; + if (parse_loader(loader_args, &loader_info) != PARSE_JSON_SUCCESS) { - free(loader_config); result = PARSE_JSON_MISSING_OR_MISCONFIGURED_CONFIG; - LogError("\"module name\" or \"module path\" in input JSON configuration is missing or misconfigured."); + LogError("Failed to parse loader configuration."); break; } - } - else - { - result = PARSE_JSON_MISCONFIGURED_OR_OTHER; - LogError("Could not allocate structure for loading args"); - break; - } - } - - if (result == PARSE_JSON_SUCCESS) - { - /* Codes_SRS_GATEWAY_JSON_04_001: [ The function shall create a Vector to Store all links to this gateway. ] */ - out_properties->gateway_links = VECTOR_create(sizeof(GATEWAY_LINK_ENTRY)); - if (out_properties->gateway_links != NULL) - { - JSON_Object *route; - size_t links_count = json_array_get_count(links_array); - for (size_t links_index = 0; links_index < links_count; ++links_index) + else { - route = json_array_get_object(links_array, links_index); - const char* module_source = json_object_get_string(route, SOURCE_KEY); - const char* module_sink = json_object_get_string(route, SINK_KEY); - - if (module_source != NULL && module_sink != NULL) + const char* module_name = json_object_get_string(module, MODULE_NAME_KEY); + if (module_name != NULL) { - GATEWAY_LINK_ENTRY entry = { - module_source, - module_sink + /*Codes_SRS_GATEWAY_JSON_14_005: [The function shall set the value of const void* module_properties in the GATEWAY_PROPERTIES instance to a char* representing the serialized args value for the particular module.]*/ + /*Codes_SRS_GATEWAY_JSON_17_003: [ The function shall set the value of const void * loader_configuration in the GATEWAY_PROPERTIES instance to a pointer of the module's DYNAMIC_LOADER_ENTRYPOINT structure. ]*/ + JSON_Value *args = json_object_get_value(module, ARG_KEY); + char* args_str = json_serialize_to_string(args); + + GATEWAY_MODULES_ENTRY entry = { + module_name, + loader_info, + args_str }; - /* Codes_SRS_GATEWAY_JSON_04_002: [ The function shall add all modules source and sink to GATEWAY_PROPERTIES inside gateway_links. ] */ - if (VECTOR_push_back(out_properties->gateway_links, &entry, 1) == 0) + /*Codes_SRS_GATEWAY_JSON_14_006: [The function shall return NULL if the JSON_Value contains incomplete information.]*/ + if (VECTOR_push_back(out_properties->gateway_modules, &entry, 1) == 0) { result = PARSE_JSON_SUCCESS; } else { + loader_info.loader->api->FreeEntrypoint(loader_info.entrypoint); + json_free_serialized_string(args_str); result = PARSE_JSON_VECTOR_FAILURE; - LogError("Failed to push data into links vector."); + LogError("Failed to push data into properties vector."); break; } } /*Codes_SRS_GATEWAY_JSON_14_006: [The function shall return NULL if the JSON_Value contains incomplete information.]*/ else { + loader_info.loader->api->FreeEntrypoint(loader_info.entrypoint); result = PARSE_JSON_MISSING_OR_MISCONFIGURED_CONFIG; - LogError("\"source\" or \"sink\" in input JSON configuration is missing or misconfigured."); + LogError("\"module name\" or \"module path\" in input JSON configuration is missing or misconfigured."); break; } } } - /* Codes_SRS_GATEWAY_JSON_14_008: [ This function shall return NULL upon any memory allocation failure. ] */ - else + + if (result == PARSE_JSON_SUCCESS) { - result = PARSE_JSON_VECTOR_FAILURE; - LogError("Failed to create links vector. "); + /* Codes_SRS_GATEWAY_JSON_04_001: [ The function shall create a Vector to Store all links to this gateway. ] */ + out_properties->gateway_links = VECTOR_create(sizeof(GATEWAY_LINK_ENTRY)); + if (out_properties->gateway_links != NULL) + { + JSON_Object *route; + size_t links_count = json_array_get_count(links_array); + for (size_t links_index = 0; links_index < links_count; ++links_index) + { + route = json_array_get_object(links_array, links_index); + const char* module_source = json_object_get_string(route, SOURCE_KEY); + const char* module_sink = json_object_get_string(route, SINK_KEY); + + if (module_source != NULL && module_sink != NULL) + { + GATEWAY_LINK_ENTRY entry = { + module_source, + module_sink + }; + + /* Codes_SRS_GATEWAY_JSON_04_002: [ The function shall add all modules source and sink to GATEWAY_PROPERTIES inside gateway_links. ] */ + if (VECTOR_push_back(out_properties->gateway_links, &entry, 1) == 0) + { + result = PARSE_JSON_SUCCESS; + } + else + { + result = PARSE_JSON_VECTOR_FAILURE; + LogError("Failed to push data into links vector."); + break; + } + } + /*Codes_SRS_GATEWAY_JSON_14_006: [The function shall return NULL if the JSON_Value contains incomplete information.]*/ + else + { + result = PARSE_JSON_MISSING_OR_MISCONFIGURED_CONFIG; + LogError("\"source\" or \"sink\" in input JSON configuration is missing or misconfigured."); + break; + } + } + } + /* Codes_SRS_GATEWAY_JSON_14_008: [ This function shall return NULL upon any memory allocation failure. ] */ + else + { + result = PARSE_JSON_VECTOR_FAILURE; + LogError("Failed to create links vector. "); + } } } + /* Codes_SRS_GATEWAY_JSON_14_008: [ This function shall return NULL upon any memory allocation failure. ] */ + else + { + result = PARSE_JSON_VECTOR_FAILURE; + LogError("Failed to create properties vector. "); + } } - /* Codes_SRS_GATEWAY_JSON_14_008: [ This function shall return NULL upon any memory allocation failure. ] */ + /*Codes_SRS_GATEWAY_JSON_14_006: [The function shall return NULL if the JSON_Value contains incomplete information.]*/ else { - result = PARSE_JSON_VECTOR_FAILURE; - LogError("Failed to create properties vector. "); + result = PARSE_JSON_MISCONFIGURED_OR_OTHER; + LogError("JSON Configuration file is configured incorrectly or some other error occurred while parsing."); } } - /*Codes_SRS_GATEWAY_JSON_14_006: [The function shall return NULL if the JSON_Value contains incomplete information.]*/ else { result = PARSE_JSON_MISCONFIGURED_OR_OTHER; - LogError("JSON Configuration file is configured incorrectly or some other error occurred while parsing."); + LogError("An error occurred while parsing the loaders configuration for the gateway."); } } /*Codes_SRS_GATEWAY_JSON_14_006: [The function shall return NULL if the JSON_Value contains incomplete information.]*/ diff --git a/core/src/gateway_internal.c b/core/src/gateway_internal.c index 32ba0e04..a1bb6000 100644 --- a/core/src/gateway_internal.c +++ b/core/src/gateway_internal.c @@ -191,13 +191,13 @@ GATEWAY_HANDLE gateway_create_internal(const GATEWAY_PROPERTIES* properties, boo { //Add the first module, if successful add others GATEWAY_MODULES_ENTRY* entry = (GATEWAY_MODULES_ENTRY*)VECTOR_element(properties->gateway_modules, 0); - MODULE_HANDLE module = gateway_addmodule_internal(gateway, entry->loader_configuration, entry->loader_api, entry->module_configuration, entry->module_name, use_json); + MODULE_HANDLE module = gateway_addmodule_internal(gateway, entry, use_json); //Continue adding modules until all are added or one fails for (size_t properties_index = 1; properties_index < entries_count && module != NULL; ++properties_index) { entry = (GATEWAY_MODULES_ENTRY*)VECTOR_element(properties->gateway_modules, properties_index); - module = gateway_addmodule_internal(gateway, entry->loader_configuration, entry->loader_api, entry->module_configuration, entry->module_name, use_json); + module = gateway_addmodule_internal(gateway, entry, use_json); } /*Codes_SRS_GATEWAY_14_036: [ If any MODULE_HANDLE is unable to be created from a GATEWAY_MODULES_ENTRY the GATEWAY_HANDLE will be destroyed. ]*/ @@ -339,28 +339,39 @@ bool checkIfModuleExists(GATEWAY_HANDLE_DATA* gateway_handle, const char* module return module_data == NULL ? false : true; } -MODULE_HANDLE gateway_addmodule_internal(GATEWAY_HANDLE_DATA* gateway_handle, const void* loader_configuration, const MODULE_LOADER_API* loader_api, const void* module_configuration, const char* module_name, bool use_json) +MODULE_HANDLE gateway_addmodule_internal(GATEWAY_HANDLE_DATA* gateway_handle, const GATEWAY_MODULES_ENTRY* module_entry, bool use_json) { MODULE_HANDLE module_result; /*Codes_SRS_GATEWAY_14_011: [ If gw, entry, or GATEWAY_MODULES_ENTRY's loader_configuration or loader_api is NULL the function shall return NULL. ]*/ - if (gateway_handle == NULL || module_name == NULL || loader_api == NULL) + if ( + gateway_handle == NULL || + module_entry == NULL || + module_entry->module_name == NULL || + module_entry->module_loader_info.loader == NULL || + module_entry->module_loader_info.entrypoint == NULL + ) { module_result = NULL; - LogError("Failed to add module because either the GATEWAY_HANDLE is NULL, module_name is NULL or empty, or loader_api is NULL. gw = %p, module_name = '%s', api = %p.", gateway_handle, module_name, loader_api); + LogError( + "Failed to add module because a required input parameter is NULL. gw = %p, module_name = '%s', loader = %p, entrypoint = %p.", + gateway_handle, + module_entry != NULL ? module_entry->module_name : "NULL", + module_entry != NULL ? module_entry->module_loader_info.loader : "NULL", + module_entry != NULL ? module_entry->module_loader_info.entrypoint : "NULL" + ); } - else if (strcmp(module_name, GATEWAY_ALL) == 0) + else if (strcmp(module_entry->module_name, GATEWAY_ALL) == 0) { /*Codes_SRS_GATEWAY_17_001: [ This function shall not accept "*" as a module name. ]*/ module_result = NULL; - LogError("Failed to add module because the module_name is invalid [%s]", module_name); + LogError("Failed to add module because the module_name is invalid [%s]", module_entry->module_name); } - else + else { //First check if a module with a given name already exists. /*Codes_SRS_GATEWAY_04_004: [ If a module with the same module_name already exists, this function shall fail and the GATEWAY_HANDLE will be destroyed. ]*/ - bool moduleExist = checkIfModuleExists(gateway_handle, module_name); - + bool moduleExist = checkIfModuleExists(gateway_handle, module_entry->module_name); if (!moduleExist) { MODULE_DATA * new_module_data = (MODULE_DATA*)malloc(sizeof(MODULE_DATA)); @@ -374,7 +385,11 @@ MODULE_HANDLE gateway_addmodule_internal(GATEWAY_HANDLE_DATA* gateway_handle, co { /*Codes_SRS_GATEWAY_14_012: [The function shall load the module located at GATEWAY_MODULES_ENTRY's module_path into a MODULE_LIBRARY_HANDLE. ]*/ /*Codes_SRS_GATEWAY_17_015: [ The function shall use GATEWAY_PROPERTIES::loader_api->Load and each GATEWAY_PROPERTIES::loader_configuration to get each module's MODULE_LIBRARY_HANDLE. ]*/ - MODULE_LIBRARY_HANDLE module_library_handle = loader_api->Load(loader_configuration); + MODULE_LIBRARY_HANDLE module_library_handle = module_entry->module_loader_info.loader->api->Load( + module_entry->module_loader_info.loader, + module_entry->module_loader_info.entrypoint + ); + /*Codes_SRS_GATEWAY_14_031: [If unsuccessful, the function shall return NULL.]*/ if (module_library_handle == NULL) { @@ -386,18 +401,41 @@ MODULE_HANDLE gateway_addmodule_internal(GATEWAY_HANDLE_DATA* gateway_handle, co { //Should always be a safe call. /*Codes_SRS_GATEWAY_14_013: [The function shall get the const MODULE_API* from the MODULE_LIBRARY_HANDLE.]*/ - const MODULE_API* module_apis = loader_api->GetApi(module_library_handle); + const MODULE_API* module_apis = module_entry->module_loader_info.loader->api->GetApi(module_library_handle); + + // parse module args if needed + const void* module_configuration = module_entry->module_configuration; + const void* transformed_module_configuration = module_configuration; + if (use_json) + { + module_configuration = MODULE_PARSE_CONFIGURATION_FROM_JSON(module_apis)( + (const char *)(module_entry->module_configuration) + ); + + // request the loader to transform the module configuration to what the module expects + transformed_module_configuration = module_entry->module_loader_info.loader->api->BuildModuleConfiguration( + module_entry->module_loader_info.loader, + module_entry->module_loader_info.entrypoint, + module_configuration + ); + } /*Codes_SRS_GATEWAY_14_015: [The function shall use the MODULE_API to create a MODULE_HANDLE using the GATEWAY_MODULES_ENTRY's module_configuration. ]*/ - MODULE_HANDLE module_handle = use_json - ? MODULE_CREATE_FROM_JSON(module_apis)(gateway_handle->broker, module_configuration) - : MODULE_CREATE(module_apis)(gateway_handle->broker, module_configuration); + MODULE_HANDLE module_handle = MODULE_CREATE(module_apis)(gateway_handle->broker, transformed_module_configuration); + + // free the configurations + if (use_json) + { + MODULE_FREE_CONFIGURATION(module_apis)((void*)module_configuration); + module_entry->module_loader_info.loader->api->FreeModuleConfiguration(transformed_module_configuration); + } + /*Codes_SRS_GATEWAY_14_016: [If the module creation is unsuccessful, the function shall return NULL.]*/ if (module_handle == NULL) { free(new_module_data); module_result = NULL; - loader_api->Unload(module_library_handle); + module_entry->module_loader_info.loader->api->Unload(module_library_handle); LogError("Module_Create failed."); } else @@ -419,7 +457,7 @@ MODULE_HANDLE gateway_addmodule_internal(GATEWAY_HANDLE_DATA* gateway_handle, co { char* name_copied = NULL; /*Codes_SRS_GATEWAY_26_020: [ The function shall make a copy of the name of the module for internal use. ]*/ - mallocAndStrcpy_s(&name_copied, module_name); + mallocAndStrcpy_s(&name_copied, module_entry->module_name); if (name_copied == NULL) { free(new_module_data); @@ -432,7 +470,7 @@ MODULE_HANDLE gateway_addmodule_internal(GATEWAY_HANDLE_DATA* gateway_handle, co } else { - strcpy(name_copied, module_name); + strcpy(name_copied, module_entry->module_name); /*Codes_SRS_GATEWAY_14_039: [ The function shall increment the BROKER_HANDLE reference count if the MODULE_HANDLE was successfully added to the GATEWAY_HANDLE_DATA's broker. ]*/ Broker_IncRef(gateway_handle->broker); /*Codes_SRS_GATEWAY_14_029: [ The function shall create a new MODULE_DATA containing the MODULE_HANDLE, MODULE_LOADER_API and MODULE_LIBRARY_HANDLE if the module was successfully linked to the message broker. ]*/ @@ -440,7 +478,7 @@ MODULE_HANDLE gateway_addmodule_internal(GATEWAY_HANDLE_DATA* gateway_handle, co { name_copied, module_library_handle, - loader_api, + module_entry->module_loader_info.loader, module_handle }; *new_module_data = module_data; @@ -487,7 +525,7 @@ MODULE_HANDLE gateway_addmodule_internal(GATEWAY_HANDLE_DATA* gateway_handle, co if (module_result == NULL) { MODULE_DESTROY(module_apis)(module_handle); - loader_api->Unload(module_library_handle); + module_entry->module_loader_info.loader->api->Unload(module_library_handle); } } } @@ -496,10 +534,10 @@ MODULE_HANDLE gateway_addmodule_internal(GATEWAY_HANDLE_DATA* gateway_handle, co else { module_result = NULL; - LogError("Error to add module. Duplicated module name: %s", module_name); + LogError("Error to add module. Duplicated module name: %s", module_entry->module_name); } } - + return module_result; } @@ -525,10 +563,13 @@ void gateway_removemodule_internal(GATEWAY_HANDLE_DATA* gateway_handle, MODULE_D } /*Codes_SRS_GATEWAY_14_038: [ The function shall decrement the BROKER_HANDLE reference count. ]*/ Broker_DecRef(gateway_handle->broker); + /*Codes_SRS_GATEWAY_14_024: [ The function shall use the MODULE_DATA's module_library_handle to retrieve the MODULE_API and destroy module. ]*/ - MODULE_DESTROY((*module_data_pptr)->module_loader->GetApi((*module_data_pptr)->module_library_handle))((*module_data_pptr)->module); + MODULE_DESTROY((*module_data_pptr)->module_loader->api->GetApi((*module_data_pptr)->module_library_handle))((*module_data_pptr)->module); + /*Codes_SRS_GATEWAY_14_025: [The function shall unload MODULE_DATA's module_library_handle. ]*/ - (*module_data_pptr)->module_loader->Unload((*module_data_pptr)->module_library_handle); + (*module_data_pptr)->module_loader->api->Unload((*module_data_pptr)->module_library_handle); + /*Codes_SRS_GATEWAY_14_026:[The function shall remove that MODULE_DATA from GATEWAY_HANDLE_DATA's modules. ]*/ MODULE_DATA * module_data_ptr = *module_data_pptr; VECTOR_erase(gateway_handle->modules, module_data_pptr, 1); diff --git a/core/src/gateway_internal.h b/core/src/gateway_internal.h index ac7f3f50..f817a85b 100644 --- a/core/src/gateway_internal.h +++ b/core/src/gateway_internal.h @@ -19,8 +19,8 @@ typedef struct MODULE_DATA_TAG { /** @brief The MODULE_LIBRARY_HANDLE associated with 'module' */ MODULE_LIBRARY_HANDLE module_library_handle; - /** @brief The API to load and unload this module. */ - const MODULE_LOADER_API * module_loader; + /** @brief The module loader associated with this module. */ + const MODULE_LOADER* module_loader; /** @brief The MODULE_HANDLE of the same module that lives on the message * broker. @@ -51,7 +51,7 @@ typedef struct LINK_DATA_TAG { GATEWAY_HANDLE gateway_create_internal(const GATEWAY_PROPERTIES* properties, bool use_json); void gateway_destroy_internal(GATEWAY_HANDLE gw); -MODULE_HANDLE gateway_addmodule_internal(GATEWAY_HANDLE_DATA* gateway_handle, const void* loader_configuration, const MODULE_LOADER_API* loader_api, const void* module_configuration, const char* module_name, bool use_json); +MODULE_HANDLE gateway_addmodule_internal(GATEWAY_HANDLE_DATA* gateway_handle, const GATEWAY_MODULES_ENTRY* entry, bool use_json); void gateway_removemodule_internal(GATEWAY_HANDLE_DATA* gateway_handle, MODULE_DATA** module); bool gateway_addlink_internal(GATEWAY_HANDLE_DATA* gateway_handle, const GATEWAY_LINK_ENTRY* link_entry); void gateway_removelink_internal(GATEWAY_HANDLE_DATA* gateway_handle, LINK_DATA* link_data); diff --git a/core/src/module_loader.c b/core/src/module_loader.c new file mode 100644 index 00000000..ef3cc644 --- /dev/null +++ b/core/src/module_loader.c @@ -0,0 +1,583 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include +#ifdef _CRTDBG_MAP_ALLOC +#include +#endif + +#include + +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/vector.h" +#include "azure_c_shared_utility/lock.h" +#include "parson.h" + +#include "azure_c_shared_utility/xlogging.h" + +#include "module.h" +#include "module_loader.h" +#include "module_loaders/dynamic_loader.h" + +#ifdef NODE_BINDING_ENABLED +#include "module_loaders/node_loader.h" +#endif +#ifdef JAVA_BINDING_ENABLED +#include "module_loaders/java_loader.h" +#endif +#ifdef DOTNET_BINDING_ENABLED +#include "module_loaders/dotnet_loader.h" +#endif + +MODULE_LOADER_RESULT add_module_loader(const MODULE_LOADER* loader); + +static struct +{ + // List of module loaders that we support. + VECTOR_HANDLE module_loaders; + + // Lock used to protect this instance. + LOCK_HANDLE lock; + +} g_module_loaders = { 0 }; + +MODULE_LOADER_RESULT ModuleLoader_Initialize(void) +{ + MODULE_LOADER_RESULT result; + + g_module_loaders.lock = Lock_Init(); + if (g_module_loaders.lock == NULL) + { + LogError("ModuleLoader_Initialize() - Lock_Init failed"); + result = MODULE_LOADER_ERROR; + } + else + { + if (Lock(g_module_loaders.lock) != LOCK_OK) + { + LogError("ModuleLoader_Initialize() - Lock failed"); + Lock_Deinit(g_module_loaders.lock); + g_module_loaders.lock = NULL; + result = MODULE_LOADER_ERROR; + } + else + { + g_module_loaders.module_loaders = VECTOR_create(sizeof(MODULE_LOADER*)); + if (g_module_loaders.module_loaders == NULL) + { + LogError("ModuleLoader_Initialize() - VECTOR_create failed"); + Unlock(g_module_loaders.lock); + Lock_Deinit(g_module_loaders.lock); + g_module_loaders.lock = NULL; + result = MODULE_LOADER_ERROR; + } + else + { + // add all supported module loaders + const MODULE_LOADER* supported_loaders[] = + { + DynamicLoader_Get() +#ifdef NODE_BINDING_ENABLED + , NodeLoader_Get() +#endif +#ifdef JAVA_BINDING_ENABLED + , JavaLoader_Get() +#endif +#ifdef DOTNET_BINDING_ENABLED + , DotnetLoader_Get() +#endif + }; + + size_t loaders_count = sizeof(supported_loaders) / sizeof(supported_loaders[0]); + size_t i; + for (i = 0; i < loaders_count; i++) + { + if (add_module_loader(NodeLoader_Get()) != MODULE_LOADER_SUCCESS) + { + LogError("ModuleLoader_Initialize() - Could not add loader"); + break; + } + } + + Unlock(g_module_loaders.lock); + + // adding loaders failed if we bailed early from the loop above + if (i < loaders_count) + { + ModuleLoader_Destroy(); + result = MODULE_LOADER_ERROR; + } + } + } + } + + return result; +} + +MODULE_LOADER_RESULT ModuleLoader_Add(const MODULE_LOADER* loader) +{ + MODULE_LOADER_RESULT result; + if (loader == NULL) + { + LogError("ModuleLoader_Add() - loader is NULL"); + result = MODULE_LOADER_ERROR; + } + else + { + if (g_module_loaders.lock == NULL || g_module_loaders.module_loaders == NULL) + { + LogError("ModuleLoader_Add() - ModuleLoader_Initialize has not been called yet"); + result = MODULE_LOADER_ERROR; + } + else + { + if (Lock(g_module_loaders.lock) != LOCK_OK) + { + LogError("ModuleLoader_Add() - Lock failed"); + result = MODULE_LOADER_ERROR; + } + else + { + if (add_module_loader(loader) != MODULE_LOADER_SUCCESS) + { + LogError("ModuleLoader_Add() - add_module_loader failed"); + result = MODULE_LOADER_ERROR; + } + else + { + result = MODULE_LOADER_SUCCESS; + } + + Unlock(g_module_loaders.lock); + } + } + } + + return result; +} + +MODULE_LOADER_RESULT ModuleLoader_UpdateConfiguration( + MODULE_LOADER* loader, + MODULE_LOADER_BASE_CONFIGURATION* configuration +) +{ + MODULE_LOADER_RESULT result; + if (loader == NULL) + { + LogError("ModuleLoader_UpdateConfiguration() - loader is NULL"); + result = MODULE_LOADER_ERROR; + } + else + { + if (g_module_loaders.lock == NULL || g_module_loaders.module_loaders == NULL) + { + LogError("ModuleLoader_UpdateConfiguration() - ModuleLoader_Initialize has not been called yet"); + result = MODULE_LOADER_ERROR; + } + else + { + if (Lock(g_module_loaders.lock) != LOCK_OK) + { + LogError("ModuleLoader_UpdateConfiguration() - Lock failed"); + result = MODULE_LOADER_ERROR; + } + else + { + loader->configuration = configuration; + Unlock(g_module_loaders.lock); + result = MODULE_LOADER_SUCCESS; + } + } + } + + return result; +} + +static bool find_module_loader_predicate(const void* element, const void* value) +{ + MODULE_LOADER* loader = (MODULE_LOADER*)element; + return strcmp(loader->name, (const char*)value) == 0; +} + +MODULE_LOADER* ModuleLoader_FindByName(const char* name) +{ + MODULE_LOADER* result; + if (name == NULL) + { + LogError("ModuleLoader_FindByName() - name is NULL"); + result = NULL; + } + else + { + if (g_module_loaders.lock == NULL || g_module_loaders.module_loaders == NULL) + { + LogError("ModuleLoader_FindByName() - ModuleLoader_Initialize has not been called yet"); + result = NULL; + } + else + { + if (Lock(g_module_loaders.lock) != LOCK_OK) + { + LogError("ModuleLoader_FindByName() - Lock failed"); + result = NULL; + } + else + { + result = VECTOR_find_if( + g_module_loaders.module_loaders, + find_module_loader_predicate, + name + ); + + Unlock(g_module_loaders.lock); + } + } + } + + return result; +} + +void ModuleLoader_Destroy(void) +{ + if (g_module_loaders.lock != NULL) + { + Lock_Deinit(g_module_loaders.lock); + g_module_loaders.lock = NULL; + } + if(g_module_loaders.module_loaders != NULL) + { + // free all module loader resources + size_t length = VECTOR_size(g_module_loaders.module_loaders); + for (size_t i = 0; i < length; i++) + { + MODULE_LOADER* loader = *((MODULE_LOADER **)VECTOR_element(g_module_loaders.module_loaders, i)); + + // NOTE: We free the configuration object even for default loaders because + // the configuration may have been replaced by the gateway even for default + // loaders. + loader->api->FreeConfiguration(loader->configuration); + + // if this is not a default loader then free resources allocated in + // add_loader_from_json + if (ModuleLoader_IsDefaultLoader(loader->name) == false) + { + free((void *)loader->name); + free(loader); + } + } + + VECTOR_destroy(g_module_loaders.module_loaders); + g_module_loaders.module_loaders = NULL; + } +} + +static MODULE_LOADER_RESULT add_module_loader(const MODULE_LOADER* loader) +{ + MODULE_LOADER_RESULT result; + + if (VECTOR_push_back(g_module_loaders.module_loaders, &loader, 1) == 0) + { + result = MODULE_LOADER_SUCCESS; + } + else + { + LogError("add_module_loader() - VECTOR_push_back failed"); + result = MODULE_LOADER_ERROR; + } + + return result; +} + +MODULE_LOADER_RESULT ModuleLoader_ParseBaseConfigurationFromJson( + MODULE_LOADER_BASE_CONFIGURATION* configuration, + const JSON_Value* json +) +{ + // The JSON is expected to be an object that has a string + // property called "binding.path" + MODULE_LOADER_RESULT result; + if ( + configuration == NULL || + json == NULL || + json_value_get_type(json) != JSONObject + ) + { + LogError( + "ModuleLoader_ParseBaseConfigurationFromJson() - Invalid input arguments. " + "configuration = %p, json = %p, json value type = %d", + configuration, + json, + (json != NULL) ? json_value_get_type(json) : 0 + ); + result = MODULE_LOADER_ERROR; + } + else + { + JSON_Object* config = json_value_get_object(json); + if (config == NULL) + { + LogError("ModuleLoader_ParseBaseConfigurationFromJson() - json_value_get_object failed"); + result = MODULE_LOADER_ERROR; + } + else + { + // It is acceptable to have binding.path be NULL. + const char* binding_path = json_object_get_string(config, "binding.path"); + configuration->binding_path = STRING_construct(binding_path); + + result = MODULE_LOADER_SUCCESS; + } + } + + return result; +} + +void ModuleLoader_FreeBaseConfiguration(MODULE_LOADER_BASE_CONFIGURATION* configuration) +{ + if(configuration != NULL) + { + STRING_delete(configuration->binding_path); + } + else + { + LogError("ModuleLoader_FreeBaseConfiguration() - configuration is NULL"); + } +} + +MODULE_LOADER* ModuleLoader_GetDefaultLoaderForType(MODULE_LOADER_TYPE type) +{ + MODULE_LOADER* result; + + switch (type) + { + case NATIVE: + result = ModuleLoader_FindByName(DYNAMIC_LOADER_NAME); + break; + +#ifdef NODE_BINDING_ENABLED + case NODEJS: + result = ModuleLoader_FindByName(NODE_LOADER_NAME); + break; +#endif + +#ifdef JAVA_BINDING_ENABLED + case JAVA: + result = ModuleLoader_FindByName(JAVA_LOADER_NAME); + break; +#endif + +#ifdef DOTNET_BINDING_ENABLED + case DOTNET: + result = ModuleLoader_FindByName(DOTNET_LOADER_NAME); + break; +#endif + + default: + result = NULL; + break; + } + + return result; +} + +MODULE_LOADER_TYPE ModuleLoader_ParseType(const char* type) +{ + MODULE_LOADER_TYPE loader_type; + if (strcmp(type, "native") == 0) + loader_type = NATIVE; + else if (strcmp(type, "node") == 0) + loader_type = NODEJS; + else if (strcmp(type, "java") == 0) + loader_type = JAVA; + else if (strcmp(type, "dotnet") == 0) + loader_type = DOTNET; + else + loader_type = UNKNOWN; + + return loader_type; +} + +bool ModuleLoader_IsDefaultLoader(const char* name) +{ + return strcmp(name, "native") == 0 + || + strcmp(name, "node") == 0 + || + strcmp(name, "java") == 0 + || + strcmp(name, "dotnet") == 0; +} + +static MODULE_LOADER_RESULT add_loader_from_json(const JSON_Value* loader, size_t index) +{ + MODULE_LOADER_RESULT result; + + if (json_value_get_type(loader) != JSONObject) + { + LogError("add_loader_from_json() - loader %zu is not a JSON object", index); + result = MODULE_LOADER_ERROR; + } + else + { + JSON_Object* loader_object = json_value_get_object(loader); + if (loader_object == NULL) + { + LogError("add_loader_from_json() - json_value_get_object failed for loader %zu", index); + result = MODULE_LOADER_ERROR; + } + else + { + const char* type = json_object_get_string(loader_object, "type"); + const char* loader_name = json_object_get_string(loader_object, "name"); + if ((type == NULL || loader_name == NULL) || + (strlen(type) == 0 || strlen(loader_name) == 0)) + { + LogError( + "add_loader_from_json() - invalid type and/or name for loader %zu. " + "type = %s, name = %s", + index, type, loader_name + ); + result = MODULE_LOADER_ERROR; + } + else + { + MODULE_LOADER_TYPE loader_type = ModuleLoader_ParseType(type); + if (loader_type == UNKNOWN) + { + LogError("add_loader_from_json() - loader type %s for loader %zu is invalid", type, index); + result = MODULE_LOADER_ERROR; + } + else + { + // get the default loader for the given type + MODULE_LOADER* default_loader = ModuleLoader_GetDefaultLoaderForType(loader_type); + if (default_loader == NULL) + { + LogError( + "add_loader_from_json() - no default module loader for type %s was " + "found for loader entry %zu", + type, index + ); + result = MODULE_LOADER_ERROR; + } + else + { + JSON_Value* configuration = json_object_get_value(loader_object, "configuration"); + MODULE_LOADER_BASE_CONFIGURATION* loader_configuration; + if (configuration != NULL) + { + loader_configuration = default_loader->api->ParseConfigurationFromJson(configuration); + } + else + { + loader_configuration = NULL; + } + + // now we have all the information we need to construct/replace a MODULE_LOADER + bool is_default_loader = ModuleLoader_IsDefaultLoader(loader_name); + MODULE_LOADER* new_loader = is_default_loader ? default_loader : + (MODULE_LOADER*)malloc(sizeof(MODULE_LOADER)); + if (new_loader == NULL) + { + LogError("add_loader_from_json() - malloc failed for MODULE_LOADER struct for loader %zu", index); + result = MODULE_LOADER_ERROR; + } + else + { + if (!is_default_loader) + { + new_loader->name = malloc(strlen(loader_name) + 1); + if (new_loader->name == NULL) + { + LogError("add_loader_from_json() - malloc failed for loader name string for loader %zu", index); + result = MODULE_LOADER_ERROR; + } + else + { + strcpy((char *)new_loader->name, loader_name); + new_loader->type = loader_type; + new_loader->configuration = loader_configuration; + new_loader->api = default_loader->api; + + // add this module to the loaders list + result = ModuleLoader_Add(new_loader); + if (result != MODULE_LOADER_SUCCESS) + { + LogError("add_loader_from_json() - ModuleLoader_Add failed"); + if (loader_configuration != NULL) + { + new_loader->api->FreeConfiguration(loader_configuration); + } + free((void *)new_loader->name); + free(new_loader); + } + } + } + else + { + // since we are updating the configuration on an existing loader, we only + // replace the configuration field + result = ModuleLoader_UpdateConfiguration(new_loader, loader_configuration); + } + } + } + } + } + } + } + + return result; +} + +MODULE_LOADER_RESULT ModuleLoader_InitializeFromJson(const JSON_Value* loaders) +{ + MODULE_LOADER_RESULT result; + + if (loaders == NULL) + { + LogError("ModuleLoader_InitializeFromJson() - loaders is NULL"); + result = MODULE_LOADER_ERROR; + } + else + { + if (json_value_get_type(loaders) != JSONArray) + { + LogError("ModuleLoader_InitializeFromJson() - loaders is not a JSON array"); + result = MODULE_LOADER_ERROR; + } + else + { + JSON_Array* loaders_array = json_value_get_array(loaders); + if (loaders_array == NULL) + { + LogError("ModuleLoader_InitializeFromJson() - json_value_get_array failed"); + result = MODULE_LOADER_ERROR; + } + else + { + size_t length = json_array_get_count(loaders_array); + size_t i; + for (i = 0; i < length; i++) + { + JSON_Value* loader = json_array_get_value(loaders_array, i); + if (loader == NULL) + { + LogError("ModuleLoader_InitializeFromJson() - json_array_get_value failed for loader %zu", i); + break; + } + + if (add_loader_from_json(loader, i) != MODULE_LOADER_SUCCESS) + { + LogError("ModuleLoader_InitializeFromJson() - add_loader_from_json failed for loader %zu", i); + break; + } + } + + // if the loop terminated early then something went wrong + result = (i < length) ? MODULE_LOADER_ERROR : MODULE_LOADER_SUCCESS; + } + } + } + + return result; +} \ No newline at end of file diff --git a/core/src/module_loaders/dotnet_loader.c b/core/src/module_loaders/dotnet_loader.c new file mode 100644 index 00000000..e69de29b diff --git a/core/src/module_loaders/dynamic_loader.c b/core/src/module_loaders/dynamic_loader.c new file mode 100644 index 00000000..0e48a9d3 --- /dev/null +++ b/core/src/module_loaders/dynamic_loader.c @@ -0,0 +1,300 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +#include +#ifdef _CRTDBG_MAP_ALLOC +#include +#endif +#include "azure_c_shared_utility/gballoc.h" +#include + +#include "azure_c_shared_utility/xlogging.h" +#include "parson.h" + +#include "module.h" +#include "module_access.h" +#include "module_loader.h" +#include "module_loaders/dynamic_loader.h" +#include "dynamic_library.h" + +typedef struct DYNAMIC_MODULE_HANDLE_DATA_TAG +{ + void* library; + const MODULE_API* api; +}DYNAMIC_MODULE_HANDLE_DATA; + +static MODULE_LIBRARY_HANDLE DynamicModuleLoader_Load(const MODULE_LOADER* loader, const void* entrypoint) +{ + DYNAMIC_MODULE_HANDLE_DATA* result; + + // loader & entrypoint cannot be null + if (loader == NULL || entrypoint == NULL) + { + result = NULL; + LogError( + "invalid input - loader = %p, entrypoint = %p", + loader, entrypoint + ); + } + else + { + if (loader->type != NATIVE) + { + result = NULL; + LogError("loader->type is not NATIVE"); + } + else + { + DYNAMIC_LOADER_ENTRYPOINT* dynamic_loader_entrypoint = (DYNAMIC_LOADER_ENTRYPOINT*)entrypoint; + if (dynamic_loader_entrypoint->moduleLibraryFileName == NULL) + { + result = NULL; + LogError("moduleLibraryFileName is NULL"); + } + else + { + const char * moduleLibraryFileName = STRING_c_str(dynamic_loader_entrypoint->moduleLibraryFileName); + /* Codes_SRS_MODULE_LOADER_17_005: [DynamicModuleLoader_Load shall allocate memory for the structure MODULE_LIBRARY_HANDLE.] */ + result = (DYNAMIC_MODULE_HANDLE_DATA*)malloc(sizeof(DYNAMIC_MODULE_HANDLE_DATA)); + if (result == NULL) + { + /*Codes_SRS_MODULE_LOADER_17_014: [If memory allocation is not successful, the load shall fail, and it shall return NULL.]*/ + LogError("malloc(sizeof(DYNAMIC_MODULE_HANDLE_DATA)) failed"); + } + else + { + /* load the DLL */ + /* Codes_SRS_MODULE_LOADER_17_002: [DynamicModuleLoader_Load shall load the library as a file, the filename given by the moduleLibraryFileName.]*/ + result->library = DynamicLibrary_LoadLibrary(moduleLibraryFileName); + if (result->library == NULL) + { + /* Codes_SRS_MODULE_LOADER_17_012: [If the attempt is not successful, the load shall fail, and it shall return NULL.]*/ + free(result); + result = NULL; + LogError("DynamicLibrary_LoadLibrary() returned NULL"); + } + else + { + /* Codes_SRS_MODULE_LOADER_17_003: [DynamicModuleLoader_Load shall locate the function defined by MODULE_GETAPI=_NAME in the open library.] */ + pfModule_GetApi pfnGetAPI = (pfModule_GetApi)DynamicLibrary_FindSymbol(result->library, MODULE_GETAPI_NAME); + if (pfnGetAPI == NULL) + { + /* Codes_SRS_MODULE_LOADER_17_013: [If locating the function is not successful, the load shall fail, and it shall return NULL.]*/ + DynamicLibrary_UnloadLibrary(result->library); + free(result); + result = NULL; + LogError("DynamicLibrary_FindSymbol() returned NULL"); + } + else + { + /* Codes_SRS_MODULE_LOADER_17_004: [DynamicModuleLoader_Load shall call the function defined by MODULE_GETAPI_NAME in the open library.]*/ + result->api = pfnGetAPI(Module_ApiGatewayVersion); + + /* if any of the required functions is NULL then we have a misbehaving module */ + if (result->api == NULL || + result->api->version > Module_ApiGatewayVersion || + MODULE_CREATE(result->api) == NULL || + MODULE_DESTROY(result->api) == NULL || + MODULE_RECEIVE(result->api) == NULL) + { + /*Codes_SRS_MODULE_LOADER_26_001: [ If the get API call doesn't set required functions, the load shall fail and it shall return `NULL`. ]*/ + DynamicLibrary_UnloadLibrary(result->library); + free(result); + result = NULL; + LogError("pfnGetapi() returned NULL"); + } + } + } + } + } + } + } + + /*Codes_SRS_MODULE_LOADER_17_006: [DynamicModuleLoader_Load shall return a non-NULL handle to a MODULE_LIBRARY_DATA_TAG upon success.]*/ + return result; +} + +static const MODULE_API* DynamicModuleLoader_GetModuleApi(MODULE_LIBRARY_HANDLE moduleLibraryHandle) +{ + const MODULE_API* result; + + if (moduleLibraryHandle == NULL) + { + /*Codes_SRS_MODULE_LOADER_17_007: [DynamicModuleLoader_GetModuleApi shall return NULL if the moduleLibraryHandle is NULL.]*/ + result = NULL; + LogError("DynamicModuleLoader_GetModuleApi() - moduleLibraryHandle is NULL"); + } + else + { + /*Codes_SRS_MODULE_LOADER_17_008: [DynamicModuleLoader_GetModuleApi shall return a valid pointer to MODULE_API on success.]*/ + DYNAMIC_MODULE_HANDLE_DATA* loader_data = moduleLibraryHandle; + result = loader_data->api; + } + + return result; +} + + +/*Codes_SRS_MODULE_LOADER_17_009: [DynamicModuleLoader_Unload shall do nothing if the moduleLibraryHandle is NULL.]*/ +/*Codes_SRS_MODULE_LOADER_17_010: [DynamicModuleLoader_Unload shall attempt to unload the library.]*/ +/*Codes_SRS_MODULE_LOADER_17_011: [ModuleLoader_UnLoad shall deallocate memory for the structure MODULE_LIBRARY_HANDLE.]*/ + +static void DynamicModuleLoader_Unload(MODULE_LIBRARY_HANDLE moduleLibraryHandle) +{ + if (moduleLibraryHandle != NULL) + { + DYNAMIC_MODULE_HANDLE_DATA* loader_data = moduleLibraryHandle; + DynamicLibrary_UnloadLibrary(loader_data->library); + free(loader_data); + } + else + { + LogError("DynamicModuleLoader_Unload() - moduleLibraryHandle is NULL"); + } +} + +static void* DynamicModuleLoader_ParseEntrypointFromJson(const JSON_Value* json) +{ + // The input is a JSON object that looks like this: + // "entrypoint": { + // "module.path": "path/to/module" + // } + DYNAMIC_LOADER_ENTRYPOINT* config; + if (json == NULL) + { + LogError("DynamicModuleLoader_ParseEntrypointFromJson() - json is NULL"); + config = NULL; + } + else + { + // "json" must be an "object" type + if (json_value_get_type(json) != JSONObject) + { + LogError("DynamicModuleLoader_ParseEntrypointFromJson() - 'json' is not an object value"); + config = NULL; + } + else + { + JSON_Object* entrypoint = json_value_get_object(json); + if (entrypoint == NULL) + { + LogError("DynamicModuleLoader_ParseEntrypointFromJson() - json_value_get_object failed"); + config = NULL; + } + else + { + const char* modulePath = json_object_get_string(entrypoint, "module.path"); + if (modulePath == NULL) + { + LogError("DynamicModuleLoader_ParseEntrypointFromJson() - json_object_get_string for 'module.path' returned NULL"); + config = NULL; + } + else + { + config = (DYNAMIC_LOADER_ENTRYPOINT*)malloc(sizeof(DYNAMIC_LOADER_ENTRYPOINT)); + if (config != NULL) + { + config->moduleLibraryFileName = STRING_construct(modulePath); + if (config->moduleLibraryFileName == NULL) + { + LogError("DynamicModuleLoader_ParseEntrypointFromJson() - STRING_construct failed"); + free(config); + config = NULL; + } + else + { + /** + * Everything's good. + */ + } + } + else + { + LogError("DynamicModuleLoader_ParseEntrypointFromJson() - malloc failed"); + } + } + } + } + } + + return (void*)config; +} + +static void DynamicModuleLoader_FreeEntrypoint(void* entrypoint) +{ + if (entrypoint != NULL) + { + DYNAMIC_LOADER_ENTRYPOINT* ep = (DYNAMIC_LOADER_ENTRYPOINT*)entrypoint; + STRING_delete(ep->moduleLibraryFileName); + free(ep); + } + else + { + LogError("DynamicModuleLoader_FreeEntrypoint - entrypoint is NULL"); + } +} + +static MODULE_LOADER_BASE_CONFIGURATION* DynamicModuleLoader_ParseConfigurationFromJson(const JSON_Value* json) +{ + /** + * The dynamic loader does not have any configuration so we always return NULL. + */ + return NULL; +} + +static void DynamicModuleLoader_FreeConfiguration(MODULE_LOADER_BASE_CONFIGURATION* configuration) +{ + /** + * Nothing to free. + */ +} + +void* DynamicModuleLoader_BuildModuleConfiguration( + const MODULE_LOADER* loader, + const void* entrypoint, + const void* module_configuration +) +{ + /** + * The native dynamic loader does not need to do any special configuration translation. + * We simply return the module_configuration as is. + */ + return (void *)module_configuration; +} + +void DynamicModuleLoader_FreeModuleConfiguration(const void* module_configuration) +{ + /** + * Nothing to free. + */ +} + +/* Codes_SRS_MODULE_LOADER_17_015: [ DynamicLoader_GetApi shall set all the fields of the MODULE_LOADER_API structure. ]*/ +MODULE_LOADER_API Dynamic_Module_Loader_API = +{ + .Load = DynamicModuleLoader_Load, + .Unload = DynamicModuleLoader_Unload, + .GetApi = DynamicModuleLoader_GetModuleApi, + + .ParseEntrypointFromJson = DynamicModuleLoader_ParseEntrypointFromJson, + .FreeEntrypoint = DynamicModuleLoader_FreeEntrypoint, + + .ParseConfigurationFromJson = DynamicModuleLoader_ParseConfigurationFromJson, + .FreeConfiguration = DynamicModuleLoader_FreeConfiguration, + + .BuildModuleConfiguration = DynamicModuleLoader_BuildModuleConfiguration, + .FreeModuleConfiguration = DynamicModuleLoader_FreeModuleConfiguration +}; + +MODULE_LOADER Dynamic_Module_Loader = +{ + NATIVE, + DYNAMIC_LOADER_NAME, + NULL, + &Dynamic_Module_Loader_API +}; + +const MODULE_LOADER* DynamicLoader_Get(void) +{ + /* Codes_SRS_MODULE_LOADER_17_020: [ DynamicLoader_GetApi shall return a non-NULL pointer to a MODULER_LOADER structure. ] */ + return &Dynamic_Module_Loader; +} diff --git a/core/src/module_loaders/java_loader.c b/core/src/module_loaders/java_loader.c new file mode 100644 index 00000000..e69de29b diff --git a/core/src/module_loaders/node_loader.c b/core/src/module_loaders/node_loader.c new file mode 100644 index 00000000..44273861 --- /dev/null +++ b/core/src/module_loaders/node_loader.c @@ -0,0 +1,370 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +#include +#ifdef _CRTDBG_MAP_ALLOC +#include +#endif +#include "azure_c_shared_utility/gballoc.h" +#include + +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/strings.h" +#include "parson.h" + +#include "module.h" +#include "module_access.h" +#include "dynamic_library.h" +#include "module_loader.h" +#include "module_loaders/node_loader.h" +#include "nodejs.h" + +typedef struct NODE_MODULE_HANDLE_DATA_TAG +{ + DYNAMIC_LIBRARY_HANDLE binding_module; + const MODULE_API* api; +}NODE_MODULE_HANDLE_DATA; + +static DYNAMIC_LIBRARY_HANDLE NodeModuleLoader_LoadBindingModule(const MODULE_LOADER* loader) +{ + // find the binding path from the loader configuration; if there isn't one, use + // a default path + const char* binding_path = NODE_BINDING_MODULE_NAME; + if (loader->configuration != NULL && loader->configuration->binding_path != NULL) + { + binding_path = STRING_c_str(loader->configuration->binding_path); + } + + return DynamicLibrary_LoadLibrary(binding_path); +} + +static MODULE_LIBRARY_HANDLE NodeModuleLoader_Load(const MODULE_LOADER* loader, const void* entrypoint) +{ + NODE_MODULE_HANDLE_DATA* result; + + // loader cannot be null + if (loader == NULL) + { + result = NULL; + LogError( + "NodeModuleLoader_Load() - invalid inputs - loader = %p", + loader + ); + } + else + { + if (loader->type != NODEJS) + { + result = NULL; + LogError("NodeModuleLoader_Load() - loader->type is not NODEJS"); + } + else + { + result = (NODE_MODULE_HANDLE_DATA*)malloc(sizeof(NODE_MODULE_HANDLE_DATA)); + if (result == NULL) + { + LogError("NodeModuleLoader_Load() - malloc returned NULL"); + } + else + { + result->binding_module = NodeModuleLoader_LoadBindingModule(loader); + if (result->binding_module == NULL) + { + LogError("NodeModuleLoader_Load() - NodeModuleLoader_LoadBindingModule returned NULL"); + free(result); + result = NULL; + } + else + { + pfModule_GetApi pfnGetAPI = (pfModule_GetApi)DynamicLibrary_FindSymbol(result->binding_module, MODULE_GETAPI_NAME); + if (pfnGetAPI == NULL) + { + DynamicLibrary_UnloadLibrary(result->binding_module); + free(result); + result = NULL; + LogError("NodeModuleLoader_Load() - DynamicLibrary_FindSymbol() returned NULL"); + } + else + { + result->api = pfnGetAPI(Module_ApiGatewayVersion); + + /* if any of the required functions is NULL then we have a misbehaving module */ + if (result->api == NULL || + result->api->version > Module_ApiGatewayVersion || + MODULE_CREATE(result->api) == NULL || + MODULE_DESTROY(result->api) == NULL || + MODULE_RECEIVE(result->api) == NULL) + { + DynamicLibrary_UnloadLibrary(result->binding_module); + free(result); + result = NULL; + LogError("NodeModuleLoader_Load() - pfnGetapi() returned NULL"); + } + } + } + } + } + } + + return result; +} + +static const MODULE_API* NodeModuleLoader_GetModuleApi(MODULE_LIBRARY_HANDLE moduleLibraryHandle) +{ + const MODULE_API* result; + + if (moduleLibraryHandle == NULL) + { + result = NULL; + LogError("NodeModuleLoader_GetModuleApi() - moduleLibraryHandle is NULL"); + } + else + { + NODE_MODULE_HANDLE_DATA* loader_data = moduleLibraryHandle; + result = loader_data->api; + } + + return result; +} + + +static void NodeModuleLoader_Unload(MODULE_LIBRARY_HANDLE moduleLibraryHandle) +{ + if (moduleLibraryHandle != NULL) + { + NODE_MODULE_HANDLE_DATA* loader_data = moduleLibraryHandle; + DynamicLibrary_UnloadLibrary(loader_data->binding_module); + free(loader_data); + } + else + { + LogError("NodeModuleLoader_Unload() - moduleLibraryHandle is NULL"); + } +} + +static void* NodeModuleLoader_ParseEntrypointFromJson(const JSON_Value* json) +{ + // The input is a JSON object that looks like this: + // "entrypoint": { + // "main.path": "path/to/module" + // } + NODE_LOADER_ENTRYPOINT* config; + if (json == NULL) + { + LogError("NodeModuleLoader_ParseEntrypointFromJson() - json is NULL"); + config = NULL; + } + else + { + // "json" must be an "object" type + if (json_value_get_type(json) != JSONObject) + { + LogError("NodeModuleLoader_ParseEntrypointFromJson() - 'json' is not an object value"); + config = NULL; + } + else + { + JSON_Object* entrypoint = json_value_get_object(json); + if (entrypoint == NULL) + { + LogError("NodeModuleLoader_ParseEntrypointFromJson() - json_value_get_object failed"); + config = NULL; + } + else + { + const char* mainPath = json_object_get_string(entrypoint, "main.path"); + if (mainPath == NULL) + { + LogError("NodeModuleLoader_ParseEntrypointFromJson() - json_object_get_string for 'main.path' returned NULL"); + config = NULL; + } + else + { + config = (NODE_LOADER_ENTRYPOINT*)malloc(sizeof(NODE_LOADER_ENTRYPOINT)); + if (config != NULL) + { + config->mainPath = STRING_construct(mainPath); + if (config->mainPath == NULL) + { + LogError("NodeModuleLoader_ParseEntrypointFromJson() - STRING_construct failed"); + free(config); + config = NULL; + } + else + { + /** + * Everything's good. + */ + } + } + else + { + LogError("NodeModuleLoader_ParseEntrypointFromJson() - malloc failed"); + } + } + } + } + } + + return (void*)config; +} + +static void NodeModuleLoader_FreeEntrypoint(void* entrypoint) +{ + if (entrypoint != NULL) + { + NODE_LOADER_ENTRYPOINT* ep = (NODE_LOADER_ENTRYPOINT*)entrypoint; + STRING_delete(ep->mainPath); + free(ep); + } + else + { + LogError("NodeModuleLoader_FreeEntrypoint - entrypoint is NULL"); + } +} + +static MODULE_LOADER_BASE_CONFIGURATION* NodeModuleLoader_ParseConfigurationFromJson(const JSON_Value* json) +{ + MODULE_LOADER_BASE_CONFIGURATION* result = (MODULE_LOADER_BASE_CONFIGURATION*)malloc(sizeof(MODULE_LOADER_BASE_CONFIGURATION)); + if (result != NULL) + { + if (ModuleLoader_ParseBaseConfigurationFromJson(result, json) != MODULE_LOADER_SUCCESS) + { + LogError("NodeModuleLoader_ParseConfigurationFromJson() - ModuleLoader_ParseBaseConfigurationFromJson failed"); + free(result); + result = NULL; + } + else + { + /** + * Everything's good. + */ + } + } + else + { + LogError("NodeModuleLoader_ParseConfigurationFromJson() - malloc failed"); + } + + return result; +} + +static void NodeModuleLoader_FreeConfiguration(MODULE_LOADER_BASE_CONFIGURATION* configuration) +{ + if (configuration != NULL) + { + ModuleLoader_FreeBaseConfiguration(configuration); + free(configuration); + } + else + { + LogError("NodeModuleLoader_FreeConfiguration() - configuration is NULL"); + } +} + +void* NodeModuleLoader_BuildModuleConfiguration( + const MODULE_LOADER* loader, + const void* entrypoint, + const void* module_configuration +) +{ + NODEJS_MODULE_CONFIG* result; + if (entrypoint == NULL) + { + LogError("NodeModuleLoader_BuildModuleConfiguration() - entrypoint is NULL."); + result = NULL; + } + else + { + NODE_LOADER_ENTRYPOINT* node_entrypoint = (NODE_LOADER_ENTRYPOINT*)entrypoint; + if (node_entrypoint->mainPath == NULL) + { + LogError("NodeModuleLoader_BuildModuleConfiguration() - entrypoint mainPath is NULL."); + result = NULL; + } + else + { + result = (NODEJS_MODULE_CONFIG*)malloc(sizeof(NODEJS_MODULE_CONFIG)); + if (result == NULL) + { + LogError("NodeModuleLoader_BuildModuleConfiguration() - malloc failed."); + } + else + { + result->main_path = STRING_clone(node_entrypoint->mainPath); + if (result->main_path == NULL) + { + LogError("NodeModuleLoader_BuildModuleConfiguration() - STRING_clone for mainPath failed."); + free(result); + result = NULL; + } + else + { + // module_configuration is a STRING_HANDLE - see NODEJS_ParseConfigurationFromJson in nodejs.cpp + // PERF NOTE: We really need ref-counted strings to avoid redundant string clones. + result->configuration_json = (module_configuration == NULL) ? NULL : + STRING_clone((STRING_HANDLE)module_configuration); + + if (module_configuration != NULL && result->configuration_json == NULL) + { + LogError("NodeModuleLoader_BuildModuleConfiguration() - STRING_clone for module configuration failed."); + STRING_delete(result->main_path); + free(result); + result = NULL; + } + else + { + /** + * Everything's good. + */ + } + } + } + } + } + + return result; +} + +void NodeModuleLoader_FreeModuleConfiguration(const void* module_configuration) +{ + if (module_configuration == NULL) + { + LogError("module_configuration is NULL"); + } + else + { + NODEJS_MODULE_CONFIG* config = (NODEJS_MODULE_CONFIG*)module_configuration; + STRING_delete(config->main_path); + STRING_delete(config->configuration_json); + free(config); + } +} + +MODULE_LOADER_API Node_Module_Loader_API = +{ + .Load = NodeModuleLoader_Load, + .Unload = NodeModuleLoader_Unload, + .GetApi = NodeModuleLoader_GetModuleApi, + + .ParseEntrypointFromJson = NodeModuleLoader_ParseEntrypointFromJson, + .FreeEntrypoint = NodeModuleLoader_FreeEntrypoint, + + .ParseConfigurationFromJson = NodeModuleLoader_ParseConfigurationFromJson, + .FreeConfiguration = NodeModuleLoader_FreeConfiguration, + + .BuildModuleConfiguration = NodeModuleLoader_BuildModuleConfiguration, + .FreeModuleConfiguration = NodeModuleLoader_FreeModuleConfiguration +}; + +MODULE_LOADER Node_Module_Loader = +{ + NODEJS, // the loader type + NODE_LOADER_NAME, // the name of this loader + NULL, // default loader configuration is NULL unless user overrides + &Node_Module_Loader_API // the module loader function pointer table +}; + +const MODULE_LOADER* NodeLoader_Get(void) +{ + return &Node_Module_Loader; +} diff --git a/core/tests/dynamic_loader_ut/CMakeLists.txt b/core/tests/dynamic_loader_ut/CMakeLists.txt index a31d2481..e14e0535 100644 --- a/core/tests/dynamic_loader_ut/CMakeLists.txt +++ b/core/tests/dynamic_loader_ut/CMakeLists.txt @@ -12,15 +12,15 @@ set(${testSuiteName}_cpp_files ) set(${testSuiteName}_c_files - ../../src/dynamic_loader.c + ../../src/module_loaders/dynamic_loader.c ) set(${testSuiteName}_h_files ../../inc/module_loader.h ../../inc/dynamic_library.h - ../../inc/dynamic_loader.h + ../../inc/module_loaders/dynamic_loader.h ) include_directories(${GW_INC}) -build_test_artifacts(${testSuiteName} ON) \ No newline at end of file +build_test_artifacts(${testSuiteName} ON) diff --git a/modules/logger/inc/logger.h b/modules/logger/inc/logger.h index 5cf678eb..4d89ede8 100644 --- a/modules/logger/inc/logger.h +++ b/modules/logger/inc/logger.h @@ -5,6 +5,7 @@ #define LOGGER_H #include "module.h" +#include "azure_c_shared_utility/strings.h" typedef enum LOGGER_TYPE_TAG { @@ -18,7 +19,7 @@ typedef struct LOGGER_CONFIG_TAG { struct LOGGER_CONFIG_FILE_TAG { - const char* name; + STRING_HANDLE name; } loggerConfigFile; } selectee; } LOGGER_CONFIG; /*this needs to be passed to the Module_Create function*/ diff --git a/modules/logger/src/logger.c b/modules/logger/src/logger.c index c7929493..58404f6b 100644 --- a/modules/logger/src/logger.c +++ b/modules/logger/src/logger.c @@ -18,6 +18,7 @@ #include #include #include +#include // TODO: wrap in #ifdef #include @@ -158,19 +159,19 @@ static MODULE_HANDLE Logger_Create(BROKER_HANDLE broker, const void* configurati else { /*Codes_SRS_LOGGER_02_006: [Logger_Create shall open the file configuration the filename selectee.loggerConfigFile.name in update (reading and writing) mode and assign the result of fopen to fout field. ]*/ - result->fout = fopen(config->selectee.loggerConfigFile.name, "r+b"); /*open binary file for update (reading and writing)*/ + result->fout = fopen(STRING_c_str(config->selectee.loggerConfigFile.name), "r+b"); /*open binary file for update (reading and writing)*/ if (result->fout == NULL) { /*if the file does not exist [or other error, indistinguishable here] try to create it*/ /*Codes_SRS_LOGGER_02_020: [If the file selectee.loggerConfigFile.name does not exist, it shall be created.]*/ - result->fout = fopen(config->selectee.loggerConfigFile.name, "w+b");/*truncate to zero length or create binary file for update*/ + result->fout = fopen(STRING_c_str(config->selectee.loggerConfigFile.name), "w+b");/*truncate to zero length or create binary file for update*/ } /*Codes_SRS_LOGGER_02_007: [If Logger_Create encounters any errors while creating the LOGGER_HANDLE_DATA then it shall fail and return NULL.]*/ /*Codes_SRS_LOGGER_02_021: [If creating selectee.loggerConfigFile.name fails then Logger_Create shall fail and return NULL.]*/ if (result->fout == NULL) { - LogError("unable to open file %s", config->selectee.loggerConfigFile.name); + LogError("unable to open file %s", STRING_c_str(config->selectee.loggerConfigFile.name)); free(result); result = NULL; } @@ -183,7 +184,7 @@ static MODULE_HANDLE Logger_Create(BROKER_HANDLE broker, const void* configurati LogError("unable to fseek to end of file"); if (fclose(result->fout) != 0) { - LogError("unable to close file %s", config->selectee.loggerConfigFile.name); + LogError("unable to close file %s", STRING_c_str(config->selectee.loggerConfigFile.name)); } free(result); result = NULL; @@ -199,7 +200,7 @@ static MODULE_HANDLE Logger_Create(BROKER_HANDLE broker, const void* configurati LogError("unable to ftell (errno=%d)", errno); if (fclose(result->fout) != 0) { - LogError("unable to close file %s", config->selectee.loggerConfigFile.name); + LogError("unable to close file %s", STRING_c_str(config->selectee.loggerConfigFile.name)); } free(result); result = NULL; @@ -216,7 +217,7 @@ static MODULE_HANDLE Logger_Create(BROKER_HANDLE broker, const void* configurati LogError("unable to write to output file"); if (fclose(result->fout) != 0) { - LogError("unable to close file %s", config->selectee.loggerConfigFile.name); + LogError("unable to close file %s", STRING_c_str(config->selectee.loggerConfigFile.name)); } free(result); result = NULL; @@ -229,7 +230,7 @@ static MODULE_HANDLE Logger_Create(BROKER_HANDLE broker, const void* configurati LogError("append_logStartStop failed"); if (fclose(result->fout) != 0) { - LogError("unable to close file %s", config->selectee.loggerConfigFile.name); + LogError("unable to close file %s", STRING_c_str(config->selectee.loggerConfigFile.name)); } free(result); result = NULL; @@ -258,18 +259,13 @@ static MODULE_HANDLE Logger_Create(BROKER_HANDLE broker, const void* configurati return result; } -static MODULE_HANDLE Logger_CreateFromJson(BROKER_HANDLE broker, const char* configuration) +static void* Logger_ParseConfigurationFromJson(const char* configuration) { - - MODULE_HANDLE result; - /*Codes_SRS_LOGGER_05_001: [ If broker is NULL then Logger_CreateFromJson shall fail and return NULL. ]*/ + LOGGER_CONFIG* result; /*Codes_SRS_LOGGER_05_003: [ If configuration is NULL then Logger_CreateFromJson shall fail and return NULL. ]*/ - if( - (broker == NULL) || - (configuration == NULL) - ) + if(configuration == NULL) { - LogError("NULL parameter detected broker=%p configuration=%p", broker, configuration); + LogError("NULL parameter detected configuration=%p", configuration); result = NULL; } else @@ -302,23 +298,27 @@ static MODULE_HANDLE Logger_CreateFromJson(BROKER_HANDLE broker, const char* con { /*fileNameValue is believed at this moment to be a string that might point to a filename on the system*/ - LOGGER_CONFIG config; - config.selector = LOGGING_TO_FILE; - config.selectee.loggerConfigFile.name = fileNameValue; - - /*Codes_SRS_LOGGER_05_005: [ Logger_CreateFromJson shall pass broker and the filename to Logger_Create. ]*/ - result = Logger_Create(broker, &config); - + result = (LOGGER_CONFIG*)malloc(sizeof(LOGGER_CONFIG)); if (result == NULL) { - /*Codes_SRS_LOGGER_05_007: [ If Logger_Create fails then Logger_CreateFromJson shall fail and return NULL. ]*/ - /*return result "as is" - that is - NULL*/ - LogError("unable to create Logger"); + LogError("malloc failed"); } else { - /*Codes_SRS_LOGGER_05_006: [ If Logger_Create succeeds then Logger_CreateFromJson shall succeed and return a non-NULL value. ]*/ - /*return result "as is" - that is - not NULL*/ + result->selector = LOGGING_TO_FILE; + result->selectee.loggerConfigFile.name = STRING_construct(fileNameValue); + if (result->selectee.loggerConfigFile.name == NULL) + { + LogError("STRING_construct failed"); + free(result); + result = NULL; + } + else + { + /** + * Everything's good. + */ + } } } } @@ -329,6 +329,20 @@ static MODULE_HANDLE Logger_CreateFromJson(BROKER_HANDLE broker, const char* con return result; } +static void Logger_FreeConfiguration(void* configuration) +{ + if (configuration == NULL) + { + LogError("configuration is NULL"); + } + else + { + LOGGER_CONFIG* config = (LOGGER_CONFIG*)configuration; + STRING_delete(config->selectee.loggerConfigFile.name); + free(config); + } +} + static void Logger_Destroy(MODULE_HANDLE module) { /*Codes_SRS_LOGGER_02_014: [If moduleHandle is NULL then Logger_Destroy shall return.]*/ @@ -484,7 +498,8 @@ static const MODULE_API_1 Logger_APIS_all = { {MODULE_API_VERSION_1}, - Logger_CreateFromJson, + Logger_ParseConfigurationFromJson, + Logger_FreeConfiguration, Logger_Create, Logger_Destroy, Logger_Receive,