From a84e730a51479f7c04d7704ce1580813bec9da00 Mon Sep 17 00:00:00 2001 From: hirosho Date: Tue, 24 Apr 2018 08:48:41 +0900 Subject: [PATCH 01/18] add uhttp --- .gitmodules | 3 +++ v1/deps/uhttp | 1 + 2 files changed, 4 insertions(+) create mode 160000 v1/deps/uhttp diff --git a/.gitmodules b/.gitmodules index e7973988..8d7b3808 100644 --- a/.gitmodules +++ b/.gitmodules @@ -25,3 +25,6 @@ [submodule "deps/iot-sdk-c"] path = v1/deps/iot-sdk-c url = https://github.com/Azure/azure-iot-sdk-c.git +[submodule "v1/deps/azure-uhttp-c"] + path = v1/deps/uhttp + url = https://github.com/Azure/azure-uhttp-c.git diff --git a/v1/deps/uhttp b/v1/deps/uhttp new file mode 160000 index 00000000..77731aed --- /dev/null +++ b/v1/deps/uhttp @@ -0,0 +1 @@ +Subproject commit 77731aeddb89f2f2cd1f9704fc030c9327eb1f3a From 88f34ff4a6b9d4e5cefeb91b7ce04ec671e6b653 Mon Sep 17 00:00:00 2001 From: hirosho Date: Tue, 24 Apr 2018 08:51:49 +0900 Subject: [PATCH 02/18] add remote update implementation draft --- v1/core/CMakeLists.txt | 42 +++++++- v1/core/dependencies.cmake | 36 +++++++ v1/core/inc/gateway.h | 8 ++ v1/core/src/gateway.c | 6 ++ v1/core/src/gateway_createfromjson.c | 97 ++++++++++++++++++- v1/core/src/gateway_internal.c | 16 +++ v1/core/src/gateway_internal.h | 4 + .../hello_world/src/hello_world_win.json | 6 +- 8 files changed, 211 insertions(+), 4 deletions(-) create mode 100644 v1/core/dependencies.cmake diff --git a/v1/core/CMakeLists.txt b/v1/core/CMakeLists.txt index 0a15a0df..aa596b8d 100644 --- a/v1/core/CMakeLists.txt +++ b/v1/core/CMakeLists.txt @@ -2,6 +2,9 @@ #Licensed under the MIT license. See LICENSE file in the project root for full license information. cmake_minimum_required(VERSION 2.8.12) + +include("dependencies.cmake") + if(POLICY CMP0054) cmake_policy(SET CMP0054 OLD) endif() @@ -120,6 +123,8 @@ set(gateway_h_sources ./inc/module_loaders/dynamic_loader.h ) +include_directories(${IOTHUB_CLIENT_INC_FOLDER}) + if(${enable_dotnet_binding}) set(gateway_c_sources ${gateway_c_sources} @@ -251,8 +256,9 @@ if(${enable_core_remote_module_support}) endif() endif() -target_link_libraries(gateway parson nanomsg aziotsharedutil ${dynamic_loader_library}) -target_link_libraries(gateway_static parson nanomsg aziotsharedutil ${dynamic_loader_library}) + +target_link_libraries(gateway parson nanomsg aziotsharedutil ${dynamic_loader_library} iothub_client) +target_link_libraries(gateway_static parson nanomsg aziotsharedutil ${dynamic_loader_library} iothub_client) target_link_libraries(module_host_static parson nanomsg aziotsharedutil ${dynamic_loader_library}) if(NOT WIN32) @@ -282,6 +288,38 @@ if(NOT ${use_xplat_uuid}) endif() endif() +###################################### +# Customize for Remote Update +###################################### +if(${use_amqp}) + target_link_libraries(gateway_static iothub_client_amqp_transport) + target_link_libraries(gateway iothub_client_amqp_transport) + linkUAMQP(gateway_static) + linkUAMQP(gateway) +else() + add_definitions(-DIOTHUBMODULE_NULL_AMQP) +endif() + +if(${use_http}) + target_link_libraries(gateway_static iothub_client_http_transport) + target_link_libraries(gateway iothub_client_http_transport) + linkHttp(gateway_static) + linkHttp(gateway) +else() + add_definitions(-DIOTHUBMODULE_NULL_HTTP) +endif() + +if(${use_mqtt}) + target_link_libraries(gateway_static iothub_client_mqtt_transport) + target_link_libraries(gateway iothub_client_mqtt_transport) + linkMqttLibrary(gateway_static) + linkMqttLibrary(gateway) +else() + add_definitions(-DIOTHUBMODULE_NULL_MQTT) +endif() + + + #this adds the tests to the build process if(${run_unittests} OR ${run_e2e_tests}) add_subdirectory(tests) diff --git a/v1/core/dependencies.cmake b/v1/core/dependencies.cmake new file mode 100644 index 00000000..b1923438 --- /dev/null +++ b/v1/core/dependencies.cmake @@ -0,0 +1,36 @@ +############################################################################### +###########################Find/Install/Build uamqp############################ +############################################################################### +findAndInstall(uamqp 1.0.25 ${PROJECT_SOURCE_DIR}/deps/uamqp ${PROJECT_SOURCE_DIR}/deps/uamqp -Duse_installed_dependencies=ON -G "${CMAKE_GENERATOR}") + +############################################################################### +###########################Find/Install/Build umqtt############################ +############################################################################### +findAndInstall(umqtt 1.0.25 ${PROJECT_SOURCE_DIR}/deps/umqtt ${PROJECT_SOURCE_DIR}/deps/umqtt -Duse_installed_dependencies=ON -G "${CMAKE_GENERATOR}") + +findAndInstall(uhttp 2018-02-09 ${PROJECT_SOURCE_DIR}/deps/uhttp ${PROJECT_SOURCE_DIR}/deps/uhttp -Duse_installed_dependencies=ON -G "${CMAKE_GENERATOR}") + +############################################################################### +#######################Find/Install/Build azure_iot_sdks####################### +############################################################################### +#The azure_iot_sdks repo requires special treatment. Parson submodule must be initialized. +if(NOT EXISTS ${PROJECT_SOURCE_DIR}/deps/iot-sdk-c/deps/parson/README.md) + execute_process( + COMMAND git submodule update --init ${PROJECT_SOURCE_DIR}/deps/iot-sdk-c + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + RESULT_VARIABLE res + + ) + if(${res}) + message(FATAL_ERROR "Error pulling iot-sdk-c submodule: ${res}") + endif() + execute_process( + COMMAND git submodule update --init deps/parson + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/deps/iot-sdk-c + RESULT_VARIABLE res + ) + if(${res}) + message(FATAL_ERROR "Error pulling parson submodule: ${res}") + endif() +endif() +findAndInstall(azure_iot_sdks 1.1.5 ${PROJECT_SOURCE_DIR}/deps/iot-sdk-c ${PROJECT_SOURCE_DIR}/deps/iot-sdk-c -Duse_installed_dependencies=ON -Duse_openssl=OFF -Dbuild_as_dynamic=ON -Dskip_samples=ON -G "${CMAKE_GENERATOR}") diff --git a/v1/core/inc/gateway.h b/v1/core/inc/gateway.h index 9a24a528..4187c731 100644 --- a/v1/core/inc/gateway.h +++ b/v1/core/inc/gateway.h @@ -20,6 +20,14 @@ #include "module_loader.h" #include "gateway_export.h" +#include "iothub_client.h" +#include "iothubtransport.h" +#include "iothubtransporthttp.h" +#include "iothubtransportamqp.h" +#include "iothubtransportmqtt.h" +#include "iothub_message.h" + + #ifdef __cplusplus extern "C" { diff --git a/v1/core/src/gateway.c b/v1/core/src/gateway.c index 7cb44943..12564fbf 100644 --- a/v1/core/src/gateway.c +++ b/v1/core/src/gateway.c @@ -3,7 +3,9 @@ #include #include +#include #include +#include #include "azure_c_shared_utility/gballoc.h" #include "azure_c_shared_utility/xlogging.h" @@ -194,6 +196,7 @@ GATEWAY_START_RESULT Gateway_Start(GATEWAY_HANDLE gw) EventSystem_ReportEvent(gw->event_system, gw, GATEWAY_STARTED); /*Codes_SRS_GATEWAY_17_013: [ This function shall return GATEWAY_START_SUCCESS upon completion. ]*/ result = GATEWAY_START_SUCCESS; + gw->runtime_status = 1; // 1 means runtime is running } else { @@ -205,6 +208,9 @@ GATEWAY_START_RESULT Gateway_Start(GATEWAY_HANDLE gw) void Gateway_Destroy(GATEWAY_HANDLE gw) { + if (gw->iothub_client != NULL) { + IoTHubClient_Destroy(gw->iothub_client); + } gateway_destroy_internal(gw); /*Codes_SRS_GATEWAY_17_019: [ The function shall destroy the module loader list. ]*/ ModuleLoader_Destroy(); diff --git a/v1/core/src/gateway_createfromjson.c b/v1/core/src/gateway_createfromjson.c index a2cba164..38538b17 100644 --- a/v1/core/src/gateway_createfromjson.c +++ b/v1/core/src/gateway_createfromjson.c @@ -5,6 +5,8 @@ #include "azure_c_shared_utility/gballoc.h" #include "azure_c_shared_utility/xlogging.h" #include "azure_c_shared_utility/macro_utils.h" +#include "azure_c_shared_utility/httpapi.h" +#include "azure_uhttp_c/uhttp.h" #include "gateway.h" #include "parson.h" #include "experimental/event_system.h" @@ -21,6 +23,10 @@ #define MODULE_PATH_KEY "module.path" #define ARG_KEY "args" +#define GATEWAY_KEY "gateway" +#define GATEWAY_IOTHUB_CONNECTION_STRING_KEY "connection-string" +#define GATEWAY_IOTHUB_TRANSPORT_KEY "transport" + #define LINKS_KEY "links" #define SOURCE_KEY "source" #define SINK_KEY "sink" @@ -39,6 +45,64 @@ static PARSE_JSON_RESULT parse_json_internal(GATEWAY_PROPERTIES* out_properties, static void destroy_properties_internal(GATEWAY_PROPERTIES* properties); void gateway_destroy_internal(GATEWAY_HANDLE gw); +void gateway_deviceTwinCallback(DEVICE_TWIN_UPDATE_STATE update_state, const unsigned char* payLoad, size_t size, void* userContextCallback) +{ + GATEWAY_HANDLE gw = (GATEWAY_HANDLE)userContextCallback; + unsigned char* buf = (unsigned char*)malloc(size); + memcpy(buf, payLoad, size); + if (buf[size - 1] != '}') { + int index = size; + while (index > 0) { + if (buf[--index] == '}') { + buf[index + 1] = '\0'; + break; + } + } +// buf[size - 1] = '\0'; + } + LogInfo("Device Twin Desired Properteis Updated - '%s'", payLoad); + + JSON_Value* root = json_parse_string(buf); + JSON_Object* document = json_value_get_object(root); + JSON_Object* desiredProperties = json_object_get_object(document, "desired"); + if (desiredProperties != NULL) { + JSON_Object* gwConfig = json_object_get_object(desiredProperties, GATEWAY_KEY); + if (gwConfig != NULL) { + const char* configValue = json_object_get_string(gwConfig, "configuration"); + if (configValue != NULL) { + HTTPAPI_RESULT httpResult = HTTPAPI_Init(); + if (httpResult == HTTPAPI_OK) { + HTTP_HANDLE handle = HTTPAPI_CreateConnection( configValue); + unsigned int statusCode; + HTTP_HEADERS_HANDLE responseHeaders = HTTPHeaders_Alloc(); + BUFFER_HANDLE responseBuffer = BUFFER_new(); + httpResult = HTTPAPI_ExecuteRequest(handle, HTTPAPI_REQUEST_GET, "", NULL, "", 0, &statusCode, responseHeaders, responseBuffer); + if (httpResult == HTTPAPI_OK) { + const unsigned char* receivedContent = BUFFER_u_char(responseBuffer); + LogInfo("Received - '%s'", receivedContent); + } + } + } + } + } + free(buf); +} + +static int strcmp_i(const char* lhs, const char* rhs) +{ + char lc, rc; + int cmp; + + do + { + lc = *lhs++; + rc = *rhs++; + cmp = tolower(lc) - tolower(rc); + } while (cmp == 0 && lc != 0 && rc != 0); + + return cmp; +} + GATEWAY_HANDLE Gateway_CreateFromJson(const char* file_path) { GATEWAY_HANDLE gw; @@ -67,7 +131,7 @@ GATEWAY_HANDLE Gateway_CreateFromJson(const char* file_path) { properties->gateway_modules = NULL; properties->gateway_links = NULL; - if ((parse_json_internal(properties, root_value) == PARSE_JSON_SUCCESS) && properties->gateway_modules != NULL && properties->gateway_links != NULL) + if ((parse_json_internal(properties, root_value) == PARSE_JSON_SUCCESS) && properties->gateway_modules != NULL && properties->gateway_links != NULL) { /*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. ]*/ @@ -79,6 +143,7 @@ GATEWAY_HANDLE Gateway_CreateFromJson(const char* file_path) } else { + /*Codes_SRS_GATEWAY_JSON_17_001: [ Upon successful creation, this function shall start the gateway. ]*/ GATEWAY_START_RESULT start_result; start_result = Gateway_Start(gw); @@ -89,6 +154,31 @@ GATEWAY_HANDLE Gateway_CreateFromJson(const char* file_path) gateway_destroy_internal(gw); gw = NULL; } + else { + JSON_Object *json_document = json_value_get_object(root_value); + JSON_Object *gwConfig = json_object_get_object(json_document, GATEWAY_KEY); + IOTHUB_CLIENT_TRANSPORT_PROVIDER transportProvider = NULL; + if (gwConfig != NULL) { + const char* connectionString = json_object_get_string(gwConfig, GATEWAY_IOTHUB_CONNECTION_STRING_KEY); + const char* iothubTransport = json_object_get_string(gwConfig, GATEWAY_IOTHUB_TRANSPORT_KEY); + if (connectionString != NULL && iothubTransport != NULL) { + if (strcmp_i(iothubTransport, "AMQP") == 0) + { + transportProvider = AMQP_Protocol; + } + else if (strcmp_i(iothubTransport, "MQTT") == 0) + { + transportProvider = MQTT_Protocol; + } + if (transportProvider != NULL) { + gw->iothub_client = IoTHubClient_CreateFromConnectionString(connectionString, transportProvider); + if (gw->iothub_client != NULL) { + IoTHubClient_SetDeviceTwinCallback(gw->iothub_client, gateway_deviceTwinCallback, gw); + } + } + } + } + } } } /*Codes_SRS_GATEWAY_JSON_14_006: [The function shall return NULL if the JSON_Value contains incomplete information.]*/ @@ -607,6 +697,11 @@ static PARSE_JSON_RESULT parse_json_internal(GATEWAY_PROPERTIES* out_properties, result = PARSE_JSON_MISCONFIGURED_OR_OTHER; LogError("JSON Configuration file is configured incorrectly or some other error occurred while parsing."); } + + JSON_Object* gatewayObject = json_object_get_object(json_document, GATEWAY_KEY); + if (gatewayObject != NULL) { + + } } /*Codes_SRS_GATEWAY_JSON_14_006: [The function shall return NULL if the JSON_Value contains incomplete information.]*/ else diff --git a/v1/core/src/gateway_internal.c b/v1/core/src/gateway_internal.c index 3369dba3..12b5a86c 100644 --- a/v1/core/src/gateway_internal.c +++ b/v1/core/src/gateway_internal.c @@ -272,6 +272,17 @@ GATEWAY_HANDLE gateway_create_internal(const GATEWAY_PROPERTIES* properties, boo return gateway; } +static void reportedStateCallback(int status_code, void* userContextCallback) + +{ + GATEWAY_HANDLE_DATA* gateway_handle = (GATEWAY_HANDLE*)userContextCallback; + + printf("Device Twin reported properties update completed with result: %d\r\n", status_code); + if (gateway_handle->runtime_status == 2) { + IoTHubClient_Destroy(gateway_handle->iothub_client); + } +} + void gateway_destroy_internal(GATEWAY_HANDLE gw) { /*Codes_SRS_GATEWAY_14_005: [If gw is NULL the function shall do nothing.]*/ @@ -279,6 +290,11 @@ void gateway_destroy_internal(GATEWAY_HANDLE gw) { GATEWAY_HANDLE_DATA* gateway_handle = (GATEWAY_HANDLE_DATA*)gw; + if (gateway_handle->iothub_client != NULL) { + gateway_handle->runtime_status = 2; + const char* reported_status = "{\"edgev1-runtime-status\":\"terminated\"}"; + IoTHubClient_SendReportedState(gateway_handle->iothub_client, reported_status, strlen(reported_status), reportedStateCallback, gateway_handle); + } if (gateway_handle->event_system != NULL) { /* event_system might be NULL here if destroying during failed creation, event system API should cleanly handle that */ diff --git a/v1/core/src/gateway_internal.h b/v1/core/src/gateway_internal.h index f817a85b..addc1c2f 100644 --- a/v1/core/src/gateway_internal.h +++ b/v1/core/src/gateway_internal.h @@ -41,6 +41,10 @@ typedef struct GATEWAY_HANDLE_DATA_TAG { /** @brief Vector of LINK_DATA links that the Gateway must track */ VECTOR_HANDLE links; + + IOTHUB_CLIENT_HANDLE iothub_client; + + int runtime_status; // 0->initializing, 1->running, 2->terminating } GATEWAY_HANDLE_DATA; typedef struct LINK_DATA_TAG { diff --git a/v1/samples/hello_world/src/hello_world_win.json b/v1/samples/hello_world/src/hello_world_win.json index 4484987b..efd40707 100644 --- a/v1/samples/hello_world/src/hello_world_win.json +++ b/v1/samples/hello_world/src/hello_world_win.json @@ -1,4 +1,8 @@ { + "gateway": { + "connection-string": "HostName=egohtaiwan20180320.azure-devices.net;DeviceId=edgev1;SharedAccessKey=PeKkxAQDC9gkDvzLVXyRPDcK7fYFKd+9oKKcVbCUlNs=", + "transport": "amqp" + }, "modules": [ { "name": "logger", @@ -29,4 +33,4 @@ "sink": "logger" } ] -} \ No newline at end of file +} From 53ab009ba10eb0dff73ac81546b4faf703a8f9cc Mon Sep 17 00:00:00 2001 From: hirosho Date: Wed, 25 Apr 2018 09:19:53 +0900 Subject: [PATCH 03/18] 1st step modification --- v1/core/dependencies.cmake | 2 +- v1/core/src/gateway_createfromjson.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/v1/core/dependencies.cmake b/v1/core/dependencies.cmake index b1923438..8a161009 100644 --- a/v1/core/dependencies.cmake +++ b/v1/core/dependencies.cmake @@ -8,7 +8,7 @@ findAndInstall(uamqp 1.0.25 ${PROJECT_SOURCE_DIR}/deps/uamqp ${PROJECT_SOURCE_DI ############################################################################### findAndInstall(umqtt 1.0.25 ${PROJECT_SOURCE_DIR}/deps/umqtt ${PROJECT_SOURCE_DIR}/deps/umqtt -Duse_installed_dependencies=ON -G "${CMAKE_GENERATOR}") -findAndInstall(uhttp 2018-02-09 ${PROJECT_SOURCE_DIR}/deps/uhttp ${PROJECT_SOURCE_DIR}/deps/uhttp -Duse_installed_dependencies=ON -G "${CMAKE_GENERATOR}") +#findAndInstall(uhttp 1.0.25 ${PROJECT_SOURCE_DIR}/deps/uhttp ${PROJECT_SOURCE_DIR}/deps/uhttp -Duse_installed_dependencies=ON -G "${CMAKE_GENERATOR}") ############################################################################### #######################Find/Install/Build azure_iot_sdks####################### diff --git a/v1/core/src/gateway_createfromjson.c b/v1/core/src/gateway_createfromjson.c index 38538b17..389b0fe8 100644 --- a/v1/core/src/gateway_createfromjson.c +++ b/v1/core/src/gateway_createfromjson.c @@ -6,7 +6,7 @@ #include "azure_c_shared_utility/xlogging.h" #include "azure_c_shared_utility/macro_utils.h" #include "azure_c_shared_utility/httpapi.h" -#include "azure_uhttp_c/uhttp.h" +#include "azure_uamqp_c/amqp_definitions.h" #include "gateway.h" #include "parson.h" #include "experimental/event_system.h" From 1e31748eaeab366f66ff729b2335a037c0a91ecb Mon Sep 17 00:00:00 2001 From: hirosho Date: Fri, 27 Apr 2018 06:40:14 +0900 Subject: [PATCH 04/18] remove deps uhttp temporal --- v1/deps/uhttp | 1 - 1 file changed, 1 deletion(-) delete mode 160000 v1/deps/uhttp diff --git a/v1/deps/uhttp b/v1/deps/uhttp deleted file mode 160000 index 77731aed..00000000 --- a/v1/deps/uhttp +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 77731aeddb89f2f2cd1f9704fc030c9327eb1f3a From 0bd534164838123d7599411a30d5a1f7235afb13 Mon Sep 17 00:00:00 2001 From: hirosho Date: Fri, 27 Apr 2018 08:26:13 +0900 Subject: [PATCH 05/18] add my local forked repository uhttp --- .gitmodules | 4 ++++ v1/deps/uhttp | 1 + 2 files changed, 5 insertions(+) create mode 160000 v1/deps/uhttp diff --git a/.gitmodules b/.gitmodules index 8d7b3808..be48bd35 100644 --- a/.gitmodules +++ b/.gitmodules @@ -28,3 +28,7 @@ [submodule "v1/deps/azure-uhttp-c"] path = v1/deps/uhttp url = https://github.com/Azure/azure-uhttp-c.git +[submodule "v1/deps/uhttp"] + path = v1/deps/uhttp + url = git@github.com:hirosho/azure-uhttp-c.git + branch = for-iot-edge diff --git a/v1/deps/uhttp b/v1/deps/uhttp new file mode 160000 index 00000000..2a8dbf7c --- /dev/null +++ b/v1/deps/uhttp @@ -0,0 +1 @@ +Subproject commit 2a8dbf7c3079cfe949eaa3f9b0507d0c586d0dd0 From 83d0986cfbb02f60a352c4ef8784f84b28ed986e Mon Sep 17 00:00:00 2001 From: hirosho Date: Sat, 28 Apr 2018 07:58:05 +0900 Subject: [PATCH 06/18] Delete submodule of uhttp Decide to refere azure-uhttp-c as nanomsg style --- .gitmodules | 4 ---- v1/core/dependencies.cmake | 7 +++++-- v1/deps/uhttp | 1 - 3 files changed, 5 insertions(+), 7 deletions(-) delete mode 160000 v1/deps/uhttp diff --git a/.gitmodules b/.gitmodules index be48bd35..8d7b3808 100644 --- a/.gitmodules +++ b/.gitmodules @@ -28,7 +28,3 @@ [submodule "v1/deps/azure-uhttp-c"] path = v1/deps/uhttp url = https://github.com/Azure/azure-uhttp-c.git -[submodule "v1/deps/uhttp"] - path = v1/deps/uhttp - url = git@github.com:hirosho/azure-uhttp-c.git - branch = for-iot-edge diff --git a/v1/core/dependencies.cmake b/v1/core/dependencies.cmake index 8a161009..37078595 100644 --- a/v1/core/dependencies.cmake +++ b/v1/core/dependencies.cmake @@ -8,12 +8,15 @@ findAndInstall(uamqp 1.0.25 ${PROJECT_SOURCE_DIR}/deps/uamqp ${PROJECT_SOURCE_DI ############################################################################### findAndInstall(umqtt 1.0.25 ${PROJECT_SOURCE_DIR}/deps/umqtt ${PROJECT_SOURCE_DIR}/deps/umqtt -Duse_installed_dependencies=ON -G "${CMAKE_GENERATOR}") -#findAndInstall(uhttp 1.0.25 ${PROJECT_SOURCE_DIR}/deps/uhttp ${PROJECT_SOURCE_DIR}/deps/uhttp -Duse_installed_dependencies=ON -G "${CMAKE_GENERATOR}") +message(STATUS,"Starting install uhttp!") +findAndInstall(uhttp 1.0.25 ${PROJECT_SOURCE_DIR}/deps/uhttp ${PROJECT_SOURCE_DIR}/deps/uhttp -Duse_installed_dependencies=ON -G "${CMAKE_GENERATOR}") +message(STATUS,"End install uhttp!") ############################################################################### #######################Find/Install/Build azure_iot_sdks####################### ############################################################################### -#The azure_iot_sdks repo requires special treatment. Parson submodule must be initialized. +The azure_iot_sdks repo requires special treatment. Parson submodule must be initialized. + if(NOT EXISTS ${PROJECT_SOURCE_DIR}/deps/iot-sdk-c/deps/parson/README.md) execute_process( COMMAND git submodule update --init ${PROJECT_SOURCE_DIR}/deps/iot-sdk-c diff --git a/v1/deps/uhttp b/v1/deps/uhttp deleted file mode 160000 index 2a8dbf7c..00000000 --- a/v1/deps/uhttp +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 2a8dbf7c3079cfe949eaa3f9b0507d0c586d0dd0 From 235881d5f7d0594baa433b0cb059ba9123824baa Mon Sep 17 00:00:00 2001 From: hirosho Date: Tue, 1 May 2018 15:35:01 +0900 Subject: [PATCH 07/18] Delete submodule of deps/uhttp --- v1/core/dependencies.cmake | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/v1/core/dependencies.cmake b/v1/core/dependencies.cmake index 37078595..d6420610 100644 --- a/v1/core/dependencies.cmake +++ b/v1/core/dependencies.cmake @@ -8,9 +8,9 @@ findAndInstall(uamqp 1.0.25 ${PROJECT_SOURCE_DIR}/deps/uamqp ${PROJECT_SOURCE_DI ############################################################################### findAndInstall(umqtt 1.0.25 ${PROJECT_SOURCE_DIR}/deps/umqtt ${PROJECT_SOURCE_DIR}/deps/umqtt -Duse_installed_dependencies=ON -G "${CMAKE_GENERATOR}") -message(STATUS,"Starting install uhttp!") -findAndInstall(uhttp 1.0.25 ${PROJECT_SOURCE_DIR}/deps/uhttp ${PROJECT_SOURCE_DIR}/deps/uhttp -Duse_installed_dependencies=ON -G "${CMAKE_GENERATOR}") -message(STATUS,"End install uhttp!") +#message(STATUS,"Starting install uhttp!") +#findAndInstall(uhttp 1.0.25 ${PROJECT_SOURCE_DIR}/deps/uhttp ${PROJECT_SOURCE_DIR}/deps/uhttp -Duse_installed_dependencies=ON -G "${CMAKE_GENERATOR}") +#message(STATUS,"End install uhttp!") ############################################################################### #######################Find/Install/Build azure_iot_sdks####################### From b75798da1e5ae0974a650222c06524f4718aa418 Mon Sep 17 00:00:00 2001 From: hirosho Date: Tue, 1 May 2018 16:09:05 +0900 Subject: [PATCH 08/18] Re-add azure-uhttp-c --- .gitmodules | 3 +++ v1/deps/uhttp | 1 + 2 files changed, 4 insertions(+) create mode 160000 v1/deps/uhttp diff --git a/.gitmodules b/.gitmodules index 8d7b3808..71df6d12 100644 --- a/.gitmodules +++ b/.gitmodules @@ -28,3 +28,6 @@ [submodule "v1/deps/azure-uhttp-c"] path = v1/deps/uhttp url = https://github.com/Azure/azure-uhttp-c.git +[submodule "v1\\deps\\uhttp"] + path = v1\\deps\\uhttp + url = https://github.com/Azure/azure-uhttp-c.git diff --git a/v1/deps/uhttp b/v1/deps/uhttp new file mode 160000 index 00000000..1673c132 --- /dev/null +++ b/v1/deps/uhttp @@ -0,0 +1 @@ +Subproject commit 1673c1321db8076729675053df58c0738ac94103 From 2a4d54f84b664a805835ac2f9d6f6f824849a21a Mon Sep 17 00:00:00 2001 From: hirosho Date: Tue, 1 May 2018 17:20:20 +0900 Subject: [PATCH 09/18] Built-in uhttp in core/dependency.cmake --- v1/core/dependencies.cmake | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/v1/core/dependencies.cmake b/v1/core/dependencies.cmake index d6420610..ea393cbf 100644 --- a/v1/core/dependencies.cmake +++ b/v1/core/dependencies.cmake @@ -15,7 +15,7 @@ findAndInstall(umqtt 1.0.25 ${PROJECT_SOURCE_DIR}/deps/umqtt ${PROJECT_SOURCE_DI ############################################################################### #######################Find/Install/Build azure_iot_sdks####################### ############################################################################### -The azure_iot_sdks repo requires special treatment. Parson submodule must be initialized. +# The azure_iot_sdks repo requires special treatment. Parson submodule must be initialized. if(NOT EXISTS ${PROJECT_SOURCE_DIR}/deps/iot-sdk-c/deps/parson/README.md) execute_process( @@ -35,5 +35,13 @@ if(NOT EXISTS ${PROJECT_SOURCE_DIR}/deps/iot-sdk-c/deps/parson/README.md) if(${res}) message(FATAL_ERROR "Error pulling parson submodule: ${res}") endif() + execute_process( + COMMAND git submodule update --init deps/uhttp + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/deps/iot-sdk-c + RESULT_VARIABLE res + ) + if(${res}) + message(FATAL_ERROR "Error pulling uhttp submodule: ${res}") + endif() endif() findAndInstall(azure_iot_sdks 1.1.5 ${PROJECT_SOURCE_DIR}/deps/iot-sdk-c ${PROJECT_SOURCE_DIR}/deps/iot-sdk-c -Duse_installed_dependencies=ON -Duse_openssl=OFF -Dbuild_as_dynamic=ON -Dskip_samples=ON -G "${CMAKE_GENERATOR}") From 69972ad61ce1a498d81c18c418bfe36f138e0ef0 Mon Sep 17 00:00:00 2001 From: hirosho Date: Tue, 1 May 2018 19:55:02 +0900 Subject: [PATCH 10/18] Adding uhttp building --- v1/core/dependencies.cmake | 4 ++ v1/dependencies.cmake | 14 ++++++ v1/gatewayFunctions.cmake | 95 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 113 insertions(+) diff --git a/v1/core/dependencies.cmake b/v1/core/dependencies.cmake index ea393cbf..d46d3e63 100644 --- a/v1/core/dependencies.cmake +++ b/v1/core/dependencies.cmake @@ -45,3 +45,7 @@ if(NOT EXISTS ${PROJECT_SOURCE_DIR}/deps/iot-sdk-c/deps/parson/README.md) endif() endif() findAndInstall(azure_iot_sdks 1.1.5 ${PROJECT_SOURCE_DIR}/deps/iot-sdk-c ${PROJECT_SOURCE_DIR}/deps/iot-sdk-c -Duse_installed_dependencies=ON -Duse_openssl=OFF -Dbuild_as_dynamic=ON -Dskip_samples=ON -G "${CMAKE_GENERATOR}") + +message(STATUS,"Starting install uhttp!") +notFindAndInstall(uhttp ${PROJECT_SOURCE_DIR}/deps/uhttp ${PROJECT_SOURCE_DIR}/deps/uhttp -Duse_installed_dependencies=ON -G "${CMAKE_GENERATOR}") +message(STATUS,"End install uhttp!") diff --git a/v1/dependencies.cmake b/v1/dependencies.cmake index 68223b94..5a914e71 100644 --- a/v1/dependencies.cmake +++ b/v1/dependencies.cmake @@ -123,3 +123,17 @@ if(NOT EXISTS ${PROJECT_SOURCE_DIR}/deps/parson/parson.c) message(FATAL_ERROR "Error pulling parson submodule: ${res}") endif() endif() + +############################################################################### +#############################Init uhttp Submodule############################# +############################################################################### +if(NOT EXISTS ${PROJECT_SOURCE_DIR}/deps/uhttp/README.md) + execute_process( + COMMAND git submodule update --init ${PROJECT_SOURCE_DIR}/deps/uhttp + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + RESULT_VARIABLE res + ) + if(${res}) + message(FATAL_ERROR "Error pulling uhttp submodule: ${res}") + endif() +endif() diff --git a/v1/gatewayFunctions.cmake b/v1/gatewayFunctions.cmake index fa1e0973..876f36e2 100644 --- a/v1/gatewayFunctions.cmake +++ b/v1/gatewayFunctions.cmake @@ -179,4 +179,99 @@ function(findAndInstall libraryName version submoduleRootDirectory cmakeRootDire endif() endif() +endfunction() + +#Additional arguments to the specific projects cmake command may be specified after specifying the cmakeRootDirectory +function(notFindAndInstall libraryName submoduleRootDirectory cmakeRootDirectory) + + # If we have a build type passed in then we use it when building this + # project by passing it as the value for the --config argument to the + # "cmake --build" command. + if(CMAKE_BUILD_TYPE) + set(BUILD_CONFIGURATION --config ${CMAKE_BUILD_TYPE}) + else() + set(BUILD_CONFIGURATION "") + endif() + + if(${rebuild_deps} OR NOT ${libraryName}_FOUND) + # We are interested in doing a "find_package" only if we aren't going + # to rebuild dependencies anyway. + if(NOT ${rebuild_deps}) +# find_package(${libraryName} ${version} QUIET CONFIG HINTS ${dependency_install_prefix}) + endif() + if(${rebuild_deps} OR NOT ${libraryName}_FOUND) + if(NOT ${rebuild_deps} ) + message(STATUS "${libraryName} not found...") + endif() + + message(STATUS "Building ${libraryName}...") + + #If the library directory doesn't exist, pull submodules + if(NOT EXISTS "${cmakeRootDirectory}/CMakeLists.txt") + message("{cmakeRootDirectory}/CMakeLists.txt not found!") + execute_process( + COMMAND git submodule update --init --recursive ${submoduleRootDirectory} + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + RESULT_VARIABLE res + ) + endif() + if(${res}) + message(FATAL_ERROR "Error pulling submodules: ${res}") + endif() + + #Create the build directory to run cmake, and run cmake + + #generate comand + set(CMD cmake) + foreach(arg ${ARGN}) + set(CMD ${CMD} ${arg}) + endforeach() + + # If we have a build type passed in then we propagate it down the chain + # when building this dependency. + if(CMAKE_BUILD_TYPE) + set(CMD ${CMD} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}) + endif() + + if(DEFINED ${dependency_install_prefix}) + set(CMD ${CMD} -DCMAKE_INSTALL_PREFIX=${dependency_install_prefix}) + endif() + if(CMAKE_TOOLCHAIN_FILE) + set(CMD ${CMD} -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}) + endif() + set(CMD ${CMD} ../) + + # Re-create the build folder for making a fresh build + file(REMOVE_RECURSE ${cmakeRootDirectory}/build) + file(MAKE_DIRECTORY ${cmakeRootDirectory}/build) + + execute_process( + COMMAND ${CMD} + WORKING_DIRECTORY ${cmakeRootDirectory}/build + RESULT_VARIABLE res + ) + if(${res}) + message(FATAL_ERROR "Error running cmake for ${libraryName}: ${res}") + endif() + + # Install library + message(STATUS "Installing ${libraryName}. Please wait...") + execute_process( + COMMAND cmake --build . --target install ${BUILD_CONFIGURATION} -- ${cores} + WORKING_DIRECTORY ${cmakeRootDirectory}/build + RESULT_VARIABLE res + OUTPUT_FILE output.txt + ERROR_FILE error.txt + ) + if(${res}) + message(FATAL_ERROR "**ERROR installing ${libraryName}. See " + "${cmakeRootDirectory}/build/error.txt and " + "${cmakeRootDirectory}/build/output.txt.\n") + endif() + + #Attempt to find library with the REQUIRED option + # find_package(${libraryName} ${version} REQUIRED CONFIG HINTS ${dependency_install_prefix}) + endif() + endif() + endfunction() \ No newline at end of file From 8624a4af5922b9ae2636de84cf010865ef87ef80 Mon Sep 17 00:00:00 2001 From: hirosho Date: Wed, 2 May 2018 15:51:19 +0900 Subject: [PATCH 11/18] Add config file downloading but have some build problem --- v1/core/CMakeLists.txt | 4 +- v1/core/dependencies.cmake | 16 ++--- v1/core/src/gateway_createfromjson.c | 104 ++++++++++++++++++++++++--- v1/dependencies.cmake | 16 ++--- 4 files changed, 113 insertions(+), 27 deletions(-) diff --git a/v1/core/CMakeLists.txt b/v1/core/CMakeLists.txt index aa596b8d..b3cd148c 100644 --- a/v1/core/CMakeLists.txt +++ b/v1/core/CMakeLists.txt @@ -257,8 +257,8 @@ if(${enable_core_remote_module_support}) endif() -target_link_libraries(gateway parson nanomsg aziotsharedutil ${dynamic_loader_library} iothub_client) -target_link_libraries(gateway_static parson nanomsg aziotsharedutil ${dynamic_loader_library} iothub_client) +target_link_libraries(gateway parson nanomsg aziotsharedutil ${dynamic_loader_library} iothub_client uhttp) +target_link_libraries(gateway_static parson nanomsg aziotsharedutil ${dynamic_loader_library} iothub_client uhttp) target_link_libraries(module_host_static parson nanomsg aziotsharedutil ${dynamic_loader_library}) if(NOT WIN32) diff --git a/v1/core/dependencies.cmake b/v1/core/dependencies.cmake index d46d3e63..66409554 100644 --- a/v1/core/dependencies.cmake +++ b/v1/core/dependencies.cmake @@ -35,14 +35,14 @@ if(NOT EXISTS ${PROJECT_SOURCE_DIR}/deps/iot-sdk-c/deps/parson/README.md) if(${res}) message(FATAL_ERROR "Error pulling parson submodule: ${res}") endif() - execute_process( - COMMAND git submodule update --init deps/uhttp - WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/deps/iot-sdk-c - RESULT_VARIABLE res - ) - if(${res}) - message(FATAL_ERROR "Error pulling uhttp submodule: ${res}") - endif() +# execute_process( +# COMMAND git submodule update --init deps/uhttp +# WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/deps/iot-sdk-c +# RESULT_VARIABLE res +# ) +# if(${res}) +# message(FATAL_ERROR "Error pulling uhttp submodule: ${res}") +# endif() endif() findAndInstall(azure_iot_sdks 1.1.5 ${PROJECT_SOURCE_DIR}/deps/iot-sdk-c ${PROJECT_SOURCE_DIR}/deps/iot-sdk-c -Duse_installed_dependencies=ON -Duse_openssl=OFF -Dbuild_as_dynamic=ON -Dskip_samples=ON -G "${CMAKE_GENERATOR}") diff --git a/v1/core/src/gateway_createfromjson.c b/v1/core/src/gateway_createfromjson.c index 389b0fe8..fe1f276c 100644 --- a/v1/core/src/gateway_createfromjson.c +++ b/v1/core/src/gateway_createfromjson.c @@ -6,6 +6,8 @@ #include "azure_c_shared_utility/xlogging.h" #include "azure_c_shared_utility/macro_utils.h" #include "azure_c_shared_utility/httpapi.h" +#include "azure_c_shared_utility/socketio.h" +#include "azure_uhttp_c/uhttp.h" #include "azure_uamqp_c/amqp_definitions.h" #include "gateway.h" #include "parson.h" @@ -45,6 +47,42 @@ static PARSE_JSON_RESULT parse_json_internal(GATEWAY_PROPERTIES* out_properties, static void destroy_properties_internal(GATEWAY_PROPERTIES* properties); void gateway_destroy_internal(GATEWAY_HANDLE gw); +static void on_uhttp_connected(void* callback_ctx, HTTP_CALLBACK_REASON connect_result) +{ + (void)callback_ctx; + if (connect_result == HTTP_CALLBACK_REASON_OK) + { + LogInfo("HTTP Connected\r\n"); + } + else + { + LogInfo("HTTP Connection FAILED\r\n"); + } +} + +static void on_error(void* callback_ctx, HTTP_CALLBACK_REASON error_result) +{ + (void)callback_ctx; + (void)error_result; + LogError("Remote configuration get error via HTTPS"); +} + +static void on_uhttp_request_callback(void* callback_ctx, HTTP_CALLBACK_REASON request_result, const unsigned char* content, size_t content_len, unsigned int statusCode, HTTP_HEADERS_HANDLE responseHeadersHandle) +{ + (void)responseHeadersHandle; + (void)request_result; + (void)content_len; + (void)statusCode; + (void)content; + if (callback_ctx != NULL) + { + } + else + { + } +} + + void gateway_deviceTwinCallback(DEVICE_TWIN_UPDATE_STATE update_state, const unsigned char* payLoad, size_t size, void* userContextCallback) { GATEWAY_HANDLE gw = (GATEWAY_HANDLE)userContextCallback; @@ -70,17 +108,65 @@ void gateway_deviceTwinCallback(DEVICE_TWIN_UPDATE_STATE update_state, const uns if (gwConfig != NULL) { const char* configValue = json_object_get_string(gwConfig, "configuration"); if (configValue != NULL) { - HTTPAPI_RESULT httpResult = HTTPAPI_Init(); - if (httpResult == HTTPAPI_OK) { - HTTP_HANDLE handle = HTTPAPI_CreateConnection( configValue); - unsigned int statusCode; - HTTP_HEADERS_HANDLE responseHeaders = HTTPHeaders_Alloc(); - BUFFER_HANDLE responseBuffer = BUFFER_new(); - httpResult = HTTPAPI_ExecuteRequest(handle, HTTPAPI_REQUEST_GET, "", NULL, "", 0, &statusCode, responseHeaders, responseBuffer); + char* configValueHost = NULL; + const char* configValueRelPath = NULL; + int cvIndex = 0; + int cvLen = strlen(configValue); + const char cvProtocol[9] = { 'h','t','t','p','s',':','/','/','\0' }; + int cvPLen = strlen(cvProtocol); + while (cvIndex < cvPLen) { + if (cvProtocol[cvIndex] != configValue[cvIndex]) { + // error + } + cvIndex++; + } + if (cvIndex == cvPLen) { + configValueHost = &configValue[cvIndex]; + } + while (cvIndex < cvLen) { + if (configValue[cvIndex++] == '/') { + break; + } + } + if (cvIndex < cvLen) { + int hostLen = cvIndex - cvPLen; + configValueHost = (char*)malloc(hostLen); + memcpy(configValueHost, &configValue[cvPLen], hostLen-1); + configValueHost[hostLen - 1] = '\0'; + configValueRelPath = &configValue[cvIndex]; + } + if (configValueHost == NULL || configValueRelPath == NULL) { + LogError("Bad url!"); + } + else { + SOCKETIO_CONFIG socketConfig; + socketConfig.accepted_socket = NULL; + socketConfig.hostname = configValueHost; + socketConfig.port = 443; + + HTTP_CLIENT_HANDLE httpClient = uhttp_client_create(socketio_get_interface_description(), &socketConfig, on_error, userContextCallback); + HTTP_CLIENT_RESULT uhttpResult = uhttp_client_open(httpClient, socketConfig.hostname, socketConfig.port, on_uhttp_connected, userContextCallback); + uhttpResult = uhttp_client_execute_request(httpClient, HTTP_CLIENT_REQUEST_GET, configValueRelPath, NULL, NULL, 0, on_uhttp_request_callback, userContextCallback); + if (uhttpResult != HTTP_CLIENT_OK) { + LogError("Failed to get config file!"); + } +/* + HTTPAPI_RESULT httpResult = HTTPAPI_Init(); if (httpResult == HTTPAPI_OK) { - const unsigned char* receivedContent = BUFFER_u_char(responseBuffer); - LogInfo("Received - '%s'", receivedContent); + HTTP_HANDLE handle = HTTPAPI_CreateConnection(configValue); + unsigned int statusCode; + HTTP_HEADERS_HANDLE responseHeaders = HTTPHeaders_Alloc(); + BUFFER_HANDLE responseBuffer = BUFFER_new(); + httpResult = HTTPAPI_ExecuteRequest(handle, HTTPAPI_REQUEST_GET, "", NULL, "", 0, &statusCode, responseHeaders, responseBuffer); + if (httpResult == HTTPAPI_OK) { + const unsigned char* receivedContent = BUFFER_u_char(responseBuffer); + LogInfo("Received - '%s'", receivedContent); + } } + */ + } + if (configValueHost != NULL) { + free(configValueHost); } } } diff --git a/v1/dependencies.cmake b/v1/dependencies.cmake index 5a914e71..1feeac71 100644 --- a/v1/dependencies.cmake +++ b/v1/dependencies.cmake @@ -128,12 +128,12 @@ endif() #############################Init uhttp Submodule############################# ############################################################################### if(NOT EXISTS ${PROJECT_SOURCE_DIR}/deps/uhttp/README.md) - execute_process( - COMMAND git submodule update --init ${PROJECT_SOURCE_DIR}/deps/uhttp - WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} - RESULT_VARIABLE res - ) - if(${res}) - message(FATAL_ERROR "Error pulling uhttp submodule: ${res}") - endif() +# execute_process( +# COMMAND git submodule update --init ${PROJECT_SOURCE_DIR}/deps/uhttp +# WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} +# RESULT_VARIABLE res +# ) +# if(${res}) +# message(FATAL_ERROR "Error pulling uhttp submodule: ${res}") +# endif() endif() From 7b02d7099fdc61ddf8cbb5d983648eeb27cffad3 Mon Sep 17 00:00:00 2001 From: hirosho Date: Wed, 2 May 2018 16:02:17 +0900 Subject: [PATCH 12/18] Add some code but doesn't work --- v1/core/src/gateway_createfromjson.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/v1/core/src/gateway_createfromjson.c b/v1/core/src/gateway_createfromjson.c index fe1f276c..f569c8b6 100644 --- a/v1/core/src/gateway_createfromjson.c +++ b/v1/core/src/gateway_createfromjson.c @@ -150,6 +150,10 @@ void gateway_deviceTwinCallback(DEVICE_TWIN_UPDATE_STATE update_state, const uns if (uhttpResult != HTTP_CLIENT_OK) { LogError("Failed to get config file!"); } + else { + uhttp_client_dowork(httpClient); + + } /* HTTPAPI_RESULT httpResult = HTTPAPI_Init(); if (httpResult == HTTPAPI_OK) { From e0e10de936eb859622d38caaadb38a36c3306e36 Mon Sep 17 00:00:00 2001 From: hirosho Date: Mon, 4 Jun 2018 15:41:58 +0900 Subject: [PATCH 13/18] Add Remote Module Update Capability just code changed --- v1/core/CMakeLists.txt | 4 +- v1/core/inc/gateway.h | 4 + v1/core/src/gateway.c | 7 +- v1/core/src/gateway_createfromjson.c | 282 +++++++++++++++++++-------- v1/core/src/gateway_internal.c | 76 +++++++- v1/core/src/gateway_internal.h | 18 +- v1/dependencies.cmake | 14 -- 7 files changed, 301 insertions(+), 104 deletions(-) diff --git a/v1/core/CMakeLists.txt b/v1/core/CMakeLists.txt index b3cd148c..aa596b8d 100644 --- a/v1/core/CMakeLists.txt +++ b/v1/core/CMakeLists.txt @@ -257,8 +257,8 @@ if(${enable_core_remote_module_support}) endif() -target_link_libraries(gateway parson nanomsg aziotsharedutil ${dynamic_loader_library} iothub_client uhttp) -target_link_libraries(gateway_static parson nanomsg aziotsharedutil ${dynamic_loader_library} iothub_client uhttp) +target_link_libraries(gateway parson nanomsg aziotsharedutil ${dynamic_loader_library} iothub_client) +target_link_libraries(gateway_static parson nanomsg aziotsharedutil ${dynamic_loader_library} iothub_client) target_link_libraries(module_host_static parson nanomsg aziotsharedutil ${dynamic_loader_library}) if(NOT WIN32) diff --git a/v1/core/inc/gateway.h b/v1/core/inc/gateway.h index 4187c731..144a0d82 100644 --- a/v1/core/inc/gateway.h +++ b/v1/core/inc/gateway.h @@ -105,6 +105,8 @@ typedef struct GATEWAY_MODULES_ENTRY_TAG /** @brief The user-defined configuration object for the module */ const void* module_configuration; + + const char* module_version; } GATEWAY_MODULES_ENTRY; /** @brief Struct representing the properties that should be used when @@ -118,6 +120,8 @@ typedef struct GATEWAY_PROPERTIES_DATA_TAG /** @brief Vector of #GATEWAY_LINK_ENTRY objects. */ VECTOR_HANDLE gateway_links; + + JSON_Object* deployConfig; } GATEWAY_PROPERTIES; /** @brief Creates a gateway using a JSON configuration file as input diff --git a/v1/core/src/gateway.c b/v1/core/src/gateway.c index 12564fbf..c8bb5b69 100644 --- a/v1/core/src/gateway.c +++ b/v1/core/src/gateway.c @@ -196,7 +196,7 @@ GATEWAY_START_RESULT Gateway_Start(GATEWAY_HANDLE gw) EventSystem_ReportEvent(gw->event_system, gw, GATEWAY_STARTED); /*Codes_SRS_GATEWAY_17_013: [ This function shall return GATEWAY_START_SUCCESS upon completion. ]*/ result = GATEWAY_START_SUCCESS; - gw->runtime_status = 1; // 1 means runtime is running + gw->runtime_status = GATEWAY_RUNTIME_STATUS_RUNNING; // 1 means runtime is running } else { @@ -208,11 +208,8 @@ GATEWAY_START_RESULT Gateway_Start(GATEWAY_HANDLE gw) void Gateway_Destroy(GATEWAY_HANDLE gw) { - if (gw->iothub_client != NULL) { - IoTHubClient_Destroy(gw->iothub_client); - } gateway_destroy_internal(gw); - /*Codes_SRS_GATEWAY_17_019: [ The function shall destroy the module loader list. ]*/ + /*Codes_SRS_GATEWAY_17_019: [ The function shall destroy the module loader list. ]*/ ModuleLoader_Destroy(); } diff --git a/v1/core/src/gateway_createfromjson.c b/v1/core/src/gateway_createfromjson.c index f569c8b6..c5197d32 100644 --- a/v1/core/src/gateway_createfromjson.c +++ b/v1/core/src/gateway_createfromjson.c @@ -5,9 +5,7 @@ #include "azure_c_shared_utility/gballoc.h" #include "azure_c_shared_utility/xlogging.h" #include "azure_c_shared_utility/macro_utils.h" -#include "azure_c_shared_utility/httpapi.h" -#include "azure_c_shared_utility/socketio.h" -#include "azure_uhttp_c/uhttp.h" +#include "azure_c_shared_utility/httpapiex.h" #include "azure_uamqp_c/amqp_definitions.h" #include "gateway.h" #include "parson.h" @@ -16,6 +14,7 @@ #include "module_loaders/dynamic_loader.h" #include "gateway_internal.h" +#define GATEWAY_KEY "gateway" #define MODULES_KEY "modules" #define LOADERS_KEY "loaders" #define LOADER_KEY "loader" @@ -28,6 +27,8 @@ #define GATEWAY_KEY "gateway" #define GATEWAY_IOTHUB_CONNECTION_STRING_KEY "connection-string" #define GATEWAY_IOTHUB_TRANSPORT_KEY "transport" +#define GATEWAY_IOTHUB_MODULES_LOCAL_PATH "modules-local-path" +#define MODULE_REMOTE_URL "module.uri" #define LINKS_KEY "links" #define SOURCE_KEY "source" @@ -46,42 +47,7 @@ GATEWAY_HANDLE gateway_create_internal(const GATEWAY_PROPERTIES* properties, boo 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); - -static void on_uhttp_connected(void* callback_ctx, HTTP_CALLBACK_REASON connect_result) -{ - (void)callback_ctx; - if (connect_result == HTTP_CALLBACK_REASON_OK) - { - LogInfo("HTTP Connected\r\n"); - } - else - { - LogInfo("HTTP Connection FAILED\r\n"); - } -} - -static void on_error(void* callback_ctx, HTTP_CALLBACK_REASON error_result) -{ - (void)callback_ctx; - (void)error_result; - LogError("Remote configuration get error via HTTPS"); -} - -static void on_uhttp_request_callback(void* callback_ctx, HTTP_CALLBACK_REASON request_result, const unsigned char* content, size_t content_len, unsigned int statusCode, HTTP_HEADERS_HANDLE responseHeadersHandle) -{ - (void)responseHeadersHandle; - (void)request_result; - (void)content_len; - (void)statusCode; - (void)content; - if (callback_ctx != NULL) - { - } - else - { - } -} - +unsigned char* gateway_get_current_module_version(JSON_Object* json, const unsigned char* moduleName); void gateway_deviceTwinCallback(DEVICE_TWIN_UPDATE_STATE update_state, const unsigned char* payLoad, size_t size, void* userContextCallback) { @@ -128,46 +94,72 @@ void gateway_deviceTwinCallback(DEVICE_TWIN_UPDATE_STATE update_state, const uns break; } } - if (cvIndex < cvLen) { + if (0 < cvIndex&& cvIndex < cvLen) { int hostLen = cvIndex - cvPLen; configValueHost = (char*)malloc(hostLen); - memcpy(configValueHost, &configValue[cvPLen], hostLen-1); + memcpy(configValueHost, &configValue[cvPLen], hostLen - 1); configValueHost[hostLen - 1] = '\0'; - configValueRelPath = &configValue[cvIndex]; + configValueRelPath = &configValue[cvIndex-1]; } if (configValueHost == NULL || configValueRelPath == NULL) { LogError("Bad url!"); } else { - SOCKETIO_CONFIG socketConfig; - socketConfig.accepted_socket = NULL; - socketConfig.hostname = configValueHost; - socketConfig.port = 443; - - HTTP_CLIENT_HANDLE httpClient = uhttp_client_create(socketio_get_interface_description(), &socketConfig, on_error, userContextCallback); - HTTP_CLIENT_RESULT uhttpResult = uhttp_client_open(httpClient, socketConfig.hostname, socketConfig.port, on_uhttp_connected, userContextCallback); - uhttpResult = uhttp_client_execute_request(httpClient, HTTP_CLIENT_REQUEST_GET, configValueRelPath, NULL, NULL, 0, on_uhttp_request_callback, userContextCallback); - if (uhttpResult != HTTP_CLIENT_OK) { - LogError("Failed to get config file!"); + HTTP_HEADERS_HANDLE httpReqHeadersHandle, httpResHeadersHandle; + httpReqHeadersHandle = HTTPHeaders_Alloc(); + httpResHeadersHandle = HTTPHeaders_Alloc(); + if (httpReqHeadersHandle == NULL || httpResHeadersHandle == NULL) { + LogError("Failed to allocate http headers"); + if (httpReqHeadersHandle != NULL) HTTPHeaders_Free(httpReqHeadersHandle); + if (httpResHeadersHandle != NULL) HTTPHeaders_Free(httpResHeadersHandle); } else { - uhttp_client_dowork(httpClient); - - } -/* - HTTPAPI_RESULT httpResult = HTTPAPI_Init(); - if (httpResult == HTTPAPI_OK) { - HTTP_HANDLE handle = HTTPAPI_CreateConnection(configValue); - unsigned int statusCode; - HTTP_HEADERS_HANDLE responseHeaders = HTTPHeaders_Alloc(); - BUFFER_HANDLE responseBuffer = BUFFER_new(); - httpResult = HTTPAPI_ExecuteRequest(handle, HTTPAPI_REQUEST_GET, "", NULL, "", 0, &statusCode, responseHeaders, responseBuffer); - if (httpResult == HTTPAPI_OK) { - const unsigned char* receivedContent = BUFFER_u_char(responseBuffer); - LogInfo("Received - '%s'", receivedContent); + BUFFER_HANDLE reqContent = BUFFER_new(); + BUFFER_HANDLE resContent = BUFFER_new(); + if (reqContent == NULL || resContent == NULL) { + LogError("Failed to allocate http contents"); + if (reqContent != NULL) BUFFER_delete(reqContent); + if (resContent != NULL) BUFFER_delete(resContent); + HTTPHeaders_Free(httpReqHeadersHandle); + HTTPHeaders_Free(httpResHeadersHandle); + } + else { + unsigned int statusCode; + HTTPAPIEX_HANDLE httpapiexHandle = HTTPAPIEX_Create(configValueHost); + if (httpapiexHandle == NULL) { + LogError("Failed to create httpapiex handle"); + HTTPHeaders_Free(httpReqHeadersHandle); + HTTPHeaders_Free(httpResHeadersHandle); + BUFFER_delete(reqContent); + BUFFER_delete(resContent); + } + else { + HTTPAPIEX_RESULT httpApiExResult = HTTPAPIEX_ExecuteRequest(httpapiexHandle, HTTPAPI_REQUEST_GET, configValueRelPath, httpReqHeadersHandle, reqContent, &statusCode, httpResHeadersHandle, resContent); + + if (httpApiExResult == HTTPAPIEX_OK) { + if (statusCode == 200) { + const char* rc = BUFFER_u_char(resContent); + int rcLen = BUFFER_length(resContent); + STRING_HANDLE sh = STRING_construct_n(rc, rcLen); + const char* configJson = STRING_c_str(sh); + LogInfo("Received configuration - \n >>>%s<<<\n", configJson); + Gateway_UpdateFromJson(gw, rc); + } + else { + LogInfo("http access result %d", statusCode); + } + } + else { + LogError("http access failed %d", httpApiExResult); + } + HTTPAPIEX_Destroy(httpapiexHandle); + HTTPHeaders_Free(httpReqHeadersHandle); + HTTPHeaders_Free(httpResHeadersHandle); + BUFFER_delete(reqContent); + BUFFER_delete(resContent); + } } } - */ } if (configValueHost != NULL) { free(configValueHost); @@ -221,6 +213,7 @@ GATEWAY_HANDLE Gateway_CreateFromJson(const char* file_path) { properties->gateway_modules = NULL; properties->gateway_links = NULL; + properties->deployConfig = NULL; if ((parse_json_internal(properties, root_value) == PARSE_JSON_SUCCESS) && properties->gateway_modules != NULL && properties->gateway_links != NULL) { /*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.]*/ @@ -251,7 +244,8 @@ GATEWAY_HANDLE Gateway_CreateFromJson(const char* file_path) if (gwConfig != NULL) { const char* connectionString = json_object_get_string(gwConfig, GATEWAY_IOTHUB_CONNECTION_STRING_KEY); const char* iothubTransport = json_object_get_string(gwConfig, GATEWAY_IOTHUB_TRANSPORT_KEY); - if (connectionString != NULL && iothubTransport != NULL) { + const char* modulesLocalPath = json_object_get_string(gwConfig, GATEWAY_IOTHUB_MODULES_LOCAL_PATH); + if (connectionString != NULL && iothubTransport != NULL && modulesLocalPath!=NULL) { if (strcmp_i(iothubTransport, "AMQP") == 0) { transportProvider = AMQP_Protocol; @@ -266,6 +260,7 @@ GATEWAY_HANDLE Gateway_CreateFromJson(const char* file_path) IoTHubClient_SetDeviceTwinCallback(gw->iothub_client, gateway_deviceTwinCallback, gw); } } + gw->modules_local_path = STRING_construct(modulesLocalPath); // should be free } } } @@ -344,6 +339,15 @@ GATEWAY_UPDATE_FROM_JSON_RESULT Gateway_UpdateFromJson(GATEWAY_HANDLE gw, const } else { + bool isUpdating = true; + while (isUpdating) { + Lock(gw->update_lock); + if (gw->runtime_status != GATEWAY_RUNTIME_STATUS_UPDATING) { + isUpdating = false; + gw->runtime_status = GATEWAY_RUNTIME_STATUS_UPDATING; + } + Unlock(gw->update_lock); + } /* Codes_SRS_GATEWAY_JSON_04_005: [ The function shall use parson to parse the JSON string to a parson JSON_Value structure. ] */ JSON_Value *root_value = json_parse_string(json_content); @@ -366,6 +370,31 @@ GATEWAY_UPDATE_FROM_JSON_RESULT Gateway_UpdateFromJson(GATEWAY_HANDLE gw, const { properties->gateway_modules = NULL; properties->gateway_links = NULL; + properties->deployConfig = NULL; + JSON_Object *json_document = json_value_get_object(root_value); + char* deployConfig = NULL; + JSON_Value* dcJsonRoot = NULL; + if (json_document != NULL) + { + JSON_Value* gateway_config = json_object_get_value(json_document, GATEWAY_KEY); + JSON_Object* gateway_object = json_value_get_object(gateway_config); + // JSON_Object* gateway_object = json_object_get_object(json_document, GATEWAY_KEY); + deployConfig = json_object_get_string(gateway_object, "deploy-path"); + // const char* deployConfig = json_value_get_string (gateway_config, "deploy-path"); + if (deployConfig != NULL) { + FILE* fpDC = fopen(deployConfig, "r"); + if (fpDC == NULL) { + unsigned char* baseDCJsonString = "{\"modules\":[]}"; + dcJsonRoot = json_parse_string(baseDCJsonString); + properties->deployConfig = json_value_get_object(dcJsonRoot); + } + else { + fclose(fpDC); + dcJsonRoot = json_parse_file(deployConfig); + properties->deployConfig = json_value_get_object(dcJsonRoot); + } + } + } /* Codes_SRS_GATEWAY_JSON_04_007: [ The function shall traverse the JSON_Value object to initialize a GATEWAY_PROPERTIES instance. ] */ /* Codes_SRS_GATEWAY_JSON_04_011: [ The function shall be able to add just `modules`, just `links` or both. ] */ if (parse_json_internal(properties, root_value) != PARSE_JSON_SUCCESS) @@ -376,7 +405,7 @@ GATEWAY_UPDATE_FROM_JSON_RESULT Gateway_UpdateFromJson(GATEWAY_HANDLE gw, const } else { - + gw->deployConfig = properties->deployConfig; VECTOR_HANDLE modules_added_successfully = VECTOR_create(sizeof(GATEWAY_MODULES_ENTRY)); if (modules_added_successfully == NULL) { @@ -546,11 +575,18 @@ GATEWAY_UPDATE_FROM_JSON_RESULT Gateway_UpdateFromJson(GATEWAY_HANDLE gw, const VECTOR_destroy(modules_added_successfully); } } + if (deployConfig!=NULL&& dcJsonRoot != NULL) { + json_serialize_to_file(dcJsonRoot, deployConfig); + json_value_free(dcJsonRoot); + } destroy_properties_internal(properties); free(properties); } json_value_free(root_value); } + Lock(gw->update_lock); + gw->runtime_status = GATEWAY_RUNTIME_STATUS_UPDATED; + Unlock(gw->update_lock); } return result; @@ -580,7 +616,7 @@ static void destroy_properties_internal(GATEWAY_PROPERTIES* properties) } } -static PARSE_JSON_RESULT parse_loader(JSON_Object* loader_json, GATEWAY_MODULE_LOADER_INFO* loader_info) +static PARSE_JSON_RESULT parse_loader(JSON_Object* loader_json, GATEWAY_MODULE_LOADER_INFO* loader_info, JSON_Object* deployConfig) { PARSE_JSON_RESULT result; @@ -608,6 +644,98 @@ static PARSE_JSON_RESULT parse_loader(JSON_Object* loader_json, GATEWAY_MODULE_L // get entrypoint JSON_Value* entrypoint_json = json_object_get_value(loader_json, LOADER_ENTRYPOINT_KEY); + if (entrypoint_json != NULL) { + // download logic module + JSON_Object* entrypoint = json_value_get_object(entrypoint_json); + const char* modulePath = json_object_get_string(entrypoint, MODULE_REMOTE_URL); + const char* deployPath = json_object_get_string(entrypoint, MODULE_PATH_KEY); + JSON_Value* entrypointParrent_json = json_value_get_parent(entrypoint_json); + const char* newModuleVersion = NULL; + const char* moduleName = NULL; + if (entrypointParrent_json != NULL) { + JSON_Value* moduleEntry_json = json_value_get_parent(entrypointParrent_json); + JSON_Object* entryPointParent = json_value_get_object(moduleEntry_json); + newModuleVersion = json_object_get_string(entryPointParent, "version"); + moduleName = json_object_get_string(entryPointParent, "name"); + } + if (modulePath != NULL && (modulePath[0] == 'h'&&modulePath[1] == 't'&&modulePath[2] == 't'&&modulePath[3] == 'p' &&modulePath[4] == 's')) { + const char* currentModuleVersion = gateway_get_current_module_version(deployConfig, moduleName); + if (strcmp(newModuleVersion, currentModuleVersion) != 0) { + unsigned char* remoteModuleHost; + unsigned char* remoteModulePath; + int modulePathIndex = strlen("https://"); + int modulePathLength = strlen(modulePath); + int lastIndex = modulePathIndex; + while (modulePathIndex < modulePathLength) { + if (modulePath[modulePathIndex] == '/') { + break; + } + modulePathIndex++; + } + remoteModuleHost = (unsigned char*)malloc(modulePathIndex - lastIndex + 1); + memcpy(remoteModuleHost, &modulePath[lastIndex], modulePathIndex - lastIndex); + remoteModuleHost[modulePathIndex - lastIndex] = '\0'; + remoteModulePath = (unsigned char*)malloc(modulePathLength - modulePathIndex + 2); + memcpy(remoteModulePath, &modulePath[modulePathIndex], modulePathLength - modulePathIndex + 1); + remoteModulePath[modulePathLength - modulePathIndex + 1] = '\0'; + + HTTP_HEADERS_HANDLE httpReqHeadersHandle, httpResHeadersHandle; + httpReqHeadersHandle = HTTPHeaders_Alloc(); + httpResHeadersHandle = HTTPHeaders_Alloc(); + if (httpReqHeadersHandle == NULL || httpResHeadersHandle == NULL) { + LogError("Failed to allocate http headers"); + if (httpReqHeadersHandle != NULL) HTTPHeaders_Free(httpReqHeadersHandle); + if (httpResHeadersHandle != NULL) HTTPHeaders_Free(httpResHeadersHandle); + } + else { + BUFFER_HANDLE reqContent = BUFFER_new(); + BUFFER_HANDLE resContent = BUFFER_new(); + if (reqContent == NULL || resContent == NULL) { + LogError("Failed to allocate http contents"); + if (reqContent != NULL) BUFFER_delete(reqContent); + if (resContent != NULL) BUFFER_delete(resContent); + HTTPHeaders_Free(httpReqHeadersHandle); + HTTPHeaders_Free(httpResHeadersHandle); + } + else { + unsigned int statusCode; + HTTPAPIEX_HANDLE httpapiexHandle = HTTPAPIEX_Create(remoteModuleHost); + if (httpapiexHandle == NULL) { + LogError("Failed to create httpapiex handle"); + HTTPHeaders_Free(httpReqHeadersHandle); + HTTPHeaders_Free(httpResHeadersHandle); + BUFFER_delete(reqContent); + BUFFER_delete(resContent); + } + else { + HTTPAPIEX_RESULT httpApiExResult = HTTPAPIEX_ExecuteRequest(httpapiexHandle, HTTPAPI_REQUEST_GET, remoteModulePath, httpReqHeadersHandle, reqContent, &statusCode, httpResHeadersHandle, resContent); + if (httpApiExResult == HTTPAPIEX_OK) { + if (statusCode == 200) { + LogInfo("Received library - %s : %d byte", deployPath, BUFFER_length(resContent)); + FILE* fp = fopen(deployPath, "wb"); + fwrite(BUFFER_u_char(resContent), sizeof(unsigned char), BUFFER_length(resContent), fp); + fclose(fp); + } + else { + LogInfo("http access result %d", statusCode); + } + } + else { + LogError("http access failed %d", httpApiExResult); + } + HTTPAPIEX_Destroy(httpapiexHandle); + HTTPHeaders_Free(httpReqHeadersHandle); + HTTPHeaders_Free(httpResHeadersHandle); + BUFFER_delete(reqContent); + BUFFER_delete(resContent); + } + } + } + if (remoteModuleHost != NULL) free(remoteModuleHost); + if (remoteModulePath != NULL) free(remoteModulePath); + } + } + } loader_info->entrypoint = entrypoint_json == NULL ? NULL : loader->api->ParseEntrypointFromJson(loader, entrypoint_json); @@ -660,7 +788,7 @@ static PARSE_JSON_RESULT parse_json_internal(GATEWAY_PROPERTIES* out_properties, /*Codes_SRS_GATEWAY_JSON_17_009: [ For each module, the function shall call the loader's ParseEntrypointFromJson function to parse the entrypoint JSON. ]*/ 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) + if (parse_loader(loader_args, &loader_info, out_properties->deployConfig) != PARSE_JSON_SUCCESS) { result = PARSE_JSON_MISSING_OR_MISCONFIGURED_CONFIG; LogError("Failed to parse loader configuration."); @@ -674,12 +802,16 @@ static PARSE_JSON_RESULT parse_json_internal(GATEWAY_PROPERTIES* out_properties, /*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.]*/ JSON_Value *args = json_object_get_value(module, ARG_KEY); char* args_str = json_serialize_to_string(args); - + char* version_str = json_object_get_string(module, "version"); GATEWAY_MODULES_ENTRY entry = { module_name, loader_info, - args_str + args_str, + NULL }; + if (version_str != NULL) { + entry.module_version = version_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) @@ -788,10 +920,6 @@ static PARSE_JSON_RESULT parse_json_internal(GATEWAY_PROPERTIES* out_properties, LogError("JSON Configuration file is configured incorrectly or some other error occurred while parsing."); } - JSON_Object* gatewayObject = json_object_get_object(json_document, GATEWAY_KEY); - if (gatewayObject != NULL) { - - } } /*Codes_SRS_GATEWAY_JSON_14_006: [The function shall return NULL if the JSON_Value contains incomplete information.]*/ else diff --git a/v1/core/src/gateway_internal.c b/v1/core/src/gateway_internal.c index 12b5a86c..9465aacb 100644 --- a/v1/core/src/gateway_internal.c +++ b/v1/core/src/gateway_internal.c @@ -6,6 +6,7 @@ #include #include #include +#include #include @@ -149,6 +150,9 @@ GATEWAY_HANDLE gateway_create_internal(const GATEWAY_PROPERTIES* properties, boo /* For freeing up NULL ptrs in case of create failure */ memset(gateway, 0, sizeof(GATEWAY_HANDLE_DATA)); + gateway->update_lock = Lock_Init(); + gateway->runtime_status = GATEWAY_RUNTIME_STATUS_INITIALIZING; + /*Codes_SRS_GATEWAY_14_003: [This function shall create a new BROKER_HANDLE for the gateway representing this gateway's message broker. ]*/ gateway->broker = Broker_Create(); if (gateway->broker == NULL) @@ -278,9 +282,9 @@ static void reportedStateCallback(int status_code, void* userContextCallback) GATEWAY_HANDLE_DATA* gateway_handle = (GATEWAY_HANDLE*)userContextCallback; printf("Device Twin reported properties update completed with result: %d\r\n", status_code); - if (gateway_handle->runtime_status == 2) { - IoTHubClient_Destroy(gateway_handle->iothub_client); - } + Lock(gateway_handle->update_lock); + gateway_handle->runtime_status = GATEWAY_RUNTIME_STATUS_TERMINATED; + Unlock(gateway_handle->update_lock); } void gateway_destroy_internal(GATEWAY_HANDLE gw) @@ -291,9 +295,13 @@ void gateway_destroy_internal(GATEWAY_HANDLE gw) GATEWAY_HANDLE_DATA* gateway_handle = (GATEWAY_HANDLE_DATA*)gw; if (gateway_handle->iothub_client != NULL) { - gateway_handle->runtime_status = 2; + gateway_handle->runtime_status = GATEWAY_RUNTIME_STATUS_RUNNING; const char* reported_status = "{\"edgev1-runtime-status\":\"terminated\"}"; - IoTHubClient_SendReportedState(gateway_handle->iothub_client, reported_status, strlen(reported_status), reportedStateCallback, gateway_handle); + IoTHubClient_SendReportedState(gateway_handle->iothub_client, reported_status, strlen(reported_status), reportedStateCallback, gateway_handle); + while (gateway_handle->runtime_status != GATEWAY_RUNTIME_STATUS_TERMINATED) { + ThreadAPI_Sleep(1000); + } + IoTHubClient_Destroy(gateway_handle->iothub_client); } if (gateway_handle->event_system != NULL) { @@ -341,6 +349,9 @@ void gateway_destroy_internal(GATEWAY_HANDLE gw) Broker_Destroy(gateway_handle->broker); } + if (gateway_handle->update_lock != NULL) { + Lock_Deinit(gateway_handle->update_lock); + } free(gateway_handle); } else @@ -349,6 +360,53 @@ void gateway_destroy_internal(GATEWAY_HANDLE gw) } } +unsigned char* gateway_get_current_module_version(JSON_Object* json, const unsigned char* moduleName) +{ + if (json == NULL) { + return NULL; + } + unsigned char* version = NULL; + JSON_Array* modules = json_object_get_array(json, "modules"); + int size = json_array_get_count(modules); + for (int i = 0; i < size; i++) { + JSON_Object* moduleDC = json_array_get_object(modules, i); + unsigned char* rModuleName = json_object_get_string(moduleDC, "name"); + if (strcmp(moduleName, rModuleName) == 0) { + version = json_object_get_string(moduleDC, "version"); + break; + } + } + return version; +} + +static void set_module_version(JSON_Object* json, const unsigned char* moduleName, const unsigned char* moduleVersion) +{ + if (json == NULL) { + return NULL; + } + JSON_Object* moduleJson = NULL; + JSON_Array* modules = json_object_get_array(json, "modules"); + int size = json_array_get_count(modules); + for (int i = 0; i < size; i++) { + JSON_Object* moduleDC = json_array_get_object(modules, i); + unsigned char* rModuleName = json_object_get_string(moduleDC, "name"); + if (strcmp(moduleName, rModuleName) == 0) { + moduleJson = moduleDC; + break; + } + } + if (moduleJson == NULL) { + JSON_Value* newModuleVersion = json_value_init_object(); + JSON_Object* newModuleVersionObj = json_object(newModuleVersion); + json_object_set_string(newModuleVersionObj, "name", moduleName); + json_object_set_string(newModuleVersionObj, "version", moduleVersion); + json_array_append_value(modules, newModuleVersion); + } + else { + json_object_set_string(moduleJson,"version", moduleVersion); + } +} + bool checkIfModuleExists(GATEWAY_HANDLE_DATA* gateway_handle, const char* module_name) { MODULE_DATA** module_data = (MODULE_DATA**)VECTOR_find_if(gateway_handle->modules, module_name_find, module_name); @@ -390,8 +448,16 @@ MODULE_HANDLE gateway_addmodule_internal(GATEWAY_HANDLE_DATA* gateway_handle, co //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_entry->module_name); + if (moduleExist) { + const unsigned char* modulePreVersion = gateway_get_current_module_version(gateway_handle->deployConfig, module_entry->module_name); + if (modulePreVersion == NULL || (modulePreVersion != NULL & strcmp(modulePreVersion, module_entry->module_version) != 0)) { + Gateway_RemoveModuleByName(gateway_handle, module_entry->module_name); + moduleExist = false; + } + } if (!moduleExist) { + set_module_version(gateway_handle->deployConfig, module_entry->module_name, module_entry->module_version); MODULE_DATA * new_module_data = (MODULE_DATA*)malloc(sizeof(MODULE_DATA)); if (new_module_data == NULL) { diff --git a/v1/core/src/gateway_internal.h b/v1/core/src/gateway_internal.h index addc1c2f..8c510a72 100644 --- a/v1/core/src/gateway_internal.h +++ b/v1/core/src/gateway_internal.h @@ -28,6 +28,16 @@ typedef struct MODULE_DATA_TAG { MODULE_HANDLE module; } MODULE_DATA; +#define GATEWAY_RUNTIME_STATUS_VALUES \ + GATEWAY_RUNTIME_STATUS_INITIALIZING, \ + GATEWAY_RUNTIME_STATUS_UPDATING, \ + GATEWAY_RUNTIME_STATUS_UPDATED, \ + GATEWAY_RUNTIME_STATUS_RUNNING, \ + GATEWAY_RUNTIME_STATUS_TERMINATED, \ + GATEWAY_RUNTIME_STATUS_REPORTED + +DEFINE_ENUM(GATEWAY_RUNTIME_STATUS, GATEWAY_RUNTIME_STATUS_VALUES); + typedef struct GATEWAY_HANDLE_DATA_TAG { /** @brief Vector of MODULE_DATA modules that the Gateway must track */ @@ -44,7 +54,13 @@ typedef struct GATEWAY_HANDLE_DATA_TAG { IOTHUB_CLIENT_HANDLE iothub_client; - int runtime_status; // 0->initializing, 1->running, 2->terminating + GATEWAY_RUNTIME_STATUS runtime_status; // 0->initializing, 1->running, 2->terminating, 3->reported + + STRING_HANDLE modules_local_path; + + JSON_Object* deployConfig; + + LOCK_HANDLE update_lock; } GATEWAY_HANDLE_DATA; typedef struct LINK_DATA_TAG { diff --git a/v1/dependencies.cmake b/v1/dependencies.cmake index 1feeac71..68223b94 100644 --- a/v1/dependencies.cmake +++ b/v1/dependencies.cmake @@ -123,17 +123,3 @@ if(NOT EXISTS ${PROJECT_SOURCE_DIR}/deps/parson/parson.c) message(FATAL_ERROR "Error pulling parson submodule: ${res}") endif() endif() - -############################################################################### -#############################Init uhttp Submodule############################# -############################################################################### -if(NOT EXISTS ${PROJECT_SOURCE_DIR}/deps/uhttp/README.md) -# execute_process( -# COMMAND git submodule update --init ${PROJECT_SOURCE_DIR}/deps/uhttp -# WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} -# RESULT_VARIABLE res -# ) -# if(${res}) -# message(FATAL_ERROR "Error pulling uhttp submodule: ${res}") -# endif() -endif() From 4c5828509bd5b9d43219695d70811041ccf4ce36 Mon Sep 17 00:00:00 2001 From: hirosho Date: Tue, 5 Jun 2018 14:30:34 +0900 Subject: [PATCH 14/18] 1st release Remote update Don't download and execute same version --- v1/core/src/gateway_createfromjson.c | 17 +++++++++++------ .../hello_world/src/hello_world_remote.json | 13 +++++++++++++ 2 files changed, 24 insertions(+), 6 deletions(-) create mode 100644 v1/samples/hello_world/src/hello_world_remote.json diff --git a/v1/core/src/gateway_createfromjson.c b/v1/core/src/gateway_createfromjson.c index c5197d32..b0137c82 100644 --- a/v1/core/src/gateway_createfromjson.c +++ b/v1/core/src/gateway_createfromjson.c @@ -660,9 +660,9 @@ static PARSE_JSON_RESULT parse_loader(JSON_Object* loader_json, GATEWAY_MODULE_L } if (modulePath != NULL && (modulePath[0] == 'h'&&modulePath[1] == 't'&&modulePath[2] == 't'&&modulePath[3] == 'p' &&modulePath[4] == 's')) { const char* currentModuleVersion = gateway_get_current_module_version(deployConfig, moduleName); - if (strcmp(newModuleVersion, currentModuleVersion) != 0) { - unsigned char* remoteModuleHost; - unsigned char* remoteModulePath; + if (currentModuleVersion == NULL || (currentModuleVersion != NULL && strcmp(newModuleVersion, currentModuleVersion) != 0)) { + unsigned char* remoteModuleHost = NULL; + unsigned char* remoteModulePath = NULL; int modulePathIndex = strlen("https://"); int modulePathLength = strlen(modulePath); int lastIndex = modulePathIndex; @@ -713,8 +713,13 @@ static PARSE_JSON_RESULT parse_loader(JSON_Object* loader_json, GATEWAY_MODULE_L if (statusCode == 200) { LogInfo("Received library - %s : %d byte", deployPath, BUFFER_length(resContent)); FILE* fp = fopen(deployPath, "wb"); - fwrite(BUFFER_u_char(resContent), sizeof(unsigned char), BUFFER_length(resContent), fp); - fclose(fp); + if (fp != NULL) { + fwrite(BUFFER_u_char(resContent), sizeof(unsigned char), BUFFER_length(resContent), fp); + fclose(fp); + } + else { + LogError("Loaded module file can't be stored - %s", deployPath); + } } else { LogInfo("http access result %d", statusCode); @@ -737,7 +742,7 @@ static PARSE_JSON_RESULT parse_loader(JSON_Object* loader_json, GATEWAY_MODULE_L } } loader_info->entrypoint = entrypoint_json == NULL ? NULL : - loader->api->ParseEntrypointFromJson(loader, entrypoint_json); + loader->api->ParseEntrypointFromJson(loader, entrypoint_json); // if entrypoint_json is not NULL then loader_info->entrypoint must not be NULL if (entrypoint_json != NULL && loader_info->entrypoint == NULL) diff --git a/v1/samples/hello_world/src/hello_world_remote.json b/v1/samples/hello_world/src/hello_world_remote.json new file mode 100644 index 00000000..d15df571 --- /dev/null +++ b/v1/samples/hello_world/src/hello_world_remote.json @@ -0,0 +1,13 @@ +{ + "gateway": { + "connection-string": "<< IoT Hub Connection String >>", + "transport": "amqp", + "modules-local-path": "<< local path for remote updated module >>" + }, + "modules": [ + + ], + "links": [ + + ] +} From 40702f311a8645297e3b7a6b8d9b624dd8d42042 Mon Sep 17 00:00:00 2001 From: hirosho Date: Tue, 5 Jun 2018 19:25:51 +0900 Subject: [PATCH 15/18] Bug Fix --- .../hello_world/src/hello_world_win.json | 59 +++++++++---------- 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/v1/samples/hello_world/src/hello_world_win.json b/v1/samples/hello_world/src/hello_world_win.json index efd40707..b3e6e477 100644 --- a/v1/samples/hello_world/src/hello_world_win.json +++ b/v1/samples/hello_world/src/hello_world_win.json @@ -1,36 +1,33 @@ { "gateway": { - "connection-string": "HostName=egohtaiwan20180320.azure-devices.net;DeviceId=edgev1;SharedAccessKey=PeKkxAQDC9gkDvzLVXyRPDcK7fYFKd+9oKKcVbCUlNs=", + "connection-string": "<< IoT Hub Connection String >>", "transport": "amqp" }, - "modules": [ - { - "name": "logger", - "loader": { - "name": "native", - "entrypoint": { - "module.path": "..\\..\\..\\modules\\logger\\Debug\\logger.dll" + "modules": [{ + "name": "logger", + "loader": { + "name": "native", + "entrypoint": { + "module.path": "..\\..\\..\\modules\\logger\\Debug\\logger.dll" + } + }, + "args": { + "filename": "log.txt" + } + }, + { + "name": "hello_world", + "loader": { + "name": "native", + "entrypoint": { + "module.path": "..\\..\\..\\modules\\hello_world\\Debug\\hello_world.dll" + } + }, + "args": null } - }, - "args": { - "filename": "log.txt" - } - }, - { - "name": "hello_world", - "loader": { - "name": "native", - "entrypoint": { - "module.path": "..\\..\\..\\modules\\hello_world\\Debug\\hello_world.dll" - } - }, - "args": null - } - ], - "links": [ - { - "source": "hello_world", - "sink": "logger" - } - ] -} + ], + "links": [{ + "source": "hello_world", + "sink": "logger" + }] +} \ No newline at end of file From e932493c1a17e6925774363a862a42b5586a88ac Mon Sep 17 00:00:00 2001 From: hirosho Date: Thu, 7 Jun 2018 11:20:16 +0900 Subject: [PATCH 16/18] Add sample of remote update feature --- v1/samples/remote_update/README.md | 34 ++++++++++++++++ .../remote_update/src/device-twin-cloud.json | 4 ++ .../src/edge-config-cloud-lin.json | 40 +++++++++++++++++++ .../src/edge-config-cloud-win.json | 40 +++++++++++++++++++ .../src/hello_world_remote_edge.json | 13 ++++++ 5 files changed, 131 insertions(+) create mode 100644 v1/samples/remote_update/README.md create mode 100644 v1/samples/remote_update/src/device-twin-cloud.json create mode 100644 v1/samples/remote_update/src/edge-config-cloud-lin.json create mode 100644 v1/samples/remote_update/src/edge-config-cloud-win.json create mode 100644 v1/samples/remote_update/src/hello_world_remote_edge.json diff --git a/v1/samples/remote_update/README.md b/v1/samples/remote_update/README.md new file mode 100644 index 00000000..933f7a87 --- /dev/null +++ b/v1/samples/remote_update/README.md @@ -0,0 +1,34 @@ +# IoT Edge SDK Version 1 - Modules Remote Update Extension +This sample is to show how to use module remote update extension. +You can update modules on your edge device from cloud via device twin desired properties. + +## Step 1 +Setup Azure IoT Edge SDK ver 1 on your device by [https://github.com/Azure/iot-edge/blob/master/v1/doc/devbox_setup.md](https://github.com/Azure/iot-edge/blob/master/v1/doc/devbox_setup.md). + +## Step 2 +Regist edge device on IoT Hub and get connection string for deviceId. +Setup 'connection-string' and 'modules-local-path' in hello_world_remote_edge.json. +### connection-string +Replace '<< IoT Hub Connection String >>' part by connection string from IoT Hub. +### modules-local-path +Replace '<< local path for remote updated module >>' part by real directory on your edge device. Examples are following... +- Windows c:\\azureiot\\modules +- Linux /usr/local/azureiot/modules + +## Step 3 +Run samples/hello_world/hello_world using above hello_world_remote_edge.json as argument. + +## Step 4 +Store module library into cloud storage and prepare url to download it. +In the case of Azure blob, you create private container, store the library and get url with SAS token. +Then create edge-config.json refering edge-config-[lin|win].json. Modify '<< url for xxx >>' by above url. '<< local deploy path >>' should be same of the Step 2. +The version of each module should be changed as your valid one. +Then store edge-config.json into cloud storage and prepare url. + +## Step 5 +Update device twin desired properties refering device-twin-cloud.json with modify '<< edge-config-cloud.json url >>' using above edge-config.json on IoT Hub or Device Explorer. + +## Starting! +Device will receive device twin desired properties change then download configuration and libraries, deploy and execute them. + +Let's enjoy! diff --git a/v1/samples/remote_update/src/device-twin-cloud.json b/v1/samples/remote_update/src/device-twin-cloud.json new file mode 100644 index 00000000..d796fa8d --- /dev/null +++ b/v1/samples/remote_update/src/device-twin-cloud.json @@ -0,0 +1,4 @@ +"gateway": { + "configuration": "<< edge-config-cloud.json url >>" + +} \ No newline at end of file diff --git a/v1/samples/remote_update/src/edge-config-cloud-lin.json b/v1/samples/remote_update/src/edge-config-cloud-lin.json new file mode 100644 index 00000000..69f498a5 --- /dev/null +++ b/v1/samples/remote_update/src/edge-config-cloud-lin.json @@ -0,0 +1,40 @@ +{ + "gateway": { + "deploy-path": "<< local deploy path >>/deploy.json", + "version": "1.0.0" + }, + "modules": [ + { + "name": "logger", + "loader": { + "name": "native", + "entrypoint": { + "module.uri": "<< url for logger.so >>", + "module.path": "<< local deploy path >>/logger.so" + } + }, + "args": { + "filename": "log.txt" + }, + "version": "1.0.0" + }, + { + "name": "hello_world", + "loader": { + "name": "native", + "entrypoint": { + "module.uri": "<< url for hello_world.so >>", + "module.path": "<< local deploy path >>/hello_world.so" + } + }, + "args": null, + "version": "1.0.1" + } + ], + "links": [ + { + "source": "hello_world", + "sink": "logger" + } + ] +} diff --git a/v1/samples/remote_update/src/edge-config-cloud-win.json b/v1/samples/remote_update/src/edge-config-cloud-win.json new file mode 100644 index 00000000..5540409d --- /dev/null +++ b/v1/samples/remote_update/src/edge-config-cloud-win.json @@ -0,0 +1,40 @@ +{ + "gateway": { + "deploy-path": "<< local deploy path >>\\deploy.json", + "version": "1.0.0" + }, + "modules": [ + { + "name": "logger", + "loader": { + "name": "native", + "entrypoint": { + "module.uri": "<< url for logger.dll >>", + "module.path": "<< local deploy path >>\\logger.dll" + } + }, + "args": { + "filename": "log.txt" + }, + "version": "1.0.0" + }, + { + "name": "hello_world", + "loader": { + "name": "native", + "entrypoint": { + "module.uri": "<< url for hello_world", + "module.path": "<< local deploy path >>\\hello_world.dll" + } + }, + "args": null, + "version": "1.0.1" + } + ], + "links": [ + { + "source": "hello_world", + "sink": "logger" + } + ] +} diff --git a/v1/samples/remote_update/src/hello_world_remote_edge.json b/v1/samples/remote_update/src/hello_world_remote_edge.json new file mode 100644 index 00000000..1839b913 --- /dev/null +++ b/v1/samples/remote_update/src/hello_world_remote_edge.json @@ -0,0 +1,13 @@ +{ + "gateway": { + "connection-string": "<< IoT Hub Connection String >>", + "transport": "amqp", + "modules-local-path": "<< local path for remote updated module >>" + }, + "modules": [ + + ], + "links": [ + + ] +} \ No newline at end of file From 37b2b04ff0ed199bc98ffb7386a980c831e074da Mon Sep 17 00:00:00 2001 From: hirosho Date: Fri, 15 Jun 2018 18:10:32 +0900 Subject: [PATCH 17/18] gatewayFunctions.make missed --- v1/gatewayFunctions.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/v1/gatewayFunctions.cmake b/v1/gatewayFunctions.cmake index 876f36e2..ae0a4300 100644 --- a/v1/gatewayFunctions.cmake +++ b/v1/gatewayFunctions.cmake @@ -197,7 +197,7 @@ function(notFindAndInstall libraryName submoduleRootDirectory cmakeRootDirectory # We are interested in doing a "find_package" only if we aren't going # to rebuild dependencies anyway. if(NOT ${rebuild_deps}) -# find_package(${libraryName} ${version} QUIET CONFIG HINTS ${dependency_install_prefix}) +# find_package(${libraryName} QUIET CONFIG HINTS ${dependency_install_prefix}) endif() if(${rebuild_deps} OR NOT ${libraryName}_FOUND) if(NOT ${rebuild_deps} ) @@ -270,7 +270,7 @@ function(notFindAndInstall libraryName submoduleRootDirectory cmakeRootDirectory endif() #Attempt to find library with the REQUIRED option - # find_package(${libraryName} ${version} REQUIRED CONFIG HINTS ${dependency_install_prefix}) +# find_package(${libraryName} REQUIRED CONFIG HINTS ${dependency_install_prefix}) endif() endif() From 70dc73e856ef977dac88ab7273fa84f139f10b44 Mon Sep 17 00:00:00 2001 From: hirosho Date: Fri, 15 Jun 2018 18:30:39 +0900 Subject: [PATCH 18/18] remove uhttp installing --- v1/core/dependencies.cmake | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/v1/core/dependencies.cmake b/v1/core/dependencies.cmake index 66409554..38ec1107 100644 --- a/v1/core/dependencies.cmake +++ b/v1/core/dependencies.cmake @@ -8,10 +8,6 @@ findAndInstall(uamqp 1.0.25 ${PROJECT_SOURCE_DIR}/deps/uamqp ${PROJECT_SOURCE_DI ############################################################################### findAndInstall(umqtt 1.0.25 ${PROJECT_SOURCE_DIR}/deps/umqtt ${PROJECT_SOURCE_DIR}/deps/umqtt -Duse_installed_dependencies=ON -G "${CMAKE_GENERATOR}") -#message(STATUS,"Starting install uhttp!") -#findAndInstall(uhttp 1.0.25 ${PROJECT_SOURCE_DIR}/deps/uhttp ${PROJECT_SOURCE_DIR}/deps/uhttp -Duse_installed_dependencies=ON -G "${CMAKE_GENERATOR}") -#message(STATUS,"End install uhttp!") - ############################################################################### #######################Find/Install/Build azure_iot_sdks####################### ############################################################################### @@ -35,17 +31,5 @@ if(NOT EXISTS ${PROJECT_SOURCE_DIR}/deps/iot-sdk-c/deps/parson/README.md) if(${res}) message(FATAL_ERROR "Error pulling parson submodule: ${res}") endif() -# execute_process( -# COMMAND git submodule update --init deps/uhttp -# WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/deps/iot-sdk-c -# RESULT_VARIABLE res -# ) -# if(${res}) -# message(FATAL_ERROR "Error pulling uhttp submodule: ${res}") -# endif() endif() findAndInstall(azure_iot_sdks 1.1.5 ${PROJECT_SOURCE_DIR}/deps/iot-sdk-c ${PROJECT_SOURCE_DIR}/deps/iot-sdk-c -Duse_installed_dependencies=ON -Duse_openssl=OFF -Dbuild_as_dynamic=ON -Dskip_samples=ON -G "${CMAKE_GENERATOR}") - -message(STATUS,"Starting install uhttp!") -notFindAndInstall(uhttp ${PROJECT_SOURCE_DIR}/deps/uhttp ${PROJECT_SOURCE_DIR}/deps/uhttp -Duse_installed_dependencies=ON -G "${CMAKE_GENERATOR}") -message(STATUS,"End install uhttp!")