diff --git a/.github/workflows/cmake-linux.yml b/.github/workflows/cmake-linux.yml index 03a04dee23..236006ff12 100644 --- a/.github/workflows/cmake-linux.yml +++ b/.github/workflows/cmake-linux.yml @@ -70,11 +70,11 @@ jobs: - args: "-DOC_SECURITY_ENABLED=OFF -DOC_TCP_ENABLED=ON -DOC_IPV4_ENABLED=ON" # rep realloc on, ocf 1.1 on - args: "-DOC_REPRESENTATION_REALLOC_ENCODING_ENABLED=ON -DOC_VERSION_1_1_0_ENABLED=ON" - # everything off (dynamic allocation off, secure off, pki off, idd off, oscore off, well-known core resource off, software update off, maintenance resource off, /oic/res observable off, push notifications off, plgd-time off, introspection off, etag off) - - args: "-DOC_DYNAMIC_ALLOCATION_ENABLED=OFF -DOC_SECURITY_ENABLED=OFF -DOC_PKI_ENABLED=OFF -DOC_IDD_API_ENABLED=OFF -DOC_OSCORE_ENABLED=OFF -DOC_WKCORE_ENABLED=OFF -DOC_SOFTWARE_UPDATE_ENABLED=OFF -DOC_MNT_ENABLED=OFF -DOC_DISCOVERY_RESOURCE_OBSERVABLE_ENABLED=OFF -DOC_PUSH_ENABLED=OFF -DPLGD_DEV_TIME_ENABLED=OFF -DOC_INTROSPECTION_ENABLED=OFF -DOC_ETAG_ENABLED=OFF" + # everything off (dynamic allocation off, secure off, pki off, idd off, oscore off, well-known core resource off, software update off, maintenance resource off, /oic/res observable off, push notifications off, plgd-time off, introspection off, etag off, bridging off) + - args: "-DOC_DYNAMIC_ALLOCATION_ENABLED=OFF -DOC_SECURITY_ENABLED=OFF -DOC_PKI_ENABLED=OFF -DOC_IDD_API_ENABLED=OFF -DOC_OSCORE_ENABLED=OFF -DOC_WKCORE_ENABLED=OFF -DOC_SOFTWARE_UPDATE_ENABLED=OFF -DOC_MNT_ENABLED=OFF -DOC_DISCOVERY_RESOURCE_OBSERVABLE_ENABLED=OFF -DOC_PUSH_ENABLED=OFF -DPLGD_DEV_TIME_ENABLED=OFF -DOC_INTROSPECTION_ENABLED=OFF -DOC_ETAG_ENABLED=OFF -DOC_BRIDGE_ENABLED=OFF" uses: ./.github/workflows/unit-test-with-cfg.yml with: - build_args: -DOC_LOG_MAXIMUM_LOG_LEVEL=INFO -DOC_WKCORE_ENABLED=ON -DOC_SOFTWARE_UPDATE_ENABLED=ON -DOC_MNT_ENABLED=ON -DOC_DISCOVERY_RESOURCE_OBSERVABLE_ENABLED=ON -DOC_PUSH_ENABLED=ON -DPLGD_DEV_TIME_ENABLED=ON -DOC_ETAG_ENABLED=ON ${{ matrix.args }} + build_args: -DOC_LOG_MAXIMUM_LOG_LEVEL=INFO -DOC_WKCORE_ENABLED=ON -DOC_SOFTWARE_UPDATE_ENABLED=ON -DOC_MNT_ENABLED=ON -DOC_DISCOVERY_RESOURCE_OBSERVABLE_ENABLED=ON -DOC_PUSH_ENABLED=ON -DPLGD_DEV_TIME_ENABLED=ON -DOC_ETAG_ENABLED=ON -DOC_BRIDGE_ENABLED=ON ${{ matrix.args }} build_type: ${{ (github.event_name == 'workflow_dispatch' && inputs.build_type) || 'Debug' }} clang: ${{ github.event_name == 'workflow_dispatch' && inputs.clang }} coverage: false @@ -90,7 +90,7 @@ jobs: - args: "-DOC_DEBUG_ENABLED=ON -DOC_CLOUD_ENABLED=ON -DOC_COLLECTIONS_IF_CREATE_ENABLED=ON" uses: ./.github/workflows/unit-test-with-cfg.yml with: - build_args: -DOC_LOG_MAXIMUM_LOG_LEVEL=INFO -DOC_WKCORE_ENABLED=ON -DOC_SOFTWARE_UPDATE_ENABLED=ON -DOC_MNT_ENABLED=ON -DOC_DISCOVERY_RESOURCE_OBSERVABLE_ENABLED=ON -DOC_PUSH_ENABLED=ON -DPLGD_DEV_TIME_ENABLED=ON -DOC_ETAG_ENABLED=ON -DBUILD_MBEDTLS_FORCE_3_5_0=ON ${{ matrix.args }} + build_args: -DOC_LOG_MAXIMUM_LOG_LEVEL=INFO -DOC_WKCORE_ENABLED=ON -DOC_SOFTWARE_UPDATE_ENABLED=ON -DOC_MNT_ENABLED=ON -DOC_DISCOVERY_RESOURCE_OBSERVABLE_ENABLED=ON -DOC_PUSH_ENABLED=ON -DPLGD_DEV_TIME_ENABLED=ON -DOC_ETAG_ENABLED=ON -DOC_BRIDGE_ENABLED=ON -DBUILD_MBEDTLS_FORCE_3_5_0=ON ${{ matrix.args }} build_type: ${{ (github.event_name == 'workflow_dispatch' && inputs.build_type) || 'Debug' }} clang: ${{ github.event_name == 'workflow_dispatch' && inputs.clang }} coverage: false @@ -100,8 +100,8 @@ jobs: cmake_linux_preinstalled: uses: ./.github/workflows/unit-test-with-cfg.yml with: - # cloud on (ipv4+tcp on), collections create on, maintenance resource on, well-known core resource on, software update on, /oic/res observable on, push notification on, plgd-time on, etag on - build_args: -DOC_LOG_MAXIMUM_LOG_LEVEL=INFO -DOC_CLOUD_ENABLED=ON -DOC_COLLECTIONS_IF_CREATE_ENABLED=ON -DOC_MNT_ENABLED=ON -DOC_WKCORE_ENABLED=ON -DOC_SOFTWARE_UPDATE_ENABLED=ON -DOC_DISCOVERY_RESOURCE_OBSERVABLE_ENABLED=ON -DOC_PUSH_ENABLED=ON -DPLGD_DEV_TIME_ENABLED=ON -DOC_ETAG_ENABLED=ON + # cloud on (ipv4+tcp on), collections create on, maintenance resource on, well-known core resource on, software update on, /oic/res observable on, push notification on, plgd-time on, etag on, bridging on + build_args: -DOC_LOG_MAXIMUM_LOG_LEVEL=INFO -DOC_CLOUD_ENABLED=ON -DOC_COLLECTIONS_IF_CREATE_ENABLED=ON -DOC_MNT_ENABLED=ON -DOC_WKCORE_ENABLED=ON -DOC_SOFTWARE_UPDATE_ENABLED=ON -DOC_DISCOVERY_RESOURCE_OBSERVABLE_ENABLED=ON -DOC_PUSH_ENABLED=ON -DPLGD_DEV_TIME_ENABLED=ON -DOC_ETAG_ENABLED=ON -DOC_BRIDGE_ENABLED=ON build_type: ${{ (github.event_name == 'workflow_dispatch' && inputs.build_type) || 'Debug' }} clang: ${{ github.event_name == 'workflow_dispatch' && inputs.clang }} coverage: false @@ -138,7 +138,7 @@ jobs: # install_faketime: true uses: ./.github/workflows/unit-test-with-cfg.yml with: - build_args: -DOC_LOG_MAXIMUM_LOG_LEVEL=INFO -DOC_CLOUD_ENABLED=ON -DOC_COLLECTIONS_IF_CREATE_ENABLED=ON -DOC_MNT_ENABLED=ON -DOC_WKCORE_ENABLED=ON -DOC_SOFTWARE_UPDATE_ENABLED=ON -DOC_DISCOVERY_RESOURCE_OBSERVABLE_ENABLED=ON -DOC_PUSH_ENABLED=ON -DOC_RESOURCE_ACCESS_IN_RFOTM_ENABLED=ON -DPLGD_DEV_TIME_ENABLED=ON -DOC_ETAG_ENABLED=ON ${{ matrix.args }} + build_args: -DOC_LOG_MAXIMUM_LOG_LEVEL=INFO -DOC_CLOUD_ENABLED=ON -DOC_COLLECTIONS_IF_CREATE_ENABLED=ON -DOC_MNT_ENABLED=ON -DOC_WKCORE_ENABLED=ON -DOC_SOFTWARE_UPDATE_ENABLED=ON -DOC_DISCOVERY_RESOURCE_OBSERVABLE_ENABLED=ON -DOC_PUSH_ENABLED=ON -DOC_RESOURCE_ACCESS_IN_RFOTM_ENABLED=ON -DPLGD_DEV_TIME_ENABLED=ON -DOC_ETAG_ENABLED=ON -DOC_BRIDGE_ENABLED=ON ${{ matrix.args }} build_type: ${{ (github.event_name == 'workflow_dispatch' && inputs.build_type) || 'Debug' }} clang: ${{ ((github.event_name == 'workflow_dispatch' && inputs.clang) || matrix.clang) || false }} coverage: false diff --git a/.github/workflows/cmake-windows.yml b/.github/workflows/cmake-windows.yml index 6a6cce2da1..79df5c53e6 100644 --- a/.github/workflows/cmake-windows.yml +++ b/.github/workflows/cmake-windows.yml @@ -92,6 +92,7 @@ jobs: -D OC_RESOURCE_ACCESS_IN_RFOTM_ENABLED=ON -D PLGD_DEV_TIME_ENABLED=ON -D OC_ETAG_ENABLED=ON + -D OC_BRIDGE_ENABLED=ON -D OC_DEBUG_ENABLED=ON COMMAND_ERROR_IS_FATAL ANY ) @@ -184,6 +185,7 @@ jobs: -D OC_RESOURCE_ACCESS_IN_RFOTM_ENABLED=ON \ -D PLGD_DEV_TIME_ENABLED=ON \ -D OC_ETAG_ENABLED=ON \ + -D OC_BRIDGE_ENABLED=ON \ -D OC_DEBUG_ENABLED=ON \ ${{ matrix.build_args }} diff --git a/.github/workflows/coverity.yml b/.github/workflows/coverity.yml index 6e47acbf6c..065346766e 100644 --- a/.github/workflows/coverity.yml +++ b/.github/workflows/coverity.yml @@ -29,6 +29,7 @@ jobs: -DPLGD_DEV_TIME_ENABLED=ON -DOC_ETAG_ENABLED=ON -DOC_JSON_ENCODER_ENABLED=ON + -DOC_BRIDGE_ENABLED=ON -B ${{github.workspace}}/build - uses: vapier/coverity-scan-action@v1 diff --git a/.github/workflows/sonar-cloud-analysis.yml b/.github/workflows/sonar-cloud-analysis.yml index aa9a30ac30..3985faca7c 100644 --- a/.github/workflows/sonar-cloud-analysis.yml +++ b/.github/workflows/sonar-cloud-analysis.yml @@ -25,10 +25,10 @@ jobs: fail-fast: false matrix: include: - # cloud (ipv4+tcp) on, collection create on, push on, rfotm on - - build_args: "-DOC_CLOUD_ENABLED=ON -DOC_COLLECTIONS_IF_CREATE_ENABLED=ON -DOC_PUSH_ENABLED=ON -DOC_RESOURCE_ACCESS_IN_RFOTM_ENABLED=ON" - # security off, ipv4 on, collection create on, push on, rfotm on - - build_args: "-DOC_SECURITY_ENABLED=OFF -DOC_IPV4_ENABLED=ON -DOC_COLLECTIONS_IF_CREATE_ENABLED=ON -DOC_PUSH_ENABLED=ON" + # cloud (ipv4+tcp) on, collection create on, push on, rfotm on, bridging on + - build_args: "-DOC_CLOUD_ENABLED=ON -DOC_COLLECTIONS_IF_CREATE_ENABLED=ON -DOC_PUSH_ENABLED=ON -DOC_RESOURCE_ACCESS_IN_RFOTM_ENABLED=ON -DOC_BRIDGE_ENABLED=ON" + # security off, ipv4 on, collection create on, push on, rfotm on, bridging on + - build_args: "-DOC_SECURITY_ENABLED=OFF -DOC_IPV4_ENABLED=ON -DOC_COLLECTIONS_IF_CREATE_ENABLED=ON -DOC_PUSH_ENABLED=ON -DOC_BRIDGE_ENABLED=ON " # ipv6 dns on, oscore off, rep realloc on, json encoder on - build_args: "-DOC_DNS_LOOKUP_IPV6_ENABLED=ON -DOC_OSCORE_ENABLED=OFF -DOC_REPRESENTATION_REALLOC_ENCODING_ENABLED=ON -DOC_JSON_ENCODER_ENABLED=ON" # ipv4 on, tcp on, dynamic allocation off, rfotm on, push off (because it forces dynamic allocation) @@ -106,7 +106,7 @@ jobs: mkdir build && cd build # sonar-scanner currently cannot handle multi configuration configuration (ie. compilation of the same file with different defines), # so we enable as many features as possible so we get max. amount of code analysis - cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_VERBOSE_MAKEFILE=ON -DOC_CLOUD_ENABLED=ON -DOC_COLLECTIONS_IF_CREATE_ENABLED=ON -DOC_MNT_ENABLED=ON -DOC_WKCORE_ENABLED=ON -DOC_SOFTWARE_UPDATE_ENABLED=ON -DOC_DISCOVERY_RESOURCE_OBSERVABLE_ENABLED=ON -DOC_PUSH_ENABLED=ON -DOC_RESOURCE_ACCESS_IN_RFOTM_ENABLED=ON -DPLGD_DEV_TIME_ENABLED=ON -DOC_ETAG_ENABLED=ON -DOC_JSON_ENCODER_ENABLED=ON -DBUILD_TESTING=ON .. + cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_VERBOSE_MAKEFILE=ON -DOC_CLOUD_ENABLED=ON -DOC_COLLECTIONS_IF_CREATE_ENABLED=ON -DOC_MNT_ENABLED=ON -DOC_WKCORE_ENABLED=ON -DOC_SOFTWARE_UPDATE_ENABLED=ON -DOC_DISCOVERY_RESOURCE_OBSERVABLE_ENABLED=ON -DOC_PUSH_ENABLED=ON -DOC_RESOURCE_ACCESS_IN_RFOTM_ENABLED=ON -DPLGD_DEV_TIME_ENABLED=ON -DOC_ETAG_ENABLED=ON -DOC_JSON_ENCODER_ENABLED=ON -DOC_BRIDGE_ENABLED=ON -DBUILD_TESTING=ON .. cd .. # for files defined in multiple cmake targets, sonar-scanner seems to take the configuration from the first compilation of the file, # so we force client-server target to be compiled first so we get analysis of code with both OC_CLIENT and OC_SERVER enabled diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml index 5c2b689018..45335470ab 100644 --- a/.github/workflows/static-analysis.yml +++ b/.github/workflows/static-analysis.yml @@ -37,5 +37,5 @@ jobs: - name: Build with clang and analyze with clang-tidy run: | mkdir build && cd build - cmake -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER=clang -DOC_CLANG_TIDY_ENABLED=ON -DOC_CLOUD_ENABLED=ON -DOC_COLLECTIONS_IF_CREATE_ENABLED=ON -DOC_MNT_ENABLED=ON -DOC_WKCORE_ENABLED=ON -DOC_SOFTWARE_UPDATE_ENABLED=ON -DOC_DISCOVERY_RESOURCE_OBSERVABLE_ENABLED=ON -DOC_PUSH_ENABLED=ON -DOC_RESOURCE_ACCESS_IN_RFOTM_ENABLED=ON -DPLGD_DEV_TIME_ENABLED=ON -DOC_ETAG_ENABLED=ON -DBUILD_TESTING=OFF .. + cmake -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER=clang -DOC_CLANG_TIDY_ENABLED=ON -DOC_CLOUD_ENABLED=ON -DOC_COLLECTIONS_IF_CREATE_ENABLED=ON -DOC_MNT_ENABLED=ON -DOC_WKCORE_ENABLED=ON -DOC_SOFTWARE_UPDATE_ENABLED=ON -DOC_DISCOVERY_RESOURCE_OBSERVABLE_ENABLED=ON -DOC_PUSH_ENABLED=ON -DOC_RESOURCE_ACCESS_IN_RFOTM_ENABLED=ON -DPLGD_DEV_TIME_ENABLED=ON -DOC_ETAG_ENABLED=ON -DOC_BRIDGE_ENABLED=ON -DBUILD_TESTING=OFF .. cmake --build . diff --git a/.gitignore b/.gitignore index 5f084ad341..418860eada 100644 --- a/.gitignore +++ b/.gitignore @@ -106,12 +106,18 @@ service/resource-directory/client/unittest/obj #vscode setting files .vscode -.clang-tidy +# eclipse +.settings +.project +.cproject +# cmake build folder build +build4test -python/plgd_headers.config +# sonarqube +.scannerwork -# eclipse config file -.cproject -.project +# clang-tidy +.clang-tidy +compile_commands.json \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 9aafc2dd20..a0b02afe07 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -54,6 +54,7 @@ set(OC_IPV4_ENABLED OFF CACHE BOOL "Enable IPv4 support.") set(OC_DNS_LOOKUP_IPV6_ENABLED OFF CACHE BOOL "Enable IPv6 DNS lookup.") set(OC_PUSH_ENABLED OFF CACHE BOOL "Enable Push Notification.") set(OC_PUSHDEBUG_ENABLED OFF CACHE BOOL "Enable debug messages for Push Notification.") +set(OC_BRIDGE_ENABLED OFF CACHE BOOL "Enable Bridge Support.") set(OC_RESOURCE_ACCESS_IN_RFOTM_ENABLED OFF CACHE BOOL "Enable resource access in RFOTM.") set(OC_MEMORY_TRACE_ENABLED OFF CACHE BOOL "Enable memory tracing.") if (OC_DEBUG_ENABLED) @@ -310,6 +311,12 @@ if(OC_PUSH_ENABLED) set(OC_DYNAMIC_ALLOCATION_ENABLED ON) set(OC_COLLECTIONS_IF_CREATE_ENABLED ON) endif() + +if(OC_BRIDGE_ENABLED) + list(APPEND PUBLIC_COMPILE_DEFINITIONS "OC_BRIDGE") + set(OC_DYNAMIC_ALLOCATION_ENABLED ON) +endif() + if(OC_RESOURCE_ACCESS_IN_RFOTM_ENABLED) if(NOT OC_SECURITY_ENABLED) message(FATAL_ERROR "Cannot enable resource access in RFOTM without security") diff --git a/api/oc_bridge.c b/api/oc_bridge.c new file mode 100644 index 0000000000..e18f1aff38 --- /dev/null +++ b/api/oc_bridge.c @@ -0,0 +1,621 @@ +/****************************************************************** + * + * Copyright 2020 Intel Corporation + * Copyright 2023 ETRI Joo-Chul Kevin Lee (rune@etri.re.kr) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************/ + +#include "util/oc_features.h" + +#ifdef OC_HAS_FEATURE_BRIDGE + +#include "oc_bridge.h" +#include "oc_api.h" +#include "oc_core_res.h" +#include "oc_core_res_internal.h" +#include +#include "port/oc_log_internal.h" +#include "port/oc_assert.h" + +#ifdef OC_SECURITY +#include "oc_store.h" +#endif // OC_SECURITY + +OC_LIST(g_vods); +static oc_resource_t *g_vodlist_res; + +static void +print_vodlist(void) +{ +#if OC_DBG_IS_ENABLED + OC_PRINTF("\"vods\": ["); + oc_vods_t *print_vod_item = (oc_vods_t *)oc_list_head(g_vods); + while (print_vod_item) { + OC_PRINTF(" {"); + OC_PRINTF(" \"n\": \"%s\"", oc_string(print_vod_item->name)); + char di_uuid[OC_UUID_LEN]; + oc_uuid_to_str(&print_vod_item->di, di_uuid, OC_UUID_LEN); + OC_PRINTF(" \"di\": \"%s\"", di_uuid); + OC_PRINTF(" \"econame\": \"%s\"", oc_string(print_vod_item->econame)); + if (print_vod_item->next) { + OC_PRINTF(" },"); + } else { + OC_PRINTF(" }"); + } + print_vod_item = print_vod_item->next; + } +#endif +} + +static bool +oc_bridge_is_virtual_device(size_t device_index) +{ + oc_resource_t *r = oc_core_get_resource_by_index(OCF_D, device_index); + for (size_t i = 0; i < oc_string_array_get_allocated_size(r->types); ++i) { + if (strncmp(oc_string_array_get_item(r->types, i), "oic.d.virtual", + strlen("oic.d.virtual")) == 0) { + return true; + } + } + return false; +} + +#ifdef OC_SECURITY +static void +add_virtual_device_to_vods_list(const char *name, const oc_uuid_t *di, + const char *econame) +{ + oc_vods_t *vod = (oc_vods_t *)malloc(sizeof(oc_vods_t)); + oc_new_string(&vod->name, name, strlen(name)); + memcpy(&vod->di, di, sizeof(oc_uuid_t)); + oc_new_string(&vod->econame, econame, strlen(econame)); + + /* mark this vod is online... */ + oc_virtual_device_t *vod_mapping_item = oc_bridge_get_vod_mapping_info2(vod); + if (vod_mapping_item) { + vod_mapping_item->is_vod_online = true; + } else { + char uuid[OC_UUID_LEN]; + oc_uuid_to_str(&vod->di, uuid, OC_UUID_LEN); + OC_DBG("oc_bridge: failed to find Device whose ID is (%s)", uuid); + } + + oc_list_add(g_vods, vod); + + OC_DBG("=====> oc_bridge: adding %s [%s] from oic.r.vodlist", name, econame); + print_vodlist(); +} +#endif // OC_SECURITY + +/* + * remove VOD from `oic.r.vodlist` Resource + */ +static void +remove_virtual_device_from_vods_list(const oc_uuid_t *di) +{ + oc_vods_t *vod_item = (oc_vods_t *)oc_list_head(g_vods); + while (vod_item) { + if (memcmp(&vod_item->di, di, 16) == 0) { + + /* mark this vod is offline */ + oc_virtual_device_t *vod_mapping_item = + oc_bridge_get_vod_mapping_info2(vod_item); + if (vod_mapping_item) { + vod_mapping_item->is_vod_online = false; + } else { + char uuid[OC_UUID_LEN]; + oc_uuid_to_str(&vod_item->di, uuid, OC_UUID_LEN); + OC_ERR("oc_bridge: failed to find Device whose ID is (%s)", uuid); + } + + oc_list_remove(g_vods, vod_item); + OC_DBG("=====> oc_bridge: removing %s [%s] from oic.r.vodlist", + oc_string(vod_item->name), oc_string(vod_item->econame)); + oc_free_string(&vod_item->name); + oc_free_string(&vod_item->econame); + + free(vod_item); + break; + } + vod_item = vod_item->next; + } + print_vodlist(); +} + +static void +get_bridge(oc_request_t *request, oc_interface_mask_t iface_mask, + void *user_data) +{ + (void)user_data; + oc_rep_start_root_object(); + switch (iface_mask) { + case OC_IF_BASELINE: + oc_process_baseline_interface(request->resource); + /* fall through */ + case OC_IF_R: + oc_rep_set_array(root, vods); + char di_str[OC_UUID_LEN]; + oc_vods_t *vod_item = (oc_vods_t *)oc_list_head(g_vods); + while (vod_item) { + oc_rep_object_array_begin_item(vods); + oc_rep_set_text_string(vods, n, oc_string(vod_item->name)); + oc_uuid_to_str(&vod_item->di, di_str, OC_UUID_LEN); + oc_rep_set_text_string(vods, di, di_str); + oc_rep_set_text_string(vods, econame, oc_string(vod_item->econame)); + oc_rep_object_array_end_item(vods); + vod_item = vod_item->next; + } + oc_rep_close_array(root, vods); + break; + default: + break; + } + oc_rep_end_root_object(); + oc_send_response(request, OC_STATUS_OK); +} + +#ifdef OC_SECURITY +static void +_handle_owned_bridge(size_t device_index) +{ + /* + * walk all devices + * if device is unowned and a virtual device then call connection_init + * assumption all virtual devices have a higher device index than bridge + */ + for (size_t device = device_index + 1; device < oc_core_get_num_devices(); + ++device) { + if (oc_uuid_is_nil(oc_core_get_device_info(device)->di)) { + continue; + } + if (!oc_is_owned_device(device) && oc_bridge_is_virtual_device(device)) { + oc_connectivity_ports_t ports; + memset(&ports, 0, sizeof(ports)); + + OC_DBG( + "=====> Bridge is owned, VOD %zu connection is being initialized!!", + device); + + if (oc_connectivity_init(device, ports) < 0) { + oc_abort("error initializing connectivity for device"); + } + OC_DBG("======> oc_bridge: init connectivity for virtual device %zu", + device); + } + } +} + +static void +_handle_unowned_bridge(size_t device_index) +{ + /* + * Reset all virtual device information. + * walk all devices + * if device is a virtual device call reset and connection_shutdown + * reset the vod_map + * assumption all virtual devices have a higher device index than bridge + */ + for (size_t device = device_index + 1; device < oc_core_get_num_devices(); + ++device) { + if (oc_bridge_is_virtual_device(device)) { + oc_virtual_device_t *vod_mapping_item = + oc_bridge_get_vod_mapping_info(device); + if (vod_mapping_item) { + vod_mapping_item->is_vod_online = false; + } + + oc_reset_device(device); + oc_connectivity_shutdown(device); + } + } +#if 0 + /* TODO4ME: add way to remove virtual device before reseting the vod_map */ + oc_vod_map_reset(); + OC_DBG("oc_bridge: bridge reset, reseting all connected virtual devices"); +#endif +} + +static void +_handle_owned_vod(const oc_uuid_t *device_uuid, size_t device_index) +{ + /* + * if corresponding non-OCF device is still in paired + * while this VOD is offboard and onboard again. + * + * the device ID of corresponding OCF Device stored in non-OCF + * device cache could point wrong OCF Device.. + * + * => NOP!!!. onboard/offboard DON"T delete oc_device_info_t + * from g_oc_device_info[] array!! + */ + if (oc_bridge_is_virtual_device(device_index)) { + oc_device_info_t *device_info = oc_core_get_device_info(device_index); + oc_string_t econame; + oc_vod_map_get_econame(&econame, device_index); + add_virtual_device_to_vods_list(oc_string(device_info->name), device_uuid, + oc_string(econame)); + OC_DBG("======> oc_bridge: adding %s [%s] to oic.r.vodslist", + oc_string(device_info->name), oc_string(econame)); + } +} + +/* + * For bridging the doxm_owned_changed callback is responsible for two tasks: + * 1. Making sure unowned VODs connect or disconnect from the network based + * on the doxm status of the bridge device + * 2. Updating the oic.r.vodslist when ownership status of the virtual devices + * is change + */ +static void +doxm_owned_changed(const oc_uuid_t *device_uuid, size_t device_index, + bool owned, void *user_data) +{ + (void)user_data; + /* Bridge Device */ + if (g_vodlist_res->device == device_index) { + if (owned) { + _handle_owned_bridge(device_index); + } else { + /* Bridge device is unowned */ + /* + * Reset all virtual device information. + * walk all devices + * if device is a virtual device call reset and connection_shutdown + * reset the vod_map + * assumption all virtual devices have a higher device index than bridge + */ + _handle_unowned_bridge(device_index); + } + } else { + /* Device other than Bridge Device */ + if (owned) { + /* + * if corresponding non-OCF device is still in paired + * while this VOD is offboard and onboard again. + * + * the device ID of corresponding OCF Device stored in non-OCF + * device cache could point wrong OCF Device.. + * + * => NOP!!!. onboard/offboard DON"T delete oc_device_info_t + * from g_oc_device_info[] array!! + */ + _handle_owned_vod(device_uuid, device_index); + } else { + /* + * attempt to remove the unowned device from the vods_list if the uuid + * does not exist the on the vods list nothing will happen. + */ + remove_virtual_device_from_vods_list(device_uuid); + } + /* notify any observers that the vodslist has been updated */ + if (oc_is_owned_device(g_vodlist_res->device)) { + oc_notify_observers(g_vodlist_res); + } + } +} + +#ifdef OC_TEST +void bridge_owned_changed(const oc_uuid_t *device_uuid, size_t device_index, + bool owned, void *user_data); + +void +bridge_owned_changed(const oc_uuid_t *device_uuid, size_t device_index, + bool owned, void *user_data) +{ + doxm_owned_changed(device_uuid, device_index, owned, user_data); +} +#endif // OC_TEST +#endif // OC_SECURITY + +int +oc_bridge_add_bridge_device(const char *name, const char *spec_version, + const char *data_model_version, + oc_add_device_cb_t add_device_cb, void *data) +{ + int ret_value = oc_add_device("/oic/d", "oic.d.bridge", name, spec_version, + data_model_version, add_device_cb, data); + if (ret_value != 0) { + return ret_value; + } + + size_t bridge_device_index = oc_core_get_num_devices() - 1; + + g_vodlist_res = + oc_new_resource(name, "/bridge/vodlist", 1, bridge_device_index); + oc_resource_bind_resource_type(g_vodlist_res, "oic.r.vodlist"); + oc_resource_bind_resource_interface(g_vodlist_res, OC_IF_R); + oc_resource_set_default_interface(g_vodlist_res, OC_IF_R); + oc_resource_set_discoverable(g_vodlist_res, true); + /* + * TODO4ME <2023/7/24> do we need to make the oic.r.vodlist periodic + * observable? oc_resource_set_periodic_observable(g_vodlist_res, 30); + */ + oc_resource_set_request_handler(g_vodlist_res, OC_GET, get_bridge, NULL); + if (!oc_add_resource(g_vodlist_res)) { + return -1; + } + + /* + * - initialize VOD mapping list : `g_vod_mapping_list.vods` + * - initialize `g_vod_mapping_list.next_index` with `g_device_count` + * - load existing `g_vod_mapping_list` from disk + */ + oc_vod_map_init(); + +#ifdef OC_SECURITY + oc_add_ownership_status_cb(&doxm_owned_changed, NULL); +#endif // OC_SECURITY + return 0; +} + +size_t +oc_bridge_add_virtual_device(const uint8_t *virtual_device_id, + size_t virtual_device_id_size, const char *econame, + const char *uri, const char *rt, const char *name, + const char *spec_version, + const char *data_model_version, + oc_add_device_cb_t add_device_cb, void *data) +{ + /* + * add new VOD mapping entry (identified by vod_id) to the proper position of + * `oc_vod_mapping_list_t.vods` list, and update + * `g_vod_mapping_list.next_index` + * + * vd_index : index of `g_oc_device_info[]` which new Device for the VOD + * will be stored. + */ + size_t vd_index = oc_vod_map_add_mapping_entry( + virtual_device_id, virtual_device_id_size, econame); + + oc_add_new_device_t cfg = { + .uri = uri, + .rt = rt, + .name = name, + .spec_version = spec_version, + .data_model_version = data_model_version, + .add_device_cb = add_device_cb, + .add_device_cb_data = data, + }; + + /* + * add corresponding new Device (`oc_device_info_t`) to + * `g_oc_device_info[vd_index]` + */ + oc_device_info_t *device = oc_core_add_new_device_at_index(cfg, vd_index); + + if (!device) { + return 0; + } + + /* + * FIXME4ME <2023/12/11> oc_bridge_add_virtual_device() : do we need this + * code? + */ + if (oc_uuid_is_nil(device->piid)) { + oc_gen_uuid(&device->piid); +#ifdef OC_SECURITY + oc_sec_dump_unique_ids(vd_index); +#endif /* OC_SECURITY */ + } + /* + * According to the security specification: + * An Unowned VOD shall not accept DTLS connection attempts nor TLS connection + * attempts nor any other requests, including discovery requests, while the + * Bridge (that created that VOD) is Unowned. + * + * For that reason only init connectivity if the bridge device is owned or + * if the virtual device is already owned. + * + * The `doxm_owned_changed` callback is responsible for calling + * oc_connectivity_init and oc_connectivity_shutdown for virtual devices + * when the ownership of the bridge device changes. + */ +#ifdef OC_SECURITY + if (oc_is_owned_device(g_vodlist_res->device) || + oc_is_owned_device(vd_index)) { + oc_connectivity_ports_t ports; + memset(&ports, 0, sizeof(ports)); + if (oc_connectivity_init(vd_index, ports) < 0) { + oc_abort("error initializing connectivity for device"); + } + OC_DBG("=====> oc_bridge: init connectivity for virtual device %zu", + vd_index); + } +#else + oc_connectivity_ports_t ports; + memset(&ports, 0, sizeof(ports)); + if (oc_connectivity_init(vd_index, ports) < 0) { + oc_abort("error initializing connectivity for device"); + } +#endif /* OC_SECURITY */ + + oc_device_bind_resource_type(vd_index, "oic.d.virtual"); + +#ifdef OC_SECURITY + if (oc_is_owned_device(vd_index)) { + add_virtual_device_to_vods_list(name, oc_core_get_device_id(vd_index), + econame); + oc_notify_observers(g_vodlist_res); + } +#endif // OC_SECURITY + return vd_index; +} + +/* + * @brief add new vodentry for an existing VOD to "oic.r.vodlist:vods". + * This function is usually called after + * `oc_bridge_remove_virtual_device()` is called. This function DOES NOT add new + * Device to `g_oc_device_info[]`, but just re-registre existing VOD to + * "oic.r.vodlist:vods" list. + * + * @param device_index + * @return 0: success, -1: failure + */ +int +oc_bridge_add_vod(size_t device_index) +{ + oc_device_info_t *device; + oc_virtual_device_t *vod_mapping_item; + + vod_mapping_item = oc_bridge_get_vod_mapping_info(device_index); + if (!vod_mapping_item) { + OC_ERR("oc_bridge: failed to find VOD mapping entry which is corresponding " + "to the Device (device index: %zu)", + device_index); + return -1; + } + + device = oc_core_get_device_info(device_index); + if (!device) { + OC_ERR("oc_bridge: failed to find Device whose index is %zu", device_index); + return -1; + } + +#ifdef OC_SECURITY + if (oc_is_owned_device(g_vodlist_res->device) || + oc_is_owned_device(device_index)) { + oc_connectivity_ports_t ports; + memset(&ports, 0, sizeof(ports)); + if (oc_connectivity_init(device_index, ports) < 0) { + oc_abort("error initializing connectivity for device"); + } + OC_DBG("oc_bridge: init connectivity for virtual device %zu", device_index); + } +#else + oc_connectivity_ports_t ports; + memset(&ports, 0, sizeof(ports)); + if (oc_connectivity_init(device_index, ports) < 0) { + oc_abort("error initializing connectivity for device"); + } +#endif /* OC_SECURITY */ + +#ifdef OC_SECURITY + if (oc_is_owned_device(device_index)) { + add_virtual_device_to_vods_list(oc_string(device->name), + oc_core_get_device_id(device_index), + oc_string(vod_mapping_item->econame)); + oc_notify_observers(g_vodlist_res); + } +#endif // OC_SECURITY + + return 0; +} + +int +oc_bridge_remove_virtual_device(size_t device_index) +{ + if (oc_bridge_is_virtual_device(device_index)) { + remove_virtual_device_from_vods_list(oc_core_get_device_id(device_index)); + oc_connectivity_shutdown(device_index); + return 0; + } + return -1; +} + +int +oc_bridge_delete_virtual_device(size_t device_index) +{ + /* 1. remove this from oic.r.vodlist:vods */ + oc_bridge_remove_virtual_device(device_index); + + /* 2. destroy Device and remove from VOD mapping list */ + if (oc_bridge_is_virtual_device(device_index)) { + oc_uuid_t nil_uuid = { { 0 } }; + oc_set_immutable_device_identifier(device_index, &nil_uuid); + oc_core_remove_device_at_index(device_index); + oc_vod_map_remove_mapping_entry(device_index); + return 0; + } + return -1; +} + +size_t +oc_bridge_get_virtual_device_index(const uint8_t *virtual_device_id, + size_t virtual_device_id_size, + const char *econame) +{ + return oc_vod_map_get_vod_index(virtual_device_id, virtual_device_id_size, + econame); +} + +oc_virtual_device_t * +oc_bridge_get_vod_mapping_info(size_t virtual_device_index) +{ + return oc_vod_map_get_mapping_entry(virtual_device_index); +} + +oc_virtual_device_t * +oc_bridge_get_vod_mapping_info2(const oc_vods_t *vod) +{ + /* find corresponding VOD mapping entry */ + size_t device_index; + if (oc_core_get_device_index(vod->di, &device_index) < 0) { + char uuid[OC_UUID_LEN]; + oc_uuid_to_str(&vod->di, uuid, OC_UUID_LEN); + OC_ERR("oc_bridge: failed to find Device whose ID is (%s)", uuid); + return NULL; + } + + return oc_vod_map_get_mapping_entry(device_index); +} + +/* + * @brief return entry of "oic.r.vodlist:vods" list + * @param di Device id of the VOD to be returned + * @return VOD entry [oc_vods_t] + */ +oc_vods_t * +oc_bridge_get_vod(oc_uuid_t di) +{ + oc_vods_t *item; + + item = (oc_vods_t *)oc_list_head(g_vods); + + while (item) { + if (oc_uuid_is_equal(item->di, di)) { + return item; + } + item = item->next; + } + + return NULL; +} + +oc_vods_t * +oc_bridge_get_vod_list(void) +{ + return oc_list_head(g_vods); +} + +void +oc_bridge_print_device_list(void) +{ + size_t device_count = oc_core_get_num_devices(); + char di[OC_UUID_LEN]; + char piid[OC_UUID_LEN]; + + for (size_t i = 0; i < device_count; i++) { + oc_uuid_to_str(&oc_core_get_device_info(i)->di, di, OC_UUID_LEN); + oc_uuid_to_str(&oc_core_get_device_info(i)->piid, piid, OC_UUID_LEN); + printf("[ Device Index : %zu ]\n |_ Device ID: %s\n |_ PIID: %s\n |_ " + "Name: %s\n |_ ICV: %s\n |_ DMV: %s\n |_ Enable: %d\n", + i, di, piid, oc_string(oc_core_get_device_info(i)->name), + oc_string(oc_core_get_device_info(i)->icv), + oc_string(oc_core_get_device_info(i)->dmv), + !oc_core_get_device_info(i)->is_removed); + } +} + +#endif /* OC_HAS_FEATURE_BRIDGE */ diff --git a/api/oc_collection.c b/api/oc_collection.c index b9edda88ed..5f5ae3fa07 100644 --- a/api/oc_collection.c +++ b/api/oc_collection.c @@ -158,6 +158,27 @@ oc_collections_free_all(void) } } +#ifdef OC_HAS_FEATURE_BRIDGE +void +oc_collections_free_per_device(size_t device) +{ + oc_collection_t *col = (oc_collection_t *)oc_list_head(g_collections); + oc_collection_t *t; + + while (col) { + if (col->res.device == device) { + OC_DBG("found collection (\"%s\") for %zu", oc_string(col->res.name), + device); + t = col; + col = (oc_collection_t *)(col->res.next); + collection_free(t, false); + continue; + } + col = (oc_collection_t *)(col->res.next); + } +} +#endif + void oc_collection_notify_resource_changed(oc_collection_t *collection, bool discoveryBatchDispatch) @@ -609,6 +630,12 @@ oc_get_next_collection_with_link(const oc_resource_t *resource, collection = (oc_collection_t *)collection->res.next; } + /* + * FIXED <2024/01/23> oc_get_next_collection_with_link() : + * wrong search logic... ? (by Joo-Chul Kevin Lee) + */ +#if 0 + /* original code <2024/01/23> */ while (collection != NULL && collection->res.device == resource->device) { const oc_link_t *link = (oc_link_t *)oc_list_head(collection->links); while (link != NULL) { @@ -619,6 +646,20 @@ oc_get_next_collection_with_link(const oc_resource_t *resource, } collection = (oc_collection_t *)collection->res.next; } +#endif + + while (collection != NULL) { + if (collection->res.device == resource->device) { + const oc_link_t *link = (oc_link_t *)oc_list_head(collection->links); + while (link != NULL) { + if (link->resource == resource) { + return collection; + } + link = link->next; + } + } + collection = (oc_collection_t *)collection->res.next; + } return collection; } diff --git a/api/oc_collection_internal.h b/api/oc_collection_internal.h index e380c75e9a..a8326036dd 100644 --- a/api/oc_collection_internal.h +++ b/api/oc_collection_internal.h @@ -79,6 +79,9 @@ oc_collection_t *oc_collection_get_all(void); /** @brief Free all collections from the global list */ void oc_collections_free_all(void); +/** @brief Free all collections bound to a device */ +void oc_collections_free_per_device(size_t device); + /** @brief Iterate the global list of colletions and return the next collection * linked with the given resource */ oc_collection_t *oc_get_next_collection_with_link(const oc_resource_t *resource, diff --git a/api/oc_core_res.c b/api/oc_core_res.c index 04132afb5c..0fe70adebe 100644 --- a/api/oc_core_res.c +++ b/api/oc_core_res.c @@ -39,6 +39,13 @@ #include "util/oc_macros_internal.h" #include "util/oc_secure_string_internal.h" +#ifdef OC_HAS_FEATURE_BRIDGE +#include "security/oc_svr_internal.h" +#include "security/oc_ael_internal.h" +#include "oc_acl.h" +#include "oc_cred.h" +#endif + #ifdef OC_CLOUD #include "api/cloud/oc_cloud_resource_internal.h" #endif /* OC_CLOUD */ @@ -105,6 +112,7 @@ oc_core_init(void) #endif /* OC_DYNAMIC_ALLOCATION */ } +#ifdef OC_DYNAMIC_ALLOCATION static void oc_core_free_device_info_properties(oc_device_info_t *oc_device_info_item) { @@ -114,6 +122,7 @@ oc_core_free_device_info_properties(oc_device_info_t *oc_device_info_item) oc_free_string(&(oc_device_info_item->dmv)); } } +#endif /* OC_DYNAMIC_ALLOCATION */ void oc_core_shutdown(void) @@ -121,19 +130,10 @@ oc_core_shutdown(void) oc_platform_deinit(); uint32_t device_count = OC_ATOMIC_LOAD32(g_device_count); -#ifdef OC_DYNAMIC_ALLOCATION - if (g_oc_device_info != NULL) { -#endif /* OC_DYNAMIC_ALLOCATION */ - for (uint32_t i = 0; i < device_count; ++i) { - oc_device_info_t *oc_device_info_item = &g_oc_device_info[i]; - oc_core_free_device_info_properties(oc_device_info_item); - } -#ifdef OC_DYNAMIC_ALLOCATION - free(g_oc_device_info); - g_oc_device_info = NULL; - } -#endif /* OC_DYNAMIC_ALLOCATION */ + /* + * 1. Removed All Core Resources + */ #ifdef OC_DYNAMIC_ALLOCATION if (g_core_resources != NULL) { #endif /* OC_DYNAMIC_ALLOCATION */ @@ -142,12 +142,39 @@ oc_core_shutdown(void) (OC_NUM_CORE_LOGICAL_DEVICE_RESOURCES * device_count); ++i) { oc_resource_t *core_resource = &g_core_resources[i]; - oc_ri_free_resource_properties(core_resource); + +#ifdef OC_HAS_FEATURE_BRIDGE + if ((i < OC_NUM_CORE_PLATFORM_RESOURCES) || + (oc_core_get_device_info((i - OC_NUM_CORE_PLATFORM_RESOURCES) / + OC_NUM_CORE_LOGICAL_DEVICE_RESOURCES) + ->is_removed == false)) { +#endif /* OC_HAS_FEATURE_BRIDGE */ + oc_ri_free_resource_properties(core_resource); +#ifdef OC_HAS_FEATURE_BRIDGE + } +#endif /* OC_HAS_FEATURE_BRIDGE */ } #ifdef OC_DYNAMIC_ALLOCATION free(g_core_resources); g_core_resources = NULL; } + + /* + * 2. Removed All Devices + */ +#ifdef OC_DYNAMIC_ALLOCATION + if (g_oc_device_info != NULL) { +#endif /* OC_DYNAMIC_ALLOCATION */ + for (uint32_t i = 0; i < device_count; ++i) { + oc_device_info_t *oc_device_info_item = &g_oc_device_info[i]; + oc_core_free_device_info_properties(oc_device_info_item); + } +#ifdef OC_DYNAMIC_ALLOCATION + free(g_oc_device_info); + g_oc_device_info = NULL; + } +#endif /* OC_DYNAMIC_ALLOCATION */ + #endif /* OC_DYNAMIC_ALLOCATION */ OC_ATOMIC_STORE32(g_device_count, 0); } @@ -327,6 +354,32 @@ oc_create_device_resource(size_t device_count, const char *uri, const char *rt) } } +#ifdef OC_HAS_FEATURE_BRIDGE +static void +core_update_existing_device_data(size_t device_count, oc_add_new_device_t cfg) +{ + oc_gen_uuid(&g_oc_device_info[device_count].di); + oc_gen_uuid(&g_oc_device_info[device_count].piid); + + oc_new_string(&g_oc_device_info[device_count].name, cfg.name, + strlen(cfg.name)); + oc_new_string(&g_oc_device_info[device_count].icv, cfg.spec_version, + strlen(cfg.spec_version)); + oc_new_string(&g_oc_device_info[device_count].dmv, cfg.data_model_version, + strlen(cfg.data_model_version)); + g_oc_device_info[device_count].add_device_cb = cfg.add_device_cb; + g_oc_device_info[device_count].data = cfg.add_device_cb_data; +} + +static void +core_set_device_removed(size_t index, bool is_removed) +{ + g_oc_device_info[index].is_removed = is_removed; + + return; +} +#endif /* OC_HAS_FEATURE_BRIDGE */ + oc_device_info_t * oc_core_add_new_device(oc_add_new_device_t cfg) { @@ -389,9 +442,246 @@ oc_core_add_new_device(oc_add_new_device_t cfg) oc_abort("error initializing connectivity for device"); } +#ifdef OC_HAS_FEATURE_BRIDGE + core_set_device_removed(device_count, false); +#endif + + return &g_oc_device_info[device_count]; +} + +#ifdef OC_HAS_FEATURE_BRIDGE +oc_device_info_t * +oc_core_add_new_device_at_index(oc_add_new_device_t cfg, size_t index) +{ + assert(cfg.uri != NULL); + assert(cfg.rt != NULL); + assert(cfg.name != NULL); + assert(cfg.spec_version != NULL); + assert(cfg.data_model_version != NULL); + + uint32_t device_count = OC_ATOMIC_LOAD32(g_device_count); + +#if defined(OC_SECURITY) || defined(OC_SOFTWARE_UPDATE) + bool is_realloc = false; +#endif + + if (index > device_count) { + OC_ERR( + "designated device index (%d) is bigger than current number of all Devices", + g_device_count); + return NULL; + } else if (index < device_count) { + /* + * If an existing Device is being replaced with new Device.. + * - check if the Device on designated `index` is still alive or removed + * before. + */ + if (g_oc_device_info[index].is_removed == false) { + OC_ERR("Trying to replace existing normal Device with new one...! \ + To insert new Device in the middle of the g_oc_device_info[], \ + remove the existing one first"); + return NULL; + } + + /* store new `oc_device_info_t` entry to existing memory slot */ + core_update_existing_device_data(index, cfg); + device_count = (uint32_t)index; + } else if (index == device_count) { + /* + * if `index` is same as the next normal index of Device, + * follow normal procedure. + */ + bool exchanged = false; + while (!exchanged) { +#ifndef OC_DYNAMIC_ALLOCATION + if (device_count == OC_MAX_NUM_DEVICES) { + OC_ERR("device limit reached"); + return NULL; + } +#endif /* !OC_DYNAMIC_ALLOCATION */ + if ((uint64_t)device_count == (uint64_t)MIN(SIZE_MAX, UINT32_MAX)) { + OC_ERR("limit of value type of g_device_count reached"); + return NULL; + } + /* store (device_count+1) to g_device_count */ + OC_ATOMIC_COMPARE_AND_SWAP32(g_device_count, device_count, + device_count + 1, exchanged); + } + + /* extend memory allocated to `g_oc_device_info` to add new Device + * and add new `oc_device_info_t` entry */ + core_update_device_data(device_count, cfg); + +#if defined(OC_SECURITY) || defined(OC_SOFTWARE_UPDATE) + is_realloc = true; +#endif + } + + /* Construct device resource */ + oc_create_device_resource(device_count, cfg.uri, cfg.rt); + + if (oc_get_con_res_announced()) { + /* Construct oic.wk.con resource for this device. */ + oc_create_con_resource(device_count); + } + + oc_create_discovery_resource(device_count); + +#ifdef OC_WKCORE + oc_create_wkcore_resource(device_count); +#endif /* OC_WKCORE */ + +#ifdef OC_INTROSPECTION + oc_create_introspection_resource(device_count); +#endif /* OC_INTROSPECTION */ + +#ifdef OC_MNT + oc_create_maintenance_resource(device_count); +#endif /* OC_MNT */ +#if defined(OC_CLIENT) && defined(OC_SERVER) && defined(OC_CLOUD) + oc_create_cloudconf_resource(device_count); +#endif /* OC_CLIENT && OC_SERVER && OC_CLOUD */ + +#ifdef OC_HAS_FEATURE_PUSH + oc_create_pushconf_resource(device_count); + oc_create_pushreceiver_resource(device_count); +#endif /* OC_HAS_FEATURE_PUSH */ + +#ifdef OC_SECURITY + /* + * Do what "main_init_resources()" does for all Devices here... + * refer to "main_init_resources()" + */ + if ((g_device_count == (device_count + 1)) && is_realloc) { + /* realloc memory and populate SVR Resources + * only if new Device is attached to the end of `g_oc_device_info[]` */ + oc_sec_svr_create_new_device(device_count, true); + } else { + oc_sec_svr_create_new_device(device_count, false); + } +#endif /* OC_SECURITY */ + +#ifdef OC_SOFTWARE_UPDATE + /* + * Do what "main_init_resources()" does for all Devices here... + * refer to "main_init_resources()" + */ + if ((g_device_count == (device_count + 1)) && is_realloc) { + /* realloc memory and populate SVR Resources + * only if new Device is attached to the end of `g_oc_device_info[]` */ + oc_swupdate_create_new_device(device_count, true); + } else { + oc_swupdate_create_new_device(device_count, false); + } +#endif /* OC_SOFTWARE_UPDATE */ + +#ifdef OC_SECURITY + /* + * Do what "main_load_resources()" does for all Devices here... + * refer to "main_load_resources()" + */ + oc_sec_svr_init_new_device(device_count); +#endif /* OC_SECURITY */ + +#ifdef OC_SOFTWARE_UPDATE + /* + * Do what "main_load_resources()" does for all Devices here... + * refer to "main_load_resources()" + */ + OC_DBG("oc_core_add_new_device_at_index(): loading swupdate(%d)", + device_count); + oc_swupdate_load(device_count); +#endif /* OC_SOFTWARE_UPDATE */ + + core_set_device_removed(device_count, false); return &g_oc_device_info[device_count]; } +static void +core_delete_app_resources_per_device(size_t index) +{ + oc_ri_delete_app_resources_per_device(index); + + return; +} + +bool +oc_core_remove_device_at_index(size_t index) +{ + if (index >= g_device_count) { + OC_ERR("Device index value is out of valid range! : \ + Device index %zu, current Device count %d", + index, g_device_count); + return false; + } + +#ifdef OC_SECURITY + oc_reset_device(index); + /* + * oc_sec_sdi_clear(oc_sec_sdi_get(index)); => already done in + * oc_reset_device() + * oc_sec_ael_free_device(index); => already done in + * oc_reset_device() + * oc_sec_cred_clear(index, NULL, NULL); => already done in + * oc_reset_device() + * oc_sec_acl_clear(index, NULL, NULL); => already done in + * oc_reset_device() + */ +#endif /* OC_SECURITY */ + + /* 1. remove core Resources mapped to this Device */ + for (size_t i = OC_NUM_CORE_PLATFORM_RESOURCES + + (OC_NUM_CORE_LOGICAL_DEVICE_RESOURCES * index); + i < OC_NUM_CORE_PLATFORM_RESOURCES + + (OC_NUM_CORE_LOGICAL_DEVICE_RESOURCES * (index + 1)); + ++i) { + oc_resource_t *core_resource = &g_core_resources[i]; + oc_ri_free_resource_properties(core_resource); + memset(core_resource, 0, sizeof(oc_resource_t)); + } + +#ifdef OC_HAS_FEATURE_PUSH + /* + * TODO4ME <2024/01/23> oc_core_remove_device_at_index() : + * - make function to delete receivers object list per VOD + */ +#if 0 + oc_push_free(); +#endif +#endif /* OC_HAS_FEATURE_PUSH */ + + /* 2. remove all application Resources (including collections) mapped to this + * Device */ + /* + * TODO4ME <2023/12/11> oc_core_remove_device_at_index() : do we need to + * delete observer too? (e.g. oc_ri_reset()) + */ + core_delete_app_resources_per_device(index); + + /* 3. clean all Properties of this Device */ + oc_core_free_device_info_properties(&g_oc_device_info[index]); + memset(&g_oc_device_info[index], 0, sizeof(oc_device_info_t)); + + /* 4. mark this Device is removed */ + core_set_device_removed(index, true); + + return true; +} + +int +oc_core_get_device_index(oc_uuid_t di, size_t *device) +{ + for (size_t i = 0; i < g_device_count; i++) { + if (oc_uuid_is_equal(g_oc_device_info[i].di, di)) { + *device = i; + return 0; + } + } + return -1; +} + +#endif /* OC_HAS_FEATURE_BRIDGE */ + static void oc_device_bind_rt(size_t device_index, const char *rt) { diff --git a/api/oc_core_res_internal.h b/api/oc_core_res_internal.h index 02b6a993f8..9132e55c74 100644 --- a/api/oc_core_res_internal.h +++ b/api/oc_core_res_internal.h @@ -55,6 +55,28 @@ void oc_core_shutdown(void); */ oc_device_info_t *oc_core_add_new_device(oc_add_new_device_t cfg); +#ifdef OC_HAS_FEATURE_BRIDGE +/** + * @brief Add new device to the position designeted by `index` of Device array + * (g_oc_device_info[]) + * + * @param cfg device configuration + * @param index index of `g_oc_device_info[]` + * @return oc_device_info_t* the device information + */ +oc_device_info_t *oc_core_add_new_device_at_index(oc_add_new_device_t cfg, + size_t index); + +/** + * @brief Remove existing device at "index" position + * + * @param index Device index + * @return true success + * @return false failure + */ +bool oc_core_remove_device_at_index(size_t index); +#endif /* OC_HAS_FEATURE_BRIDGE */ + /** * @brief encode the interfaces with the cbor (payload) encoder * diff --git a/api/oc_push.c b/api/oc_push.c index ea0cc6a4bc..6543eb09b1 100644 --- a/api/oc_push.c +++ b/api/oc_push.c @@ -2286,9 +2286,10 @@ oc_push_init(void) /* * clean up push related data structure - * - for push configuration Resource: they are cleaned when all app Resources - * are removed (see oc_main_shutdown()) - * - for push receivers Resource: free in this function + * - Push configuration Resource, Push Receiver Resource: + * they are cleaned when all app Resources are removed + * (see oc_ri_shutdown()) + * - for push receivers Resource: free receiver object list here */ void oc_push_free(void) @@ -2300,7 +2301,9 @@ oc_push_free(void) _purge_recv_obj_list(recvs_instance); OC_PUSH_DBG("free push receiver Resource (device: %zu)... ", recvs_instance->resource->device); +#if 0 oc_delete_resource(recvs_instance->resource); +#endif oc_memb_free(&g_recvs_instance_memb, recvs_instance); recvs_instance = (oc_recvs_t *)oc_list_pop(g_recvs_list); } diff --git a/api/oc_ri.c b/api/oc_ri.c index 502374259c..117a08d1e6 100644 --- a/api/oc_ri.c +++ b/api/oc_ri.c @@ -473,8 +473,79 @@ oc_ri_get_app_resource_by_uri(const char *uri, size_t uri_len, size_t device) #endif /* OC_COLLECTIONS */ return NULL; } + #endif /* OC_SERVER */ +#ifdef OC_HAS_FEATURE_BRIDGE +oc_resource_t * +oc_ri_get_app_resource_by_device(size_t device, bool reset) +{ + static oc_resource_t *rsc; +#ifdef OC_COLLECTIONS + static oc_resource_t *coll_rsc; +#endif + oc_resource_t *found; + + if (reset) { + rsc = oc_ri_get_app_resources(); +#ifdef OC_COLLECTIONS + coll_rsc = oc_collection_get_collections(); +#endif + } + +#ifdef OC_COLLECTIONS + while (rsc || coll_rsc) { + if (rsc && (rsc->device == device)) { + found = rsc; + rsc = rsc->next; + return found; + } else { + if (rsc) + rsc = rsc->next; + if (coll_rsc && (coll_rsc->device == device)) { + found = coll_rsc; + coll_rsc = coll_rsc->next; + return found; + } else if (coll_rsc) { + coll_rsc = coll_rsc->next; + } + } + } +#else + while (rsc) { + if (rsc->device == device) { + found = rsc; + rsc = rsc->next; + return found; + } + + rsc = rsc->next; + } +#endif + + return NULL; +} + +void +oc_ri_delete_app_resources_per_device(size_t index) +{ + oc_resource_t *res = oc_ri_get_app_resources(); + oc_resource_t *t; + + while (res) { + if (res->device == index) { + t = res; + res = res->next; + oc_ri_delete_resource(t); + continue; + } + res = res->next; + } + + oc_collections_free_per_device(index); +} +#endif /* OC_HAS_FEATURE_BRIDGE */ + void oc_ri_init(void) { diff --git a/api/oc_swupdate.c b/api/oc_swupdate.c index 0b9948895a..170008f05b 100644 --- a/api/oc_swupdate.c +++ b/api/oc_swupdate.c @@ -353,6 +353,36 @@ oc_swupdate_create(void) } } +#ifdef OC_HAS_FEATURE_BRIDGE +static void +_init_swupdate(size_t device_index) +{ + memset(&g_sw[device_index], 0, sizeof(oc_swupdate_t)); + swupdate_create_resource(device_index); +} + +void +oc_swupdate_create_new_device(size_t device_index, bool need_realloc) +{ +#ifdef OC_DYNAMIC_ALLOCATION + if ((device_index == (oc_core_get_num_devices() - 1)) && need_realloc) { + g_sw = (oc_swupdate_t *)realloc(g_sw, oc_core_get_num_devices() * sizeof(oc_swupdate_t)); + if (g_sw == NULL) { + oc_abort("Insufficient memory"); + } + _init_swupdate(device_index); + } else if (device_index < oc_core_get_num_devices()) { + oc_free_string(&g_sw[device_index].purl); + oc_free_string(&g_sw[device_index].nv); + oc_free_string(&g_sw[device_index].signage); + _init_swupdate(device_index); + } else { + OC_ERR("device index error ! (%zu)", device_index); + } +#endif /* OC_DYNAMIC_ALLOCATION */ +} +#endif /* OC_HAS_FEATURE_BRIDGE */ + const char * oc_swupdate_action_to_str(oc_swupdate_action_t action) { diff --git a/api/oc_swupdate_internal.h b/api/oc_swupdate_internal.h index c84bab359c..44ffc59229 100644 --- a/api/oc_swupdate_internal.h +++ b/api/oc_swupdate_internal.h @@ -68,6 +68,13 @@ typedef struct oc_swupdate_t */ void oc_swupdate_create(void); +#ifdef OC_HAS_FEATURE_BRIDGE +/** + * @brief Allocate and initialize Software Update (SWU) resources and data for specific Device. + */ +void oc_swupdate_create_new_device(size_t device_index, bool need_realloc); +#endif + /** * @brief Deallocate all SWU resource data. */ diff --git a/api/oc_uuid.c b/api/oc_uuid.c index ca076c83f4..934a22a3e7 100644 --- a/api/oc_uuid.c +++ b/api/oc_uuid.c @@ -178,3 +178,10 @@ oc_uuid_is_equal(oc_uuid_t first, oc_uuid_t second) { return memcmp(first.id, second.id, OC_UUID_ID_SIZE) == 0; } + +bool +oc_uuid_is_nil(oc_uuid_t uuid) +{ + oc_uuid_t nil_uuid = { { 0 } }; + return (memcmp(&uuid, &nil_uuid, sizeof(oc_uuid_t)) == 0); +} diff --git a/api/oc_vod_map.c b/api/oc_vod_map.c new file mode 100644 index 0000000000..d9464af619 --- /dev/null +++ b/api/oc_vod_map.c @@ -0,0 +1,499 @@ +/****************************************************************** + * + * Copyright 2020 Intel Corporation + * Copyright 2023 ETRI Joo-Chul Kevin Lee (rune@etri.re.kr) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************/ + +#include "util/oc_features.h" + +#ifdef OC_HAS_FEATURE_BRIDGE + +#include "oc_vod_map.h" +#include "oc_rep.h" +#include "oc_rep_internal.h" +#include "oc_core_res.h" +#include "port/oc_connectivity.h" +#include "port/oc_log_internal.h" +#include "port/oc_storage.h" + +/* + * g_reset_index : + * - it stores the index of g_oc_device_info[] where the first VOD was added + */ +static size_t g_reset_index; + +/* + * g_vod_mapping_list : + * - vods : list of VOD (oc_virtual_device_t) + * - next_index : index of g_oc_device_info[]. new VOD will be added to + * g_oc_device_info[next_index] + */ +static oc_vod_mapping_list_t g_vod_mapping_list; + +#define SVR_TAG_MAX (32) + +static bool +_decode_vod_list(const oc_rep_t *rep) +{ + oc_rep_t *v; + if (!oc_rep_get_object_array(rep, "vods", &v)) { + OC_DBG("oc_vod_map: decode 'vods' object array not found."); + return false; + } + + while (NULL != v) { + oc_virtual_device_t *vod = + (oc_virtual_device_t *)malloc(sizeof(oc_virtual_device_t)); + char *v_id = NULL; + if (!oc_rep_get_byte_string(v->value.object, "vod_id", &v_id, + &vod->v_id_size)) { + OC_DBG("oc_vod_map: decode 'vod_id' not found."); + return false; + } + if (NULL != v_id) { + vod->v_id = (uint8_t *)malloc(vod->v_id_size * sizeof(uint8_t)); + memcpy(vod->v_id, v_id, vod->v_id_size); + } else { + OC_DBG("oc_vod_map: decode failed to find 'vod_id'"); + return false; + } + char *en = NULL; + size_t en_size = 0; + if (!oc_rep_get_string(v->value.object, "econame", &en, &en_size)) { + OC_DBG("oc_vod_map: decode 'econame' not found."); + return false; + } + if (NULL != en) { + oc_new_string(&vod->econame, en, en_size); + } else { + return false; + } + int64_t temp = 0; + if (!oc_rep_get_int(v->value.object, "index", &temp)) { + OC_DBG("oc_vod_map: decode 'index' not found."); + return false; + } + vod->index = (size_t)temp; + /* + * TODO4ME oc_vod_map_decode() : insert codes to restore + * `is_removed` value + */ +#if 0 + vod->is_removed = true; +#endif + oc_list_add(g_vod_mapping_list.vods, vod); + v = v->next; + } + + return true; +} + +static bool +_decode_rep(const oc_rep_t *rep) +{ + size_t len = 0; + + len = oc_string_len(rep->name); + switch (rep->type) { + case OC_REP_INT: + if (len == 10 && memcmp(oc_string(rep->name), "next_index", 10) == 0) { + g_vod_mapping_list.next_index = (size_t)rep->value.integer; + } + break; + case OC_REP_OBJECT_ARRAY: { + if (_decode_vod_list(rep) == false) + return false; + } break; + default: + break; + } + + return true; +} + +static bool +oc_vod_map_decode(oc_rep_t *rep, bool from_storage) +{ + /* + * TODO4ME use the from_storage param or drop it from the map_decode + */ + (void)from_storage; + + /* + * TODO4ME <2023/8/14> This could make a bug because Devices array + * (g_oc_device_info[]) can not be loaded now. so currently disable loading of + * `g_vod_mapping_list` + */ + if (from_storage == false) + return true; + + while (rep != NULL) { + if (_decode_rep(rep) == false) + return false; + rep = rep->next; + } + return true; +} + +/* + * load vod_map file and pass bytes to decode to populate oc_vod_list_t + * + * reference oc_sec_load_acl(size_t device) in oc_store.c + */ +static void +oc_vod_map_load(void) +{ + long ret = 0; + oc_rep_t *rep; + +#ifdef OC_DYNAMIC_ALLOCATION + uint8_t *buf = malloc(OC_MAX_APP_DATA_SIZE); + if (!buf) { + return; + } +#else /* OC_DYNAMIC_ALLOCATION */ + uint8_t buf[OC_MAX_APP_DATA_SIZE]; +#endif /* !OC_DYNAMIC_ALLOCATION */ + + ret = oc_storage_read("vod_map", buf, OC_MAX_APP_DATA_SIZE); + if (ret > 0) { +#ifndef OC_DYNAMIC_ALLOCATION + char rep_objects_alloc[OC_MAX_NUM_REP_OBJECTS]; + oc_rep_t rep_objects_pool[OC_MAX_NUM_REP_OBJECTS]; + memset(rep_objects_alloc, 0, OC_MAX_NUM_REP_OBJECTS * sizeof(char)); + memset(rep_objects_pool, 0, OC_MAX_NUM_REP_OBJECTS * sizeof(oc_rep_t)); + struct oc_memb rep_objects = { sizeof(oc_rep_t), OC_MAX_NUM_REP_OBJECTS, + rep_objects_alloc, (void *)rep_objects_pool, + 0 }; +#else /* !OC_DYNAMIC_ALLOCATION */ + struct oc_memb rep_objects = { sizeof(oc_rep_t), 0, 0, 0, 0 }; +#endif /* OC_DYNAMIC_ALLOCATION */ + oc_rep_set_pool(&rep_objects); + rep = oc_parse_rep(buf, (size_t)ret); + /* + * TODO4ME <2023/8/14> This could make a bug because Devices + * (g_oc_device_info[]) can not be loaded now. so, currently disable loading + * of `g_vod_mapping_list` + */ +#if 0 + oc_vod_map_decode(rep, true); +#endif + oc_vod_map_decode(rep, false); + oc_free_rep(rep); + } +#ifdef OC_DYNAMIC_ALLOCATION + free(buf); +#endif /* OC_DYNAMIC_ALLOCATION */ +} + +/* + * responsible for encoding the oc_vod_list_t to cbor + * function will be used by dump_vod_map() + */ +static void +oc_vod_map_encode(void) +{ + oc_rep_begin_root_object(); + oc_rep_set_int(root, next_index, g_vod_mapping_list.next_index); + oc_virtual_device_t *v = oc_list_head(g_vod_mapping_list.vods); + + oc_rep_open_array(root, vods); + while (v != NULL) { + oc_rep_object_array_begin_item(vods); + oc_rep_set_byte_string(vods, vod_id, v->v_id, v->v_id_size); + oc_rep_set_text_string(vods, econame, oc_string(v->econame)); + oc_rep_set_int(vods, index, v->index); + oc_rep_object_array_end_item(vods); + v = v->next; + } + oc_rep_close_array(root, vods); + oc_rep_end_root_object(); +} + +/* + * convert the oc_vod_list_t to cbor + * dump cbor bytes to vod_map file + * + * reference oc_sec_dump_acl(size_t device) in oc_store.c + */ +static void +oc_vod_map_dump(void) +{ +#ifdef OC_DYNAMIC_ALLOCATION + uint8_t *buf = malloc(OC_MAX_APP_DATA_SIZE); + if (!buf) + return; +#else /* OC_DYNAMIC_ALLOCATION */ + uint8_t buf[OC_MAX_APP_DATA_SIZE]; +#endif /* !OC_DYNAMIC_ALLOCATION */ + + oc_rep_new_v1(buf, OC_MAX_APP_DATA_SIZE); + oc_vod_map_encode(); + int size = oc_rep_get_encoded_payload_size(); + if (size > 0) { + OC_DBG("oc_vod_map: encoded vod_map size %d", size); + oc_storage_write("vod_map", buf, size); + } + +#ifdef OC_DYNAMIC_ALLOCATION + free(buf); +#endif /* OC_DYNAMIC_ALLOCATION */ +} + +void +oc_vod_map_init(void) +{ + OC_LIST_STRUCT_INIT(&g_vod_mapping_list, vods); + g_reset_index = g_vod_mapping_list.next_index = oc_core_get_num_devices(); + oc_vod_map_load(); +} + +/* + * release the resouces. + */ +void +oc_vod_map_free(void) +{ + if (g_vod_mapping_list.vods) { + oc_virtual_device_t *v = oc_list_head(g_vod_mapping_list.vods); + oc_virtual_device_t *v_to_free; + while (v != NULL) { + free(v->v_id); + oc_free_string(&v->econame); + v_to_free = v; + v = v->next; + oc_list_remove(g_vod_mapping_list.vods, v_to_free); + free(v_to_free); + v_to_free = NULL; + } + } +} + +/* + * Reset the vod map as if no VODs had been discovered. + */ +void +oc_vod_map_reset(void) +{ + oc_vod_map_free(); + g_vod_mapping_list.next_index = g_reset_index; + oc_vod_map_dump(); +} + +size_t +oc_vod_map_get_vod_index(const uint8_t *vod_id, size_t vod_id_size, + const char *econame) +{ + oc_virtual_device_t *v = oc_list_head(g_vod_mapping_list.vods); + + while (v != NULL) { + if (v->v_id_size == vod_id_size && + memcmp(vod_id, v->v_id, vod_id_size) == 0 && + (v->econame.size - 1) == strlen(econame) && + memcmp(econame, oc_string(v->econame), v->econame.size) == 0) { + return v->index; + } + v = v->next; + } + return 0; +} + +static void +_update_next_index(oc_virtual_device_t *v) +{ + /* + * continue walking the vods mapping entry list till an open next_index is + * found + * + * if the new VOD mapping entry is inserted in the middle of + * `g_vod_mapping_list.vods` list, find next available index of + * `g_oc_device_info[]` and save index value into the + * `g_vod_mapping_list.next_index` + */ + while (v != NULL) { + if (v->next != NULL && v->next->index == g_vod_mapping_list.next_index) { + g_vod_mapping_list.next_index++; + } + v = v->next; + } +} + +static void +_add_mapping_entry(oc_virtual_device_t *v, oc_virtual_device_t *vod) +{ + /* + * if this is not the first VOD mapping entry of `g_vod_mapping_list.vods` + * list (`g_vod_mapping_list.vods` is not empty).. Traverse + * g_vod_mapping_list.vods to find `v` whose index is + * (g_vod_mapping_list.next_index - 1), and insert new VOD mapping entry after + * `v`. + * + * After that, continue to increase `next_index` until no `v->index` matching + * `next_index` is found. After that, next_index will be updated And + * therefore,`g_vod_mapping_list.vods` list is always sorted in the order of + * `oc_virtual_device_t.index`... + */ + while (v != NULL) { + if ((g_vod_mapping_list.next_index == g_reset_index) || + (v->index == (g_vod_mapping_list.next_index - 1))) { + + if (g_vod_mapping_list.next_index == g_reset_index) { + /* + * if `next_index` points the first node of `oc_vod_mapping_list.vods`, + * (v->index == (g_vod_mapping_list.next_index - 1)) can't be satisfied + * ever... because there is no VOD mapping entry pointing Device before + * the first VOD. + * + * therefore, insert new VOD mapping entry as the the first item in the + * list in this case. + */ + oc_list_insert(g_vod_mapping_list.vods, NULL, vod); + v = oc_list_head(g_vod_mapping_list.vods); + } else { + /* + * if `next_index` points the middle node of `oc_vod_mapping_list.vods`, + * finds `v` whose index is (g_vod_mapping_list.next_index - 1). + * and insert new VOD mapping entry after `v`. + */ + oc_list_insert(g_vod_mapping_list.vods, v, vod); + } + + g_vod_mapping_list.next_index++; + + /* + * continue walking the vods mapping entry list till an open next_index is + * found + * + * if the new VOD mapping entry is inserted in the middle of + * `g_vod_mapping_list.vods` list, find next available index of + * `g_oc_device_info[]` and save index value into the + * `g_vod_mapping_list.next_index` + */ + _update_next_index(v); + break; + } + v = v->next; + } /* while */ +} + +size_t +oc_vod_map_add_mapping_entry(const uint8_t *vod_id, const size_t vod_id_size, + const char *econame) +{ + /* + * try to find this VOD mapping entry is already in `g_vod_mapping_list.vods` + * or not + */ + size_t v_index = oc_vod_map_get_vod_index(vod_id, vod_id_size, econame); + + /* + * if this vod mapping entry is already in `g_vod_mapping_list.vods`, + * return corresponding index of Device in g_oc_device_info[]. + */ + if (v_index != 0) { + return v_index; + } + + /* + * if this VOD mapping entry has not been added to `g_vod_mapping_list.vods`, + * insert it to g_vod_mapping_list.vods. + */ + oc_virtual_device_t *vod = + (oc_virtual_device_t *)malloc(sizeof(oc_virtual_device_t)); + vod->v_id = (uint8_t *)malloc(vod_id_size * sizeof(uint8_t)); + memcpy(vod->v_id, vod_id, vod_id_size); + vod->v_id_size = vod_id_size; + oc_new_string(&vod->econame, econame, strlen(econame)); + vod->is_vod_online = false; + + /* + * save corresponding index of Device in `g_oc_device_info[]` into + * `vod->index` (this Device has not been added to g_oc_device_info[] yet.) + */ + vod->index = g_vod_mapping_list.next_index; + + /* + * if this is the first VOD mapping entry (`g_vod_list.vods` is empty)... + * add new VOD mapping entry to `g_vod_mapping_list.vods` list + */ + oc_virtual_device_t *v = oc_list_head(g_vod_mapping_list.vods); + if (v == NULL) { + oc_list_add(g_vod_mapping_list.vods, vod); + g_vod_mapping_list.next_index++; + } else { + _add_mapping_entry(v, vod); + } + + oc_vod_map_dump(); + return vod->index; +} + +void +oc_vod_map_remove_mapping_entry(size_t device_index) +{ + oc_virtual_device_t *v = oc_list_head(g_vod_mapping_list.vods); + while (v != NULL) { + if (v->index == device_index) { + free(v->v_id); + oc_free_string(&v->econame); + oc_virtual_device_t *v_to_free = v; + oc_list_remove(g_vod_mapping_list.vods, v); + if (device_index < g_vod_mapping_list.next_index) { + g_vod_mapping_list.next_index = device_index; + } + free(v_to_free); + v_to_free = NULL; + break; + } + v = v->next; + } + + oc_vod_map_dump(); +} + +void +oc_vod_map_get_econame(oc_string_t *econame, size_t device_index) +{ + oc_virtual_device_t *v = oc_list_head(g_vod_mapping_list.vods); + while (v != NULL) { + if (v->index == device_index) { + *econame = v->econame; + return; + } + v = v->next; + } +} + +oc_virtual_device_t * +oc_vod_map_get_mapping_entry(size_t device_index) +{ + oc_virtual_device_t *v = oc_list_head(g_vod_mapping_list.vods); + while (v != NULL) { + if (v->index == device_index) { + return v; + } + v = v->next; + } + return NULL; +} + +oc_virtual_device_t * +oc_vod_map_get_mapping_list(void) +{ + return oc_list_head(g_vod_mapping_list.vods); +} + +#endif /* OC_HAS_FEATURE_BRIDGE */ diff --git a/api/unittest/RITest.cpp b/api/unittest/RITest.cpp index 64104a3dc2..c89f4b506a 100644 --- a/api/unittest/RITest.cpp +++ b/api/unittest/RITest.cpp @@ -2,6 +2,7 @@ * * Copyright 2018 GRANITE RIVER LABS All Rights Reserved. * 2021 CASCODA LTD All Rights Reserved. + * 2024 ETRI All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"), * you may not use this file except in compliance with the License. @@ -26,10 +27,26 @@ #include "api/oc_runtime_internal.h" #include "port/oc_network_event_handler_internal.h" +#ifdef OC_HAS_FEATURE_BRIDGE +#include "oc_api.h" +#include "oc_core_res.h" +#include "security/oc_svr_internal.h" +#include "api/oc_core_res_internal.h" +#include +#endif /* OC_HAS_FEATURE_BRIDGE */ + +#ifdef OC_SOFTWARE_UPDATE +#include "api/oc_swupdate_internal.h" +#endif + #ifdef OC_TCP #include "messaging/coap/signal_internal.h" #endif /* OC_TCP */ +#ifdef OC_HAS_FEATURE_PUSH +#include "api/oc_push_internal.h" +#endif /* OC_HAS_FEATURE_PUSH */ + #include #include #include @@ -40,19 +57,165 @@ class TestOcRi : public testing::Test { public: void SetUp() override { - oc_network_event_handler_mutex_init(); oc_runtime_init(); oc_ri_init(); + +#ifdef OC_HAS_FEATURE_BRIDGE + oc_core_init(); +#endif /* OC_HAS_FEATURE_BRIDGE */ + + oc_network_event_handler_mutex_init(); + +#if defined(OC_HAS_FEATURE_BRIDGE) && defined(OC_SECURITY) + oc_sec_svr_create(); +#endif /* OC_SECURITY */ + +#ifdef OC_SOFTWARE_UPDATE + oc_swupdate_create(); +#endif } void TearDown() override { +#if defined(OC_HAS_FEATURE_BRIDGE) && defined(OC_SECURITY) + oc_sec_svr_free(); +#endif /* defined(OC_HAS_FEATURE_BRIDGE) && defined(OC_SECURITY) */ + +#ifdef OC_SOFTWARE_UPDATE + oc_swupdate_free(); +#endif /* OC_SOFTWARE_UPDATE */ + +#ifdef OC_HAS_FEATURE_PUSH + oc_push_free(); +#endif /* OC_HAS_FEATURE_PUSH */ + oc_ri_shutdown(); - oc_runtime_shutdown(); oc_network_event_handler_mutex_destroy(); + +#ifdef OC_HAS_FEATURE_BRIDGE + oc_core_shutdown(); +#endif /* OC_HAS_FEATURE_BRIDGE */ + + oc_runtime_shutdown(); } }; +#ifdef OC_HAS_FEATURE_BRIDGE +static void +get_handler(oc_request_t *request, oc_interface_mask_t iface_mask, + void *user_data) +{ +} + +static const std::string kDeviceName{ "Table Lamp" }; +static const std::string kDeviceURI{ "/oic/d" }; +static const std::string kDeviceType{ "oic.d.light" }; +static const std::string kOCFSpecVersion{ "ocf.1.0.0" }; +static const std::string kOCFDataModelVersion{ "ocf.res.1.0.0" }; + +TEST_F(TestOcRi, GetNDeleteAppResourceByIndex) +{ + oc_add_new_device_t cfg{}; + cfg.name = kDeviceName.c_str(); + cfg.uri = kDeviceURI.c_str(); + cfg.rt = kDeviceType.c_str(); + cfg.spec_version = kOCFSpecVersion.c_str(); + cfg.data_model_version = kOCFDataModelVersion.c_str(); + size_t device_count; + + /* -------------------------------------------------*/ + /* + * oc_ri_get_app_resource_by_device(device, reset) + */ + /*--------------------------------------------------*/ + + auto deviceIndex = oc_core_get_num_devices(); + EXPECT_NE(oc_core_add_new_device_at_index(cfg, deviceIndex), nullptr); + + auto deviceInfo = oc_core_get_device_info(deviceIndex); + + ASSERT_NE(deviceInfo, nullptr); + EXPECT_STREQ(kDeviceName.c_str(), oc_string(deviceInfo->name)); + EXPECT_EQ(0, deviceIndex); + + /* + * add Resources + */ + std::string rscURI1{ "/rsc1" }; + auto resource1 = oc_new_resource(nullptr, rscURI1.c_str(), 0, deviceIndex); + oc_resource_set_request_handler(resource1, OC_GET, get_handler, nullptr); + ASSERT_NE(nullptr, resource1); + EXPECT_EQ(deviceIndex, resource1->device); + oc_add_resource(resource1); + + std::string rscURI2{ "/rsc2" }; + auto resource2 = oc_new_resource(nullptr, rscURI2.c_str(), 0, deviceIndex); + oc_resource_set_request_handler(resource2, OC_GET, get_handler, nullptr); + ASSERT_NE(nullptr, resource2); + EXPECT_EQ(deviceIndex, resource2->device); + oc_add_resource(resource2); + + /* + * add collection Resource + */ + std::string colURI1 = "/col1"; + auto colResource3 = + oc_new_collection(nullptr, colURI1.c_str(), 0, deviceIndex); + ASSERT_NE(nullptr, colResource3); + EXPECT_STREQ(colURI1.c_str(), oc_string(colResource3->uri)); + EXPECT_EQ(deviceIndex, colResource3->device); + oc_add_collection_v1(colResource3); + + /* + * try to find from the first entry... + */ + auto rsc1 = oc_ri_get_app_resource_by_device(deviceIndex, true); + ASSERT_NE(rsc1, nullptr); + + auto rsc2 = oc_ri_get_app_resource_by_device(deviceIndex, true); + ASSERT_NE(rsc2, nullptr); + EXPECT_STREQ(oc_string(rsc1->uri), oc_string(rsc2->uri)); + + /* + * try to resume search from the entry which was seen last... + */ + rsc1 = oc_ri_get_app_resource_by_device(deviceIndex, true); + ASSERT_NE(rsc1, nullptr); + + rsc2 = oc_ri_get_app_resource_by_device(deviceIndex, false); + ASSERT_NE(rsc2, nullptr); + EXPECT_STRNE(oc_string(rsc1->uri), oc_string(rsc2->uri)); + + /* + * try to find all app resources mapped to a Device + */ + std::set rscSet{ rscURI1.c_str(), rscURI2.c_str(), + colURI1.c_str() }; + + rsc1 = oc_ri_get_app_resource_by_device(deviceIndex, true); + while (rsc1) { + rscSet.erase(oc_string(rsc1->uri)); + rsc1 = oc_ri_get_app_resource_by_device(deviceIndex, false); + } + + EXPECT_EQ(true, rscSet.empty()); + + /* -------------------------------------------------*/ + /* + * oc_ri_delete_app_resources_per_device(index) + */ + /*--------------------------------------------------*/ + oc_ri_delete_app_resources_per_device(deviceIndex); + rsc1 = oc_ri_get_app_resource_by_device(deviceIndex, true); + + EXPECT_EQ(nullptr, rsc1); + + /* remove device */ + oc_core_remove_device_at_index(deviceIndex); +} + +#endif /* OC_HAS_FEATURE_BRIDGE */ + TEST_F(TestOcRi, StatusCodeToCoapCode) { for (int i = OC_STATUS_OK; i < __NUM_OC_STATUS_CODES__; ++i) { diff --git a/api/unittest/bridgetest.cpp b/api/unittest/bridgetest.cpp new file mode 100644 index 0000000000..b511c08d66 --- /dev/null +++ b/api/unittest/bridgetest.cpp @@ -0,0 +1,298 @@ +/****************************************************************** + * + * Copyright 2023 ETRI Joo-Chul Kevin Lee (rune@etri.re.kr) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************/ + +#include "util/oc_features.h" + +#ifdef OC_HAS_FEATURE_BRIDGE + +#include +#include + +#include "oc_api.h" +#include "oc_bridge.h" +#include "oc_vod_map.h" +#include "port/oc_storage.h" +#include "api/oc_ri_internal.h" +#include "api/oc_runtime_internal.h" +#include "api/oc_core_res_internal.h" +#include "api/oc_swupdate_internal.h" +#include "security/oc_svr_internal.h" +#include "security/oc_doxm_internal.h" +#include "port/oc_network_event_handler_internal.h" + +#include "tests/gtest/Device.h" + +#ifdef OC_HAS_FEATURE_PUSH +#include "api/oc_push_internal.h" +#endif /* OC_HAS_FEATURE_PUSH */ + +extern "C" { +void bridge_owned_changed(const oc_uuid_t *device_uuid, size_t device_index, + bool owned, void *user_data); +} + +static const oc::DeviceToAdd kBridgeDevice = { "oic.d.bridge", "BridgeDevice", + "ocf.1.0.0", "ocf.res.1.0.0", + "/oic/d" }; + +static constexpr size_t kBridgeDeviceID{ 0 }; + +static const oc::DeviceToAdd kVODDevice = { "oic.d.virtual", "VOD1", + "ocf.1.0.0", "ocf.res.1.0.0", + "/oic/d" }; + +static const std::string kVODDeviceID{ "vod1" }; +static const std::string kVODEconame{ "matter" }; +static const std::string kVODlistRscURI{ "/bridge/vodlist" }; + +class TestBridge : public testing::Test { +private: + void RunBridgeDevice() + { + auto result = oc_init_platform("ETRI", nullptr, nullptr); + EXPECT_EQ(result, 0); + + /* + * add bridge device (oic.d.bridge) + */ + auto bridgeIndex = oc_bridge_add_bridge_device( + kBridgeDevice.name.c_str(), kBridgeDevice.spec_version.c_str(), + kBridgeDevice.data_model_version.c_str(), nullptr, nullptr); + + EXPECT_EQ(bridgeIndex, kBridgeDeviceID); + } + +public: + void SetUp() override + { + oc_runtime_init(); + oc_ri_init(); + oc_core_init(); + oc_network_event_handler_mutex_init(); + RunBridgeDevice(); +#ifdef OC_SECURITY + oc_sec_svr_create(); +#endif /* OC_SECURITY */ +#ifdef OC_SOFTWARE_UPDATE + oc_swupdate_create(); +#endif + } + + void ShutdownConnectivity() + { + for (size_t device = 0; device < oc_core_get_num_devices(); device++) { + if (oc_core_get_device_info(device)->is_removed == false) + oc_connectivity_shutdown(device); + } + } + + void TearDown() override + { +#ifdef OC_SECURITY + oc_sec_svr_free(); +#endif /* OC_SECURITY */ + +#ifdef OC_SOFTWARE_UPDATE + oc_swupdate_free(); +#endif /* OC_SOFTWARE_UPDATE */ + +#ifdef OC_HAS_FEATURE_PUSH + oc_push_free(); +#endif /* OC_HAS_FEATURE_PUSH */ + oc_ri_shutdown(); + ShutdownConnectivity(); + oc_network_event_handler_mutex_destroy(); + oc_core_shutdown(); + oc_runtime_shutdown(); + } +}; + +TEST_F(TestBridge, BridgeAPITest) +{ + /* -------------------------------------------------*/ + /* + * oc_bridge_add_bridge_device() + */ + /* -------------------------------------------------*/ + + /* + * add bridge device (oic.d.bridge) + */ + auto bridgeDeviceInfo = oc_core_get_device_info(kBridgeDeviceID); + ASSERT_NE(bridgeDeviceInfo, nullptr); + + /* check bridge device name */ + EXPECT_STREQ(kBridgeDevice.name.c_str(), oc_string(bridgeDeviceInfo->name)); + + auto bridgeDeviceRsc = oc_core_get_resource_by_uri_v1( + kBridgeDevice.uri.c_str(), kBridgeDevice.uri.size(), kBridgeDeviceID); + ASSERT_NE(bridgeDeviceRsc, nullptr); + + /* check bridge device type */ + EXPECT_STREQ(kBridgeDevice.rt.c_str(), + oc_string_array_get_item(bridgeDeviceRsc->types, 0)); + + /* -------------------------------------------------*/ + /* + * oc_bridge_add_virtual_device() + * oc_bridge_get_virtual_device_index() + */ + /* -------------------------------------------------*/ + size_t vodIndex = oc_bridge_add_virtual_device( + (const uint8_t *)kVODDeviceID.c_str(), kVODDeviceID.size(), + kVODEconame.c_str(), kVODDevice.uri.c_str(), kVODDevice.rt.c_str(), + kVODDevice.name.c_str(), kVODDevice.spec_version.c_str(), + kVODDevice.data_model_version.c_str(), nullptr, nullptr); + EXPECT_NE(vodIndex, 0); + + // oc_device_info_t *vodDeviceInfo = oc_core_get_device_info(vodIndex); + auto vodDeviceInfo = oc_core_get_device_info(vodIndex); + ASSERT_NE(vodDeviceInfo, nullptr); + + /* check VOD device name */ + EXPECT_STREQ(kVODDevice.name.c_str(), oc_string(vodDeviceInfo->name)); + + auto vodDeviceRsc = oc_core_get_resource_by_uri_v1( + kVODDevice.uri.c_str(), kVODDevice.uri.size(), vodIndex); + ASSERT_NE(vodDeviceRsc, nullptr); + + /* check bridge device type */ + EXPECT_STREQ(kVODDevice.rt.c_str(), + oc_string_array_get_item(vodDeviceRsc->types, 0)); + + /* check device index */ + auto vodIndexTemp = oc_bridge_get_virtual_device_index( + (const uint8_t *)kVODDeviceID.c_str(), kVODDeviceID.size(), + kVODEconame.c_str()); + EXPECT_EQ(vodIndexTemp, vodIndex); + + /* -------------------------------------------------*/ + /* + * oc_bridge_remove_virtual_device() + * oc_bridge_get_vod() + * oc_bridge_get_vod_list() + * oc_bridge_get_vod_mapping_info() + * oc_bridge_get_vod_mapping_info2() + */ + /* -------------------------------------------------*/ + /* get vodlist resource */ + auto vodListRsc = oc_ri_get_app_resource_by_uri( + kVODlistRscURI.c_str(), kVODlistRscURI.size(), kBridgeDeviceID); + ASSERT_NE(vodListRsc, nullptr); + EXPECT_EQ(vodListRsc->device, kBridgeDeviceID); + + /* get vod item for VOD */ + // oc_vods_t * vodItem = oc_bridge_get_vod(vodDeviceInfo->di); + auto vodItem = oc_bridge_get_vod(vodDeviceInfo->di); + EXPECT_EQ(vodItem, nullptr); + + /* get vodlist */ + vodItem = oc_bridge_get_vod_list(); + EXPECT_EQ(vodItem, nullptr); + +#ifdef OC_SECURITY + /* own bridge device */ + bridge_owned_changed(&bridgeDeviceInfo->di, kBridgeDeviceID, true, nullptr); + auto bridgeDoxm = oc_sec_get_doxm(kBridgeDeviceID); + bridgeDoxm->owned = true; + + /* own VODS */ + bridge_owned_changed(&vodDeviceInfo->di, vodIndex, true, nullptr); + auto vodDoxm = oc_sec_get_doxm(vodIndex); + vodDoxm->owned = true; + + /* get vod item for VOD */ + vodItem = oc_bridge_get_vod(vodDeviceInfo->di); + EXPECT_NE(vodItem, nullptr); + + /* get vodlist */ + vodItem = oc_bridge_get_vod_list(); + EXPECT_NE(vodItem, nullptr); + + /* try to get vod map entry */ + auto vodMapEntry1 = oc_bridge_get_vod_mapping_info(vodIndex); + auto vodMapEntry2 = oc_bridge_get_vod_mapping_info2(vodItem); + EXPECT_EQ(vodMapEntry1, vodMapEntry2); + + /* remove vod from vod list */ + ASSERT_EQ(oc_bridge_remove_virtual_device(vodIndex), 0); + + /* get vod item for VOD */ + vodItem = oc_bridge_get_vod(vodDeviceInfo->di); + EXPECT_EQ(vodItem, nullptr); + + /* get vodlist */ + vodItem = oc_bridge_get_vod_list(); + EXPECT_EQ(vodItem, nullptr); +#else + /* try to get vod map entry */ + auto vodMapEntry1 = oc_bridge_get_vod_mapping_info(vodIndex); + EXPECT_NE(vodMapEntry1, nullptr); +#endif /* OC_SECURITY */ + + /* -------------------------------------------------*/ + /* + * oc_bridge_add_vod() + */ + /* -------------------------------------------------*/ + ASSERT_EQ(oc_bridge_add_vod(vodIndex), 0); + +#ifdef OC_SECURITY + /* get vod item for VOD */ + vodItem = oc_bridge_get_vod(vodDeviceInfo->di); + EXPECT_NE(vodItem, nullptr); + + /* get vodlist */ + vodItem = oc_bridge_get_vod_list(); + EXPECT_NE(vodItem, nullptr); +#else + /* get vod item for VOD */ + vodItem = oc_bridge_get_vod(vodDeviceInfo->di); + EXPECT_EQ(vodItem, nullptr); + + /* get vodlist */ + vodItem = oc_bridge_get_vod_list(); + EXPECT_EQ(vodItem, nullptr); +#endif /* OC_SECURITY */ + + /* -------------------------------------------------*/ + /* + * oc_bridge_delete_virtual_device() + */ + /* -------------------------------------------------*/ + oc_uuid_t vodDi; + memcpy(vodDi.id, vodDeviceInfo->di.id, OC_UUID_ID_SIZE); + + auto result = oc_bridge_delete_virtual_device(vodIndex); + EXPECT_EQ(result, 0); + + /* check vod is removed from vod list */ + /* get vod item for VOD */ + vodItem = oc_bridge_get_vod(vodDi); + EXPECT_EQ(vodItem, nullptr); + + /* get vodlist */ + vodItem = oc_bridge_get_vod_list(); + EXPECT_EQ(vodItem, nullptr); + + /* check vod is removed from vod mapping list */ + auto vodMapEntry3 = oc_bridge_get_vod_mapping_info(vodIndex); + EXPECT_EQ(vodMapEntry3, nullptr); +} + +#endif /* OC_HAS_FEATURE_BRIDGE */ diff --git a/api/unittest/collectiontest.cpp b/api/unittest/collectiontest.cpp index 054e35a98f..866cb6208d 100644 --- a/api/unittest/collectiontest.cpp +++ b/api/unittest/collectiontest.cpp @@ -1,6 +1,7 @@ /**************************************************************************** * * Copyright (c) 2023 plgd.dev s.r.o. + * Copyright (c) 2024 ETRI * * Licensed under the Apache License, Version 2.0 (the "License"), * you may not use this file except in compliance with the License. @@ -1015,4 +1016,60 @@ TEST_F(TestCollectionsWithServer, GetRequest_Batch) #endif // !OC_SECURITY || OC_HAS_FEATURE_RESOURCE_ACCESS_IN_RFOTM +#ifdef OC_HAS_FEATURE_BRIDGE +/* + * Done: + * oc_collections_free_per_device(device) + */ +TEST_F(TestCollectionsWithServer, FreePerDevice) +{ + /* + * add collection Resource 1 + */ + std::string name1 = "col1"; + std::string uri1 = "/col1"; + oc_resource_t *col1 = + oc_new_collection(name1.c_str(), uri1.c_str(), 0, kDeviceID); + ASSERT_NE(nullptr, col1); + + EXPECT_STREQ(name1.c_str(), oc_string(col1->name)); + EXPECT_STREQ(uri1.c_str(), oc_string(col1->uri)); + EXPECT_EQ(kDeviceID, col1->device); + + EXPECT_TRUE(oc_add_collection_v1(col1)); + + /* + * add collection Resource 2 + */ + std::string name2 = "col2"; + std::string uri2 = "/col2"; + oc_resource_t *col2 = + oc_new_collection(name2.c_str(), uri2.c_str(), 0, kDeviceID); + ASSERT_NE(nullptr, col2); + + EXPECT_STREQ(name2.c_str(), oc_string(col2->name)); + EXPECT_STREQ(uri2.c_str(), oc_string(col2->uri)); + EXPECT_EQ(kDeviceID, col2->device); + + EXPECT_TRUE(oc_add_collection_v1(col2)); + + /* + * check all Collection Resources were inserted successfully + */ + oc_collection_t *col = oc_collection_get_all(); + ASSERT_NE(nullptr, col); + + EXPECT_STREQ(oc_string(col->res.name), name1.c_str()); + col = reinterpret_cast(col->res.next); + EXPECT_STREQ(oc_string(col->res.name), name2.c_str()); + + /* + * check all collection mapped to this Device are removed successfully + */ + oc_collections_free_per_device(kDeviceID); + col = oc_collection_get_all(); + EXPECT_EQ(nullptr, col); +} +#endif /* OC_HAS_FEATURE_BRIDGE */ + #endif /* OC_COLLECTIONS */ diff --git a/api/unittest/coreresourcetest.cpp b/api/unittest/coreresourcetest.cpp index 7a18fcb73d..8d7e026769 100644 --- a/api/unittest/coreresourcetest.cpp +++ b/api/unittest/coreresourcetest.cpp @@ -1,6 +1,8 @@ /****************************************************************** * * Copyright 2018 GRANITE RIVER LABS All Rights Reserved. + * 2024 ETRI All Rights Reserved. + * * * Licensed under the Apache License, Version 2.0 (the "License"), * you may not use this file except in compliance with the License. @@ -19,6 +21,7 @@ #include "api/oc_core_res_internal.h" #include "api/oc_ri_internal.h" #include "api/oc_runtime_internal.h" +#include "api/oc_swupdate_internal.h" #include "oc_api.h" #include "oc_core_res.h" #include "oc_helpers.h" @@ -31,6 +34,11 @@ #include "api/oc_push_internal.h" #endif /* OC_HAS_FEATURE_PUSH */ +#ifdef OC_HAS_FEATURE_BRIDGE +#include "oc_bridge.h" +#include "security/oc_svr_internal.h" +#endif /* OC_HAS_FEATURE_BRIDGE */ + #include #include #include @@ -57,20 +65,107 @@ class TestCoreResource : public testing::Test { oc_runtime_init(); oc_ri_init(); oc_core_init(); +#if defined(OC_HAS_FEATURE_BRIDGE) && defined(OC_SECURITY) + oc_sec_svr_create(); +#endif /* defined(OC_HAS_FEATURE_BRIDGE) && defined(OC_SECURITY) */ + +#ifdef OC_SOFTWARE_UPDATE + oc_swupdate_create(); +#endif } void TearDown() override { +#if defined(OC_HAS_FEATURE_BRIDGE) && defined(OC_SECURITY) + oc_sec_svr_free(); +#endif /* defined(OC_HAS_FEATURE_BRIDGE) && defined(OC_SECURITY) */ + +#ifdef OC_SOFTWARE_UPDATE + oc_swupdate_free(); +#endif + #ifdef OC_HAS_FEATURE_PUSH oc_push_free(); #endif /* OC_HAS_FEATURE_PUSH */ - oc_core_shutdown(); + oc_ri_shutdown(); - oc_runtime_shutdown(); oc_network_event_handler_mutex_destroy(); + oc_core_shutdown(); + oc_runtime_shutdown(); } }; +#ifdef OC_HAS_FEATURE_BRIDGE +/* + * Not testable (static functioins) : + * core_update_existing_device_data() + * core_delete_app_resources_per_device() + * core_set_device_removed() + * + * Done: + * oc_core_add_new_device_at_index() + * oc_core_remove_device_at_index() + * oc_core_get_device_index() + * + */ +TEST_F(TestCoreResource, CoreAddNRemoveDeviceAtIndex) +{ + oc_add_new_device_t cfg{}; + cfg.name = kDeviceName.c_str(); + cfg.uri = kDeviceURI.c_str(); + cfg.rt = kDeviceType.c_str(); + cfg.spec_version = kOCFSpecVersion.c_str(); + cfg.data_model_version = kOCFDataModelVersion.c_str(); + size_t device_count; + + /* -------------------------------------------------*/ + /* + * oc_core_add_new_device_at_index() + * oc_core_remove_device_at_index() + * oc_core_get_device_index() + */ + /*--------------------------------------------------*/ + + /* + * device index is outranged + * => should fail + */ + device_count = oc_core_get_num_devices(); + EXPECT_EQ(oc_core_add_new_device_at_index(cfg, device_count + 1), nullptr); + + /* + * add new device at the end of array + * => should succeed + */ + EXPECT_NE(oc_core_add_new_device_at_index(cfg, device_count), nullptr); + EXPECT_EQ(oc_core_get_device_info(device_count)->is_removed, false); + + /* + * try to overwrite new device onto existing device + * => should fail + */ + EXPECT_EQ(oc_core_add_new_device_at_index(cfg, device_count), nullptr); + + /* + * try to overwrite new device into the slot where existing device was deleted + * => should succeed + */ + oc_core_remove_device_at_index(device_count); + EXPECT_NE(oc_core_add_new_device_at_index(cfg, device_count), nullptr); + + /* + * try to find device with device id + * => should succeed + */ + size_t device_index; + oc_core_get_device_index(oc_core_get_device_info(device_count)->di, + &device_index); + EXPECT_EQ(device_index, device_count); + + oc_core_remove_device_at_index(device_count); +} +#endif /* OC_HAS_FEATURE_BRIDGE */ + TEST_F(TestCoreResource, CoreDevice_P) { oc_add_new_device_t cfg{}; @@ -79,11 +174,23 @@ TEST_F(TestCoreResource, CoreDevice_P) cfg.rt = kDeviceType.c_str(); cfg.spec_version = kOCFSpecVersion.c_str(); cfg.data_model_version = kOCFDataModelVersion.c_str(); + +#ifdef OC_HAS_FEATURE_BRIDGE + oc_device_info_t *addcoredevice = + oc_core_add_new_device_at_index(cfg, oc_core_get_num_devices()); +#else oc_device_info_t *addcoredevice = oc_core_add_new_device(cfg); +#endif /* defined(OC_HAS_FEATURE_BRIDGE) && defined(OC_SECURITY) */ + ASSERT_NE(addcoredevice, nullptr); size_t numcoredevice = oc_core_get_num_devices(); EXPECT_EQ(1, numcoredevice); + +#ifdef OC_HAS_FEATURE_BRIDGE + oc_core_remove_device_at_index(numcoredevice-1); +#else oc_connectivity_shutdown(kDevice1ID); +#endif } static void @@ -234,15 +341,9 @@ class TestCoreResourceWithDevice : public testing::Test { #endif /* OC_SERVER && OC_DYNAMIC_ALLOCATION */ } - static void TearDownTestCase() - { - oc::TestDevice::StopServer(); - } + static void TearDownTestCase() { oc::TestDevice::StopServer(); } - void TearDown() override - { - oc::TestDevice::Reset(); - } + void TearDown() override { oc::TestDevice::Reset(); } }; TEST_F(TestCoreResourceWithDevice, CoreGetDeviceID_F) diff --git a/api/unittest/uuidtest.cpp b/api/unittest/uuidtest.cpp index 8110395e54..d53b698195 100644 --- a/api/unittest/uuidtest.cpp +++ b/api/unittest/uuidtest.cpp @@ -1,6 +1,7 @@ /****************************************************************** * * Copyright 2018 Samsung Electronics All Rights Reserved. + * Copyright 2024 ETRI All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"), * you may not use this file except in compliance with the License. @@ -29,6 +30,18 @@ constexpr const char UUID2[] = "XYZabcdefghijklmnopqrstuvwxyz012"; using uuid_buffer_t = std::array; +#ifdef OC_HAS_FEATURE_BRIDGE +TEST(UUID, UUIDIsNill) +{ + oc_uuid_t uuid{}; + EXPECT_EQ(oc_uuid_is_nil(uuid), true); + + oc_uuid_t uuid2{}; + oc_str_to_uuid(UUID, &uuid2); + EXPECT_EQ(oc_uuid_is_nil(uuid2), false); +} +#endif /* OC_HAS_FEATURE_BRIDGE */ + TEST(UUID, StrToUUIDTest_P) { oc_uuid_t uuid{}; @@ -227,4 +240,4 @@ TEST(UUIDComparison, NonEmptyUUID) EXPECT_FALSE(oc_uuid_is_equal(uuid, gen_uuid)); EXPECT_FALSE(oc_uuid_is_equal(gen_uuid, uuid)); -} \ No newline at end of file +} diff --git a/api/unittest/vodmaptest.cpp b/api/unittest/vodmaptest.cpp new file mode 100644 index 0000000000..c452862d2c --- /dev/null +++ b/api/unittest/vodmaptest.cpp @@ -0,0 +1,529 @@ +/****************************************************************** + * + * Copyright 2020 Intel Corporation + * Copyright 2023 ETRI Joo-Chul Kevin Lee + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************/ + +#include "util/oc_features.h" + +#ifdef OC_HAS_FEATURE_BRIDGE + +#include +#include +#define OC_STORAGE + +#include "oc_api.h" +#include "oc_bridge.h" +#include +#include "port/oc_storage.h" + +#include +#include + +using test_vod = struct test_vod +{ + std::string vod_id; + std::string eco_system; +}; + +class VodMapTest : public testing::Test { +public: + int dirExists(const char *const path); + std::vector tv = { + { "1b32e152-3756-4fb6-b3f2-d8db7aafe39f", "ABC" }, + { "f959f6fd-8d08-4766-849b-74c3eec5e041", "ABC" }, + { "02feb15a-bf94-4f33-9794-adfb25c7bc60", "XYZ" }, + { "686ef93d-36e0-47fc-8316-fbd7045e850a", "ABC" }, + { "686ef93d-36e0-47fc-8316-fbd7045e850a", "XYZ" } + }; + +protected: + void SetUp() override + { +#ifdef OC_STORAGE +#if defined(_WIN32_) + mkdir("vod_map_test_dir"); +#elif defined(__linux__) + mkdir("vod_map_test_dir", + S_IRWXU | S_IRWXG | S_IRWXG /* 0777 permissions*/); +#endif /* if defined(_WIN32_) */ + oc_storage_config("./vod_map_test_dir/"); +#endif /* OC_STORAGE */ + } + void TearDown() override + { + remove("./vod_map_test_dir/vod_map"); + remove("./vod_map_test_dir/"); + } +}; + +TEST_F(VodMapTest, vod_map_add_id) +{ + oc_vod_map_init(); + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[0].vod_id.c_str(), + strlen(tv[0].vod_id.c_str()), + tv[0].eco_system.c_str()), + 0); + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[1].vod_id.c_str(), + strlen(tv[1].vod_id.c_str()), + tv[1].eco_system.c_str()), + 0); + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[2].vod_id.c_str(), + strlen(tv[2].vod_id.c_str()), + tv[2].eco_system.c_str()), + 0); + + // The vod map code actually expects the zero device to always be a bridge or + // other device for that reason we are inserting a dummy device at index zero + // this will be dumped into the output map file but shouldn't effect the + // test results. + const char *dummy = "dummy"; + EXPECT_EQ( + oc_vod_map_add_mapping_entry((uint8_t *)dummy, strlen(dummy), dummy), 0); + EXPECT_EQ(oc_vod_map_add_mapping_entry((uint8_t *)tv[0].vod_id.c_str(), + strlen(tv[0].vod_id.c_str()), + tv[0].eco_system.c_str()), + 1); + EXPECT_EQ(oc_vod_map_add_mapping_entry((uint8_t *)tv[1].vod_id.c_str(), + strlen(tv[1].vod_id.c_str()), + tv[1].eco_system.c_str()), + 2); + + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[0].vod_id.c_str(), + strlen(tv[0].vod_id.c_str()), + tv[0].eco_system.c_str()), + 1); + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[1].vod_id.c_str(), + strlen(tv[1].vod_id.c_str()), + tv[1].eco_system.c_str()), + 2); + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[2].vod_id.c_str(), + strlen(tv[2].vod_id.c_str()), + tv[2].eco_system.c_str()), + 0); + + oc_vod_map_free(); +} + +TEST_F(VodMapTest, vod_map_remove_id) +{ + oc_vod_map_init(); + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[0].vod_id.c_str(), + strlen(tv[0].vod_id.c_str()), + tv[0].eco_system.c_str()), + 0); + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[1].vod_id.c_str(), + strlen(tv[1].vod_id.c_str()), + tv[1].eco_system.c_str()), + 0); + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[2].vod_id.c_str(), + strlen(tv[2].vod_id.c_str()), + tv[2].eco_system.c_str()), + 0); + + // The vod map code actually expects the zero device to always be a bridge or + // other device for that reason we are inserting a dummy device at index zero + // this will be dumped into the output map file but shouldn't effect the + // test results. + const char *dummy = "dummy"; + EXPECT_EQ( + oc_vod_map_add_mapping_entry((uint8_t *)dummy, strlen(dummy), dummy), 0); + EXPECT_EQ(oc_vod_map_add_mapping_entry((uint8_t *)tv[0].vod_id.c_str(), + strlen(tv[0].vod_id.c_str()), + tv[0].eco_system.c_str()), + 1); + EXPECT_EQ(oc_vod_map_add_mapping_entry((uint8_t *)tv[1].vod_id.c_str(), + strlen(tv[1].vod_id.c_str()), + tv[1].eco_system.c_str()), + 2); + + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[0].vod_id.c_str(), + strlen(tv[0].vod_id.c_str()), + tv[0].eco_system.c_str()), + 1); + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[1].vod_id.c_str(), + strlen(tv[1].vod_id.c_str()), + tv[1].eco_system.c_str()), + 2); + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[2].vod_id.c_str(), + strlen(tv[2].vod_id.c_str()), + tv[2].eco_system.c_str()), + 0); + + oc_vod_map_remove_mapping_entry(1); + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[0].vod_id.c_str(), + strlen(tv[0].vod_id.c_str()), + tv[0].eco_system.c_str()), + 0); + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[1].vod_id.c_str(), + strlen(tv[1].vod_id.c_str()), + tv[1].eco_system.c_str()), + 2); + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[2].vod_id.c_str(), + strlen(tv[2].vod_id.c_str()), + tv[2].eco_system.c_str()), + 0); + + EXPECT_EQ(oc_vod_map_add_mapping_entry((uint8_t *)tv[0].vod_id.c_str(), + strlen(tv[0].vod_id.c_str()), + tv[0].eco_system.c_str()), + 1); + EXPECT_EQ(oc_vod_map_add_mapping_entry((uint8_t *)tv[1].vod_id.c_str(), + strlen(tv[1].vod_id.c_str()), + tv[1].eco_system.c_str()), + 2); + EXPECT_EQ(oc_vod_map_add_mapping_entry((uint8_t *)tv[2].vod_id.c_str(), + strlen(tv[2].vod_id.c_str()), + tv[2].eco_system.c_str()), + 3); + + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[0].vod_id.c_str(), + strlen(tv[0].vod_id.c_str()), + tv[0].eco_system.c_str()), + 1); + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[1].vod_id.c_str(), + strlen(tv[1].vod_id.c_str()), + tv[1].eco_system.c_str()), + 2); + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[2].vod_id.c_str(), + strlen(tv[2].vod_id.c_str()), + tv[2].eco_system.c_str()), + 3); + + oc_vod_map_reset(); + + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[0].vod_id.c_str(), + strlen(tv[0].vod_id.c_str()), + tv[0].eco_system.c_str()), + 0); + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[1].vod_id.c_str(), + strlen(tv[1].vod_id.c_str()), + tv[1].eco_system.c_str()), + 0); + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[2].vod_id.c_str(), + strlen(tv[2].vod_id.c_str()), + tv[2].eco_system.c_str()), + 0); + + oc_vod_map_free(); + + // test intermittent removal of vod_id.c_strs + + oc_vod_map_init(); + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[0].vod_id.c_str(), + strlen(tv[0].vod_id.c_str()), + tv[0].eco_system.c_str()), + 0); + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[1].vod_id.c_str(), + strlen(tv[1].vod_id.c_str()), + tv[1].eco_system.c_str()), + 0); + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[2].vod_id.c_str(), + strlen(tv[2].vod_id.c_str()), + tv[2].eco_system.c_str()), + 0); + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[3].vod_id.c_str(), + strlen(tv[3].vod_id.c_str()), + tv[3].eco_system.c_str()), + 0); + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[4].vod_id.c_str(), + strlen(tv[4].vod_id.c_str()), + tv[4].eco_system.c_str()), + 0); + + // The vod map code actually expects the zero device to always be a bridge or + // other device for that reason we are inserting a dummy device at index zero + // this will be dumped into the output map file but shouldn't effect the + // test results. + EXPECT_EQ( + oc_vod_map_add_mapping_entry((uint8_t *)dummy, strlen(dummy), dummy), 0); + EXPECT_EQ(oc_vod_map_add_mapping_entry((uint8_t *)tv[0].vod_id.c_str(), + strlen(tv[0].vod_id.c_str()), + tv[0].eco_system.c_str()), + 1); + EXPECT_EQ(oc_vod_map_add_mapping_entry((uint8_t *)tv[1].vod_id.c_str(), + strlen(tv[1].vod_id.c_str()), + tv[1].eco_system.c_str()), + 2); + EXPECT_EQ(oc_vod_map_add_mapping_entry((uint8_t *)tv[2].vod_id.c_str(), + strlen(tv[2].vod_id.c_str()), + tv[2].eco_system.c_str()), + 3); + EXPECT_EQ(oc_vod_map_add_mapping_entry((uint8_t *)tv[3].vod_id.c_str(), + strlen(tv[3].vod_id.c_str()), + tv[3].eco_system.c_str()), + 4); + EXPECT_EQ(oc_vod_map_add_mapping_entry((uint8_t *)tv[4].vod_id.c_str(), + strlen(tv[4].vod_id.c_str()), + tv[4].eco_system.c_str()), + 5); + + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[0].vod_id.c_str(), + strlen(tv[0].vod_id.c_str()), + tv[0].eco_system.c_str()), + 1); + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[1].vod_id.c_str(), + strlen(tv[1].vod_id.c_str()), + tv[1].eco_system.c_str()), + 2); + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[2].vod_id.c_str(), + strlen(tv[2].vod_id.c_str()), + tv[2].eco_system.c_str()), + 3); + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[3].vod_id.c_str(), + strlen(tv[3].vod_id.c_str()), + tv[3].eco_system.c_str()), + 4); + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[4].vod_id.c_str(), + strlen(tv[4].vod_id.c_str()), + tv[4].eco_system.c_str()), + 5); + + oc_vod_map_remove_mapping_entry(2); + oc_vod_map_remove_mapping_entry(4); + + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[0].vod_id.c_str(), + strlen(tv[0].vod_id.c_str()), + tv[0].eco_system.c_str()), + 1); + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[1].vod_id.c_str(), + strlen(tv[1].vod_id.c_str()), + tv[1].eco_system.c_str()), + 0); + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[2].vod_id.c_str(), + strlen(tv[2].vod_id.c_str()), + tv[2].eco_system.c_str()), + 3); + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[3].vod_id.c_str(), + strlen(tv[3].vod_id.c_str()), + tv[3].eco_system.c_str()), + 0); + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[4].vod_id.c_str(), + strlen(tv[4].vod_id.c_str()), + tv[4].eco_system.c_str()), + 5); + + EXPECT_EQ(oc_vod_map_add_mapping_entry((uint8_t *)tv[3].vod_id.c_str(), + strlen(tv[3].vod_id.c_str()), + tv[3].eco_system.c_str()), + 2); + EXPECT_EQ(oc_vod_map_add_mapping_entry((uint8_t *)tv[1].vod_id.c_str(), + strlen(tv[1].vod_id.c_str()), + tv[1].eco_system.c_str()), + 4); + + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[0].vod_id.c_str(), + strlen(tv[0].vod_id.c_str()), + tv[0].eco_system.c_str()), + 1); + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[1].vod_id.c_str(), + strlen(tv[1].vod_id.c_str()), + tv[1].eco_system.c_str()), + 4); + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[2].vod_id.c_str(), + strlen(tv[2].vod_id.c_str()), + tv[2].eco_system.c_str()), + 3); + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[3].vod_id.c_str(), + strlen(tv[3].vod_id.c_str()), + tv[3].eco_system.c_str()), + 2); + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[4].vod_id.c_str(), + strlen(tv[4].vod_id.c_str()), + tv[4].eco_system.c_str()), + 5); + + oc_vod_map_reset(); + oc_vod_map_free(); + + // test consecutive removal of vod_id.c_strs + + oc_vod_map_init(); + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[0].vod_id.c_str(), + strlen(tv[0].vod_id.c_str()), + tv[0].eco_system.c_str()), + 0); + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[1].vod_id.c_str(), + strlen(tv[1].vod_id.c_str()), + tv[1].eco_system.c_str()), + 0); + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[2].vod_id.c_str(), + strlen(tv[2].vod_id.c_str()), + tv[2].eco_system.c_str()), + 0); + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[3].vod_id.c_str(), + strlen(tv[3].vod_id.c_str()), + tv[3].eco_system.c_str()), + 0); + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[4].vod_id.c_str(), + strlen(tv[4].vod_id.c_str()), + tv[4].eco_system.c_str()), + 0); + + // The vod map code actually expects the zero device to always be a bridge or + // other device for that reason we are inserting a dummy device at index zero + // this will be dumped into the output map file but shouldn't effect the + // test results. + EXPECT_EQ( + oc_vod_map_add_mapping_entry((uint8_t *)dummy, strlen(dummy), dummy), 0); + EXPECT_EQ(oc_vod_map_add_mapping_entry((uint8_t *)tv[0].vod_id.c_str(), + strlen(tv[0].vod_id.c_str()), + tv[0].eco_system.c_str()), + 1); + EXPECT_EQ(oc_vod_map_add_mapping_entry((uint8_t *)tv[1].vod_id.c_str(), + strlen(tv[1].vod_id.c_str()), + tv[1].eco_system.c_str()), + 2); + EXPECT_EQ(oc_vod_map_add_mapping_entry((uint8_t *)tv[2].vod_id.c_str(), + strlen(tv[2].vod_id.c_str()), + tv[2].eco_system.c_str()), + 3); + EXPECT_EQ(oc_vod_map_add_mapping_entry((uint8_t *)tv[3].vod_id.c_str(), + strlen(tv[3].vod_id.c_str()), + tv[3].eco_system.c_str()), + 4); + EXPECT_EQ(oc_vod_map_add_mapping_entry((uint8_t *)tv[4].vod_id.c_str(), + strlen(tv[4].vod_id.c_str()), + tv[4].eco_system.c_str()), + 5); + + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[0].vod_id.c_str(), + strlen(tv[0].vod_id.c_str()), + tv[0].eco_system.c_str()), + 1); + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[1].vod_id.c_str(), + strlen(tv[1].vod_id.c_str()), + tv[1].eco_system.c_str()), + 2); + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[2].vod_id.c_str(), + strlen(tv[2].vod_id.c_str()), + tv[2].eco_system.c_str()), + 3); + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[3].vod_id.c_str(), + strlen(tv[3].vod_id.c_str()), + tv[3].eco_system.c_str()), + 4); + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[4].vod_id.c_str(), + strlen(tv[4].vod_id.c_str()), + tv[4].eco_system.c_str()), + 5); + + oc_vod_map_remove_mapping_entry(2); + oc_vod_map_remove_mapping_entry(4); + oc_vod_map_remove_mapping_entry(3); + + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[0].vod_id.c_str(), + strlen(tv[0].vod_id.c_str()), + tv[0].eco_system.c_str()), + 1); + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[1].vod_id.c_str(), + strlen(tv[1].vod_id.c_str()), + tv[1].eco_system.c_str()), + 0); + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[2].vod_id.c_str(), + strlen(tv[2].vod_id.c_str()), + tv[2].eco_system.c_str()), + 0); + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[3].vod_id.c_str(), + strlen(tv[3].vod_id.c_str()), + tv[3].eco_system.c_str()), + 0); + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[4].vod_id.c_str(), + strlen(tv[4].vod_id.c_str()), + tv[4].eco_system.c_str()), + 5); + + EXPECT_EQ(oc_vod_map_add_mapping_entry((uint8_t *)tv[3].vod_id.c_str(), + strlen(tv[3].vod_id.c_str()), + tv[3].eco_system.c_str()), + 2); + EXPECT_EQ(oc_vod_map_add_mapping_entry((uint8_t *)tv[2].vod_id.c_str(), + strlen(tv[2].vod_id.c_str()), + tv[2].eco_system.c_str()), + 3); + EXPECT_EQ(oc_vod_map_add_mapping_entry((uint8_t *)tv[1].vod_id.c_str(), + strlen(tv[1].vod_id.c_str()), + tv[1].eco_system.c_str()), + 4); + + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[0].vod_id.c_str(), + strlen(tv[0].vod_id.c_str()), + tv[0].eco_system.c_str()), + 1); + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[1].vod_id.c_str(), + strlen(tv[1].vod_id.c_str()), + tv[1].eco_system.c_str()), + 4); + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[2].vod_id.c_str(), + strlen(tv[2].vod_id.c_str()), + tv[2].eco_system.c_str()), + 3); + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[3].vod_id.c_str(), + strlen(tv[3].vod_id.c_str()), + tv[3].eco_system.c_str()), + 2); + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[4].vod_id.c_str(), + strlen(tv[4].vod_id.c_str()), + tv[4].eco_system.c_str()), + 5); + + oc_vod_map_free(); +} + +TEST_F(VodMapTest, vod_map_add_same_id_different_econame) +{ + oc_vod_map_init(); + // verify the vod_id.c_str() are not yet added + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[3].vod_id.c_str(), + strlen(tv[3].vod_id.c_str()), + tv[3].eco_system.c_str()), + 0); + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[4].vod_id.c_str(), + strlen(tv[4].vod_id.c_str()), + tv[4].eco_system.c_str()), + 0); + + // The vod map code actually expects the zero device to always be a bridge or + // other device for that reason we are inserting a dummy device at index zero + // this will be dumped into the output map file but shouldn't effect the + // test results. + const char *dummy = "dummy"; + EXPECT_EQ( + oc_vod_map_add_mapping_entry((uint8_t *)dummy, strlen(dummy), dummy), 0); + + // even though tv[3] and tv[4] have the same vod_id.c_str() they have + // different econames and should each get a different index + size_t vod_index3 = oc_vod_map_add_mapping_entry( + (uint8_t *)tv[3].vod_id.c_str(), strlen(tv[3].vod_id.c_str()), + tv[3].eco_system.c_str()); + size_t vod_index4 = oc_vod_map_add_mapping_entry( + (uint8_t *)tv[4].vod_id.c_str(), strlen(tv[4].vod_id.c_str()), + tv[4].eco_system.c_str()); + EXPECT_NE(vod_index3, vod_index4); + + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[3].vod_id.c_str(), + strlen(tv[3].vod_id.c_str()), + tv[3].eco_system.c_str()), + 1); + EXPECT_EQ(oc_vod_map_get_vod_index((uint8_t *)tv[4].vod_id.c_str(), + strlen(tv[4].vod_id.c_str()), + tv[4].eco_system.c_str()), + 2); + + oc_vod_map_free(); +} + +#endif /* OC_HAS_FEATURE_BRIDGE */ diff --git a/apps/CMakeLists.txt b/apps/CMakeLists.txt index f7a1d4fa16..55791d7a06 100644 --- a/apps/CMakeLists.txt +++ b/apps/CMakeLists.txt @@ -218,6 +218,14 @@ if(UNIX) DEPENDENCIES client-server-static ) endif() + + if(OC_BRIDGE_ENABLED) + oc_add_app_executable( + TARGET dummy_bridge_linux + SOURCES ${PROJECT_SOURCE_DIR}/dummy_bridge_linux.c + DEPENDENCIES client-server-static + ) + endif() if(EXISTS ${PROJECT_SOURCE_DIR}/device_builder_server.c) oc_add_app_executable( diff --git a/apps/Readme.md b/apps/Readme.md index 807f0915d6..4cd466a926 100644 --- a/apps/Readme.md +++ b/apps/Readme.md @@ -152,7 +152,11 @@ naming convention: - ### push_targetserver_multithread_linux.c: PUSH target server which receives PUSH update request from PUSH origin server. It plays the role of PUSH configurator at the same time. - - for more detail see [OCF Push Notification](./docs/push.md) + - for more detail see [Push Notification](./docs/push.md) + +- ### dummy_bridge_linux.c: + VOD testing sample for Bridging feature. + - for more detail see [Bridging](./docs/bridging.md) - ### Other files: The JSON files are the introspection files in JSON. diff --git a/apps/docs/bridging.md b/apps/docs/bridging.md new file mode 100644 index 0000000000..0b4566f7a1 --- /dev/null +++ b/apps/docs/bridging.md @@ -0,0 +1,260 @@ +# Introduction +> Joo-Chul Kevin Lee (rune@etri.re.kr) + +OCF provides bridging feature to support interaction with other NON-OCF ecosystem devices. + +## VOD (Virtual OCF Device) +- OCF Briding feature is based on **VOD (Virtual OCF Device)** concept. +- A VOD an OCF Device which is exactly same as a normal OCF Device except that it represents non-OCF Device and it has additional device type value (i.e. "`oic.d.virtual`"). This additional device type can help users to distinguish VODs from other normal OCF Devices. + +## VOD vs. Bridge Device +- **Bridge Device** is another special OCF Device used to manage VODs. It also has additional device type value (i.e. "`oic.d.bridge`") to identify it from other normal OCF Devices. An OCF Platform works as a Bridge should have one Bridge Device. +- VODs have dependency on a Bridge Device for security reason. In the initial state (the Bridge Device has not been onboarded yet) all other VODs shall be reset state, so they can't be onboarded until the Bridge Device is onboarded. Once the Bridge Device is onboarded all VODs become online and they are ready to be onboarded (RFOTM). If the Bridge Device is reset all otxher VODs shall be reset too (they shall be in RFOTM state). Below figure shows the state diagram of VOD. + + ![bridging_vod_statemachine.png](./resources/bridging_vod_statemachine.png)) +## VOD list Resource +- VOD list Resource (rt : "`oic.r.vodlist`") maintains the list of current onboarded VODs. It has an array property ("`vods`") which keeps the list of VOD information ("`vodentry`"). Each `vodentry` has 3 Properties: `name`, `Device ID`, `econame`. VOD list Resource is managed by the Bridge Device ("`oic.d.bridge`"). + ```json + "vodentry" : { + "description": "Information for a VOD created by the Bridge", + "type": "object", + "properties": { + "n": { + "$ref": "https://openconnectivityfoundation.github.io/core/schemas/oic.common.properties.core-schema.json#/definitions/n" + }, + "di" : { + "$ref": "https://openconnectivityfoundation.github.io/core/schemas/oic.types-schema.json#/definitions/uuid" + }, + "econame": { + "description": "Ecosystem Name of the Bridged Device which is exposed by this VOD", + "type": "string", + "enum": [ "Matter", "BLE", "oneM2M", "UPlus", "Zigbee", "Z-Wave", "EnOcean", "AllJoyn", "LWM2M" ], + "readOnly": true + } + } + } + + /* vod list Resource */ + "vodlist": { + "rt" : { + "description": "Resource Type", + "items": { + "maxLength": 64, + "type": "string", + "enum": ["oic.r.vodlist"] + }, + "minItems": 1, + "uniqueItems": true, + "readOnly": true, + "type": "array" + }, + "vods": { + "description": "Array of information per VOD created by the Bridge", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "readOnly": true, + "items": { + "$ref": "#/definitions/vodentry" + } + } + } + ``` +
+ +# Bridge Framework Implementation in iotivity-lite +Bridge Framework implementation in iotivity-lite is composed of following components: + +## Iotivity-lite Bridging feature : +- supports VOD feature in iotivity-lite stack + +## [Bridge Manager](./../bridge_manager/readme.md) : +- manages ecosystem translation plugin modules +- manages VODs list Resources +- manages VODs state machine +- interacts with CLI + +## [Bridge CLI](./../bridge_cli/readme.md) : +- operates ecosystem translation plugins +- handles CRUDN request from users + +## [Translation Plugin per each Non-OCF Ecosystem](./../matter_translation_plugin/readme.md) +- does OCF Server role +- does non-OCF Client role +- does data model translation +- pairing/unpairing non-OCF Devices + +![bridging_implementation.png](./resources/bridging_implementation.png) +
+ +# How to enable Bridging feature of iotivity-lite +- CMake + ```cmake + OC_DYNAMIC_ALLOCATION_ENABLED = ON + OC_BRIDGE_ENABLED = ON + ``` + +- Makefile + ```console + $ make BRIDGE=1 DYNAMIC=1 + $ sudo make BRIDGE=1 DYNAMIC=1 install + ``` +
+ +# API +## oc_bridge_add_bridge_device() +Adds Bridge Device ("`oic.d.bridge`"). +```cpp +/** + * Add an oic.d.bridge device. + * + * The oic.r.vodlist resource will be registered to the bridge device. + * + * @param[in] name the user readable name of the device + * @param[in] spec_version The version of the OCF Server. + * This is the "icv" device property + * @param[in] data_model_version Spec version of the resource and device + * specifications to which this device data model + * is implemented. This is the "dmv" device + * property + * @param[in] add_device_cb callback function invoked during oc_add_device(). + * The purpose is to add additional device properties + * that are not supplied to + * oc_bridge_add_bridge_device() function call. + * @param[in] data context pointer that is passed to the oc_add_device_cb_t + * + * @return + * `0` on success + * `-1` on failure + */ +OC_API +int oc_bridge_add_bridge_device(const char *name, const char *spec_version, + const char *data_model_version, + oc_add_device_cb_t add_device_cb, void *data); +``` + +## oc_bridge_add_virtual_device() +Adds a VOD to the stack. +```cpp +/** + * Add a virtual ocf device to the the stack. + * + * This function is called to add a newly discovered non-ocf device to a bridge + * device. This will typically be called in response to the non-ocf devices + * discovery mechanism. + * + * The `oc_bridge_add_virtual_device()` function may be called as many times as + * needed. Each call will add a new device to the stack with its own port + * address. Each device is automatically assigned a device index number. Unlike + * the `oc_add_device()` function this number is not incremented by one but + * assigned an index number based on avalibility. The index assigned to the + * virtual device will be returned from the function call. The function + * `oc_bridge_get_virtual_device_index()` can also be used to get the logical + * device index number after this function call. + * + * The function `oc_bridge_add_bridge_device()` must be called before this + * function. + * + * @param virtual_device_id a unique identifier that identifies the virtual + * device this could be a UUID, serial number or other + * means of uniquely identifying the device + * @param virtual_device_id_size size in bytes of the virtual_device_id param + * @param econame ecosystem name of the bridged device which is exposed by this + * virtual device + * @param uri the The device URI. The wellknown default URI "/oic/d" is hosted + * by every server. Used to device specific information. + * @param rt the resource type + * @param name the user readable name of the device + * @param spec_version The version of the OCF Server. This is the "icv" device + * property + * @param data_model_version Spec version of the resource and device + * specifications to which this device data model is + * implemented. This is the "dmv" device property + * @param add_device_cb callback function invoked during oc_add_device(). The + * purpose is to add additional device properties that are + * not supplied to oc_add_device() function call. + * @param data context pointer that is passed to the oc_add_device_cb_t + * + * @return + * - the logical index of the virtual device on success + * - `0` on failure since a bridge device is required to add virtual devices + a zero index cannot be assigned to a virtual device. + * + * @note device index is cast from size_t to int and may lose information. + * The `oc_bridge_add_virtual_device()` function can be used to get + * the non-cast device index. + * @note The function `oc_bridge_add_bridge_device()` must be called before this + * function. + * + * @see init + */ +OC_API +size_t oc_bridge_add_virtual_device( + const uint8_t *virtual_device_id, size_t virtual_device_id_size, + const char *econame, const char *uri, const char *rt, const char *name, + const char *spec_version, const char *data_model_version, + oc_add_device_cb_t add_device_cb, void *data); +``` + +## oc_bridge_add_vod() +Adds new vodentry to the VOD list Resource ("`oic.r.vodlist`") +```cpp +/** + * @brief add new vodentry for an existing VOD to "oic.r.vodlist:vods". + * This function is usually called after `oc_bridge_remove_virtual_device()` + * is called. + * This function DOES NOT add new Device to `g_oc_device_info[]`, but + * just re-registre existing VOD to "oic.r.vodlist:vods" list. + * + * @param device_index Device index of VOD to be online + * @return 0: success, -1: failure + */ +OC_API +int oc_bridge_add_vod(size_t device_index); +``` + +## oc_bridge_remove_virtual_device() +Removes a VOD from the VOD list Resource ("`oic.r.vodlist`") and shutdown connectivity of it. +```cpp +/** + * If the non-ocf device is no longer reachable this can be used to remove + * the virtual device from the bridge device. + * + * This will shutdown network connectivity for the device and will update + * the vodslist resource found on the bridge. + * + * Any any persistant settings will remain unchanged. If the virtual device has + * already been onboarded and permission settings have been modified when the + * device is added again using `oc_bridge_add_virtual_device` those + * persistant settings will still be in place. + * + * @param device_index the index of the virtual device + * + * @return + * - `0` on succes + * - `-1` on failure + */ +OC_API +int oc_bridge_remove_virtual_device(size_t device_index); +``` + +## oc_bridge_delete_virtual_device() +Removes a VOD from the stack and the VOD list Resource ("`oic.r.vodlist`"). +```cpp +/** + * This will remove the virtual device and free memory associated with that + * device. + * + * Delete virtual device will remove all persistant settings. If the virtual + * device is added again the onboarding and device permissions will need to be + * setup as if the device were a new device. + * + * @param device_index index of teh virtual device + * + * @return + * - `0` on success + * - `-1` on failure + */ +OC_API +int oc_bridge_delete_virtual_device(size_t device_index); +``` \ No newline at end of file diff --git a/apps/docs/resources/bridging_implementation.png b/apps/docs/resources/bridging_implementation.png new file mode 100644 index 0000000000..075f991145 Binary files /dev/null and b/apps/docs/resources/bridging_implementation.png differ diff --git a/apps/docs/resources/bridging_vod_statemachine.png b/apps/docs/resources/bridging_vod_statemachine.png new file mode 100644 index 0000000000..90277fdf0d Binary files /dev/null and b/apps/docs/resources/bridging_vod_statemachine.png differ diff --git a/apps/dummy_bridge_bridge_device_IDD.cbor b/apps/dummy_bridge_bridge_device_IDD.cbor new file mode 100644 index 0000000000..71c9e4b3e3 Binary files /dev/null and b/apps/dummy_bridge_bridge_device_IDD.cbor differ diff --git a/apps/dummy_bridge_bridge_device_IDD.json b/apps/dummy_bridge_bridge_device_IDD.json new file mode 100644 index 0000000000..1015d909af --- /dev/null +++ b/apps/dummy_bridge_bridge_device_IDD.json @@ -0,0 +1,303 @@ +{ + "schemes": [ + "http" + ], + "swagger": "2.0", + "consumes": [ + "application/json" + ], + "paths": { + "/bridge/vodlist": { + "get": { + "description": "This resource describes the VODs that have been onboarded on the Bridge Platform.\n", + "responses": { + "200": { + "schema": { + "$ref": "#/definitions/vodlist" + }, + "description": "" + } + }, + "parameters": [ + { + "$ref": "#/parameters/interface-r" + }, + { + "schema": { + "$ref": "#/definitions/vodlist" + }, + "in": "body", + "required": true, + "name": "body" + } + ] + } + }, + "/oic/p": { + "get": { + "responses": { + "200": { + "schema": { + "$ref": "#/definitions/Platform" + }, + "description": "" + } + }, + "parameters": [ + { + "$ref": "#/parameters/interface-r" + } + ], + "description": "" + } + } + }, + "definitions": { + "vodlist": { + "properties": { + "n": { + "type": "string", + "maxLength": 64, + "readOnly": true, + "description": "Friendly name of the Resource" + }, + "rt": { + "minItems": 1, + "items": { + "enum": [ + "oic.r.vodlist" + ], + "type": "string", + "maxLength": 64 + }, + "description": "Resource Type", + "uniqueItems": true, + "readOnly": true, + "default": [ + "oic.r.vodlist" + ], + "type": "array" + }, + "if": { + "minItems": 2, + "items": { + "enum": [ + "oic.if.r", + "oic.if.baseline" + ], + "type": "string" + }, + "description": "The OCF Interface set supported by this Resource", + "uniqueItems": true, + "readOnly": true, + "type": "array" + }, + "vods": { + "description": "Array of information per VOD created by the Bridge", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "readOnly": true, + "items": { + "$ref": "#/definitions/vodentry" + } + } + }, + "required": [ "vods" ], + "type": "object" + }, + "vodentry": { + "description": "Information for a VOD created by the Bridge", + "type": "object", + "properties": { + "n": { + "type": "string", + "maxLength": 64, + "readOnly": true, + "description": "Friendly name of the Resource" + }, + "di": { + "type": "string", + "pattern": "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$", + "readOnly": true, + "description": "Format pattern according to IETF RFC 4122." + }, + "econame": { + "type": "string", + "enum": [ "ABC", "XYZ" ], + "readOnly": true, + "description": "Ecosystem Name of the Bridged Device which is exposed by this VOD" + } + }, + "required": ["n", "di", "econame"] + }, + "Platform": { + "properties": { + "id": { + "type": "string", + "readOnly": true, + "maxLength": 64, + "description": "" + }, + "if": { + "minItems": 2, + "items": { + "enum": [ + "oic.if.r", + "oic.if.baseline" + ], + "type": "string", + "maxLength": 64 + }, + "description": "", + "uniqueItems": true, + "readOnly": true, + "type": "array" + }, + "n": { + "type": "string", + "readOnly": true, + "maxLength": 64, + "description": "" + }, + "mnmn": { + "type": "string", + "readOnly": true, + "maxLength": 64, + "description": "" + }, + "mnnct": { + "minItems": 1, + "items": { + "minimum": 1, + "type": "integer", + "description": "" + }, + "readOnly": true, + "type": "array", + "description": "" + }, + "mnml": { + "format": "uri", + "type": "string", + "readOnly": true, + "maxLength": 256, + "description": "" + }, + "mnsel": { + "type": "string", + "readOnly": true, + "maxLength": 64, + "description": "" + }, + "mnpv": { + "type": "string", + "readOnly": true, + "maxLength": 64, + "description": "" + }, + "mnfv": { + "type": "string", + "readOnly": true, + "maxLength": 64, + "description": "" + }, + "rt": { + "minItems": 1, + "items": { + "enum": [ + "oic.wk.p" + ], + "type": "string", + "maxLength": 64 + }, + "description": "", + "uniqueItems": true, + "readOnly": true, + "default": [ + "oic.wk.p" + ], + "type": "array" + }, + "mnos": { + "type": "string", + "readOnly": true, + "maxLength": 64, + "description": "" + }, + "mnhw": { + "type": "string", + "readOnly": true, + "maxLength": 64, + "description": "" + }, + "vid": { + "type": "string", + "readOnly": true, + "maxLength": 64, + "description": "" + }, + "mnmo": { + "type": "string", + "readOnly": true, + "maxLength": 64, + "description": "" + }, + "mndt": { + "readOnly": true, + "type": "string", + "pattern": "^([0-9]{4})-(1[0-2]|0[1-9])-(3[0-1]|2[0-9]|1[0-9]|0[1-9])$", + "description": "" + }, + "pi": { + "readOnly": true, + "type": "string", + "pattern": "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$", + "description": "" + }, + "st": { + "format": "date-time", + "readOnly": true, + "type": "string", + "description": "" + }, + "mnsl": { + "format": "uri", + "type": "string", + "readOnly": true, + "maxLength": 256, + "description": "" + } + }, + "required": [ + "pi", + "mnmn" + ], + "type": "object" + } + }, + "info": { + "version": "20190215", + "title": "serverlite24521", + "termsOfService": "https://openconnectivityfoundation.github.io/core/DISCLAIMER.md", + "license": { + "x-copyright": "copyright 2016-2017, 2019 Open Connectivity Foundation, Inc. All rights reserved.", + "url": "https://github.com/openconnectivityfoundation/core/blob/e28a9e0a92e17042ba3e83661e4c0fbce8bdc4ba/LICENSE.md", + "name": "OCF Data Model License" + } + }, + "parameters": { + "interface-r": { + "enum": [ + "oic.if.r", + "oic.if.baseline" + ], + "type": "string", + "in": "query", + "name": "if" + } + }, + "produces": [ + "application/json" + ] +} \ No newline at end of file diff --git a/apps/dummy_bridge_linux.c b/apps/dummy_bridge_linux.c new file mode 100644 index 0000000000..08feaacb2d --- /dev/null +++ b/apps/dummy_bridge_linux.c @@ -0,0 +1,800 @@ +/* +// Copyright (c) 2016 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include "oc_log.h" +#include "oc_api.h" +#include "oc_bridge.h" +#include "oc_core_res.h" +#include "port/oc_clock.h" +#if defined(_WIN32) +#include +#elif defined(__linux__) +#include +#else +#error "Unsupported OS" +#endif +#include +#include +#include +#include + +#if defined(OC_IDD_API) +#include "oc_introspection.h" +#endif + +#if defined(_WIN32) +static HANDLE event_thread; +static CRITICAL_SECTION app_sync_lock; +static CONDITION_VARIABLE cv; +static CRITICAL_SECTION cs; + +/* OS specific definition for lock/unlock */ +#define app_mutex_lock(m) EnterCriticalSection(&(m)) +#define app_mutex_unlock(m) LeaveCriticalSection(&(m)) + +#elif defined(__linux__) +static pthread_t event_thread; +static pthread_mutex_t app_sync_lock; +static pthread_mutex_t mutex; +static pthread_cond_t cv; + +/* OS specific definition for lock/unlock */ +#define app_mutex_lock(m) pthread_mutex_lock(&(m)) +#define app_mutex_unlock(m) pthread_mutex_unlock(&(m)) + +static struct timespec ts; +#endif + +int quit = 0; + +/* + * There are two ways that GET/POST/PUT calls can get the information about a + * virtual device. The information can be passed to the GET/PUT/POST callback + * via the user_data context pointer, or the device index can be used to obtain + * the virtual device information and that information can then be used to look + * up the virtual device. + * + * Both methods are shown in this sample if USE_VIRTUAL_DEVICE_LOOKUP is `1` + * then the device index will be used to obtain the virtual device info. If it + * is `0` then the information will be sent via the user_data context pointer. + */ +#define USE_VIRTUAL_DEVICE_LOOKUP 1 + +#define UUID_LEN 37 + +static bool discover_vitual_devices = true; +static bool display_ascii_ui = false; + +typedef struct virtual_light_t +{ + const char device_name[32]; + const char uuid[UUID_LEN]; + const char eco_system[32]; + bool on; + bool discovered; + bool added_to_bridge; +} virtual_light_t; + +#define VOD_COUNT 5 +struct virtual_light_t virtual_lights[VOD_COUNT] = { + { "Light 1", "1b32e152-3756-4fb6-b3f2-d8db7aafe39f", "ABC", true, false, + false }, + { "Light 2", "f959f6fd-8d08-4766-849b-74c3eec5e041", "ABC", false, false, + false }, + { "Light 3", "686ef93d-36e0-47fc-8316-fbd7045e850a", "ABC", true, false, + false }, + { "Light 4", "02feb15a-bf94-4f33-9794-adfb25c7bc60", "XYZ", false, false, + false }, + { "Light 5", "e2f0109f-ef7d-496a-9676-d3d87b38e52f", "XYZ", true, false, + false } +}; + +#if defined(_WIN32) +HANDLE hConsole; +CONSOLE_SCREEN_BUFFER_INFO consoleInfo; +WORD saved_attributes; + +#define C_RESET \ + do { \ + hConsole = GetStdHandle(STD_OUTPUT_HANDLE); \ + SetConsoleTextAttribute(hConsole, saved_attributes); \ + } while (false) +#define C_YELLOW \ + do { \ + hConsole = GetStdHandle(STD_OUTPUT_HANDLE); \ + GetConsoleScreenBufferInfo(hConsole, &consoleInfo); \ + saved_attributes = consoleInfo.wAttributes; \ + SetConsoleTextAttribute(hConsole, FOREGROUND_GREEN | FOREGROUND_RED | \ + FOREGROUND_INTENSITY); \ + } while (false) + +#elif defined(__linux__) +#define C_RESET OC_PRINTF("\x1B[0m") +#define C_YELLOW OC_PRINTF("\x1B[1;33m") +#endif + +static void +print_ascii_lights_ui(void) +{ + OC_PRINTF("\n"); + + for (size_t i = 0; i < VOD_COUNT; i++) { + if (virtual_lights[i].discovered) { + if (virtual_lights[i].on) { + C_YELLOW; + } + OC_PRINTF(" %s ", " _ "); + if (virtual_lights[i].on) { + C_RESET; + } + } else { + OC_PRINTF(" "); + } + } + OC_PRINTF("\n"); + for (size_t i = 0; i < VOD_COUNT; i++) { + if (virtual_lights[i].discovered) { + if (virtual_lights[i].on) { + C_YELLOW; + } + OC_PRINTF(" %s ", (virtual_lights[i].on) ? "(*)" : "(~)"); + if (virtual_lights[i].on) { + C_RESET; + } + } else { + OC_PRINTF(" "); + } + } + OC_PRINTF("\n"); + for (size_t i = 0; i < VOD_COUNT; i++) { + if (virtual_lights[i].discovered) { + if (virtual_lights[i].on) { + C_YELLOW; + } + OC_PRINTF(" %s ", " # "); + if (virtual_lights[i].on) { + C_RESET; + } + } else { + OC_PRINTF(" "); + } + } + OC_PRINTF("\n"); + for (size_t i = 0; i < VOD_COUNT; i++) { + if (virtual_lights[i].discovered) { + OC_PRINTF(" %s ", (virtual_lights[i].on) ? "ON " : "OFF"); + } else { + OC_PRINTF(" N/A "); + } + } + OC_PRINTF("\n"); +} + +static void +set_idd_from_file(const char *file_name, size_t device) +{ + (void)file_name; + (void)device; +#if defined(OC_IDD_API) + FILE *fp; + uint8_t *buffer; + size_t buffer_size; + const char introspection_error1[] = "\tERROR Could not read "; + const char introspection_error2[] = + "\tIntrospection data not set for device.\n"; + fp = fopen(file_name, "rb"); + if (fp) { + fseek(fp, 0, SEEK_END); + buffer_size = ftell(fp); + rewind(fp); + + buffer = (uint8_t *)malloc(buffer_size * sizeof(uint8_t)); + size_t fread_ret = fread(buffer, buffer_size, 1, fp); + fclose(fp); + + if (fread_ret == 1) { + oc_set_introspection_data(device, buffer, buffer_size); + OC_PRINTF("\tIntrospection data set for device.\n"); + } else { + OC_PRINTF("%s %s\n %s", introspection_error1, file_name, + introspection_error2); + } + free(buffer); + } else { + OC_PRINTF("%s %s\n %s", introspection_error1, file_name, + introspection_error2); + } +#endif +} + +static int +app_init(void) +{ + int ret = oc_init_platform("Desktop PC", NULL, NULL); + ret |= oc_bridge_add_bridge_device("Dummy Bridge", "ocf.2.0.0", + "ocf.res.1.0.0, ocf.sh.1.0.0", NULL, NULL); + return ret; +} + +static void +register_resources(void) +{ + set_idd_from_file("dummy_bridge_bridge_device_IDD.cbor", 0); +} + +static void +signal_event_loop(void) +{ +#if defined(_WIN32) + WakeConditionVariable(&cv); +#elif defined(__linux__) + app_mutex_lock(mutex); + pthread_cond_signal(&cv); + app_mutex_unlock(mutex); +#endif +} + +static void +handle_signal(int signal) +{ + (void)signal; + signal_event_loop(); + quit = 1; +} + +static virtual_light_t * +lookup_virtual_light(size_t device_index) +{ + oc_virtual_device_t *virtual_device_info = + oc_bridge_get_vod_mapping_info(device_index); + for (size_t i = 0; i < VOD_COUNT; ++i) { + if (strncmp(virtual_lights[i].eco_system, + oc_string(virtual_device_info->econame), 32) == 0) { + if (memcmp(virtual_lights[i].uuid, virtual_device_info->v_id, + virtual_device_info->v_id_size) == 0) { + return &virtual_lights[i]; + } + } + } + return NULL; +} + +static void +get_binary_switch(oc_request_t *request, oc_interface_mask_t iface_mask, + void *user_data) +{ + (void)user_data; + virtual_light_t *light = NULL; +#if USE_VIRTUAL_DEVICE_LOOKUP + light = lookup_virtual_light(request->resource->device); +#else + light = (virtual_light_t *)user_data; +#endif + + oc_status_t resp = OC_STATUS_OK; + oc_rep_begin_root_object(); + if (light) { + switch (iface_mask) { + case OC_IF_BASELINE: + oc_process_baseline_interface(request->resource); + /* fall through */ + case OC_IF_A: + case OC_IF_RW: + oc_rep_set_boolean(root, value, light->on); + break; + default: + resp = OC_STATUS_BAD_REQUEST; + break; + } + } else { + resp = OC_STATUS_BAD_REQUEST; + } + oc_rep_end_root_object(); + oc_send_response(request, resp); +} + +static void +post_binary_switch(oc_request_t *request, oc_interface_mask_t iface_mask, + void *user_data) +{ + (void)iface_mask; + (void)user_data; + virtual_light_t *light = NULL; +#if USE_VIRTUAL_DEVICE_LOOKUP + light = lookup_virtual_light(request->resource->device); +#else + light = (virtual_light_t *)user_data; +#endif + OC_PRINTF("POST_BinarySwitch\n"); + if (light) { + oc_rep_t *rep = request->request_payload; + if (rep != NULL) { + switch (rep->type) { + case OC_REP_BOOL: + oc_rep_get_bool(rep, "value", &light->on); + break; + default: + oc_send_response(request, OC_STATUS_BAD_REQUEST); + break; + } + } + if (display_ascii_ui) { + print_ascii_lights_ui(); + } + oc_send_response(request, OC_STATUS_CHANGED); + } else { + oc_send_response(request, OC_STATUS_BAD_REQUEST); + } +} + +static void +put_binary_switch(oc_request_t *request, oc_interface_mask_t iface_mask, + void *user_data) +{ + post_binary_switch(request, iface_mask, user_data); +} + +static void +register_binaryswitch_resource(const char *name, const char *uri, + size_t device_index, void *user_data) +{ + oc_resource_t *r = oc_new_resource(name, uri, 1, device_index); + oc_resource_bind_resource_type(r, "oic.r.switch.binary"); + oc_resource_bind_resource_interface(r, OC_IF_A); + oc_resource_set_default_interface(r, OC_IF_A); + oc_resource_set_discoverable(r, true); + oc_resource_set_request_handler(r, OC_GET, get_binary_switch, user_data); + oc_resource_set_request_handler(r, OC_POST, post_binary_switch, user_data); + oc_resource_set_request_handler(r, OC_PUT, put_binary_switch, user_data); + oc_add_resource(r); +} + +/* + * TODO place this in a thread loop + * When a device is discovered it will be added to + * the bridge as a virtual_device + */ +static void +poll_for_discovered_devices(void) +{ + size_t virtual_device_index; + for (size_t i = 0; i < VOD_COUNT; i++) { + if (virtual_lights[i].discovered && !virtual_lights[i].added_to_bridge) { + OC_PRINTF("Adding %s to bridge\n", virtual_lights[i].device_name); + app_mutex_lock(app_sync_lock); + + virtual_device_index = oc_bridge_add_virtual_device( + (uint8_t *)virtual_lights[i].uuid, OC_UUID_LEN, + virtual_lights[i].eco_system, "/oic/d", "oic.d.light", + virtual_lights[i].device_name, "ocf.2.0.0", + "ocf.res.1.0.0, ocf.sh.1.0.0", NULL, NULL); + if (virtual_device_index != 0) { +#if USE_VIRTUAL_DEVICE_LOOKUP + register_binaryswitch_resource(virtual_lights[i].device_name, + "/bridge/light/switch", + virtual_device_index, NULL); +#else + register_binaryswitch_resource( + virtual_lights[i].device_name, "/bridge/light/switch", + virtual_device_index, &virtual_lights[i]); +#endif + // the immutable_device_identifier ("piid") + oc_uuid_t piid; + oc_str_to_uuid(virtual_lights[i].uuid, &piid); + oc_set_immutable_device_identifier(virtual_device_index, &piid); + // Set Introspection Device Data + set_idd_from_file("dummy_bridge_virtual_light_IDD.cbor", + virtual_device_index); + } + + app_mutex_unlock(app_sync_lock); + virtual_lights[i].added_to_bridge = true; + } + } +} + +#if defined(_WIN32) +DWORD WINAPI +ocf_event_thread(LPVOID lpParam) +{ + oc_clock_time_t next_event; + while (quit != 1) { + app_mutex_lock(app_sync_lock); + next_event = oc_main_poll(); + app_mutex_unlock(app_sync_lock); + + if (next_event == 0) { + SleepConditionVariableCS(&cv, &cs, INFINITE); + } else { + oc_clock_time_t now = oc_clock_time(); + if (now < next_event) { + SleepConditionVariableCS( + &cv, &cs, (DWORD)((next_event - now) * 1000 / OC_CLOCK_SECOND)); + } + } + } + + oc_main_shutdown(); + return TRUE; +} +#elif defined(__linux__) +static void * +ocf_event_thread(void *data) +{ + (void)data; + oc_clock_time_t next_event; + while (quit != 1) { + app_mutex_lock(app_sync_lock); + next_event = oc_main_poll_v1(); + app_mutex_unlock(app_sync_lock); + + app_mutex_lock(mutex); + if (next_event == 0) { + pthread_cond_wait(&cv, &mutex); + } else { + ts.tv_sec = (__time_t)(next_event / OC_CLOCK_SECOND); + ts.tv_nsec = (__syscall_slong_t)((double)(next_event % OC_CLOCK_SECOND) * + 1.e09 / OC_CLOCK_SECOND); + pthread_cond_timedwait(&cv, &mutex, &ts); + } + app_mutex_unlock(mutex); + } + oc_main_shutdown(); + return NULL; +} +#endif + +static void +display_menu(void) +{ + OC_PRINTF("\n"); + if (display_ascii_ui) { + print_ascii_lights_ui(); + } + OC_PRINTF("################################################\n"); + OC_PRINTF("Dummy Bridge\n"); + OC_PRINTF("################################################\n"); + OC_PRINTF("[0] Display this menu\n"); + OC_PRINTF("-----------------------------------------------\n"); + OC_PRINTF("[1] Simulate discovery of 'Light 1'\n"); + OC_PRINTF("[2] Simulate discovery of 'Light 2'\n"); + OC_PRINTF("[3] Simulate discovery of 'Light 3'\n"); + OC_PRINTF("[4] Simulate discovery of 'Light 4'\n"); + OC_PRINTF("[5] Simulate discovery of 'Light 5'\n"); + OC_PRINTF(" Select simulate discovery of any device again\n"); + OC_PRINTF(" to simulate that device being disconnected.\n"); + OC_PRINTF("-----------------------------------------------\n"); + OC_PRINTF("[6] Display summary of dummy bridge.\n"); + OC_PRINTF("[7] Start/Stop virtual device discovery.\n"); + OC_PRINTF("[8] Enable/Disable ASCII light bulb UI.\n"); + OC_PRINTF(" A representation of the bridged lights\n"); + OC_PRINTF(" using ASCII art.\n"); +#ifdef OC_SECURITY + OC_PRINTF("[9] Reset Device\n"); + OC_PRINTF("[10] Delete Device\n"); +#endif /* OC_SECURITY */ + OC_PRINTF("-----------------------------------------------\n"); + OC_PRINTF("[99] Exit\n"); + OC_PRINTF("################################################\n"); + OC_PRINTF("Select option: \n"); +} + +static void +disconnect_light(unsigned int index) +{ + virtual_lights[index].discovered = false; + virtual_lights[index].added_to_bridge = false; + size_t device = oc_bridge_get_virtual_device_index( + (uint8_t *)virtual_lights[index].uuid, OC_UUID_LEN, + virtual_lights[index].eco_system); + if (device != 0) { + if (oc_bridge_remove_virtual_device(device) == 0) { + OC_PRINTF("%s removed from the bridge\n", + virtual_lights[index].device_name); + } else { + OC_PRINTF("FAILED to remove %s from the bridge\n", + virtual_lights[index].device_name); + } + } else { + OC_PRINTF("FAILED to find virtual light to remove."); + } +} + +static void +discover_light(unsigned int index) +{ + virtual_lights[index].discovered = !virtual_lights[index].discovered; + // virtual_lights[index].discovered = true; + // TODO Move the poll code into its own thread. + + if (virtual_lights[index].discovered && discover_vitual_devices) { + poll_for_discovered_devices(); + } else { + if (!virtual_lights[index].discovered) { + disconnect_light(index); + } + } +} + +static void +display_summary(void) +{ + for (size_t i = 0; i < VOD_COUNT; i++) { + char di_str[OC_UUID_LEN] = "\0"; + if (virtual_lights[i].added_to_bridge) { + size_t device = oc_bridge_get_virtual_device_index( + (uint8_t *)virtual_lights[i].uuid, OC_UUID_LEN, + virtual_lights[i].eco_system); + if (device != 0) { + oc_uuid_t *id = oc_core_get_device_id(device); + oc_uuid_to_str(id, di_str, OC_UUID_LEN); + } else { + strcpy(di_str, "ERROR FETCHING"); + } + } + + OC_PRINTF("%s:\n", virtual_lights[i].device_name); + OC_PRINTF("\tVirtual Device ID :%s\n", virtual_lights[i].uuid); + OC_PRINTF("\teconame: %s\n", virtual_lights[i].eco_system); + OC_PRINTF("\tlight switch is: %s\n", (virtual_lights[i].on ? "ON" : "OFF")); + OC_PRINTF("\tAdded to bridge: %s\n", + (virtual_lights[i].discovered ? "discovered" : "not discovered")); + OC_PRINTF("\tOCF Device ID: %s\n", + (virtual_lights[i].added_to_bridge ? di_str : "N/A")); + } + OC_PRINTF((discover_vitual_devices) ? "ACTIVELY DISCOVERING DEVICES\n" + : "NOT DISCOVERING DEVICES\n"); +} +#define SCANF(...) \ + do { \ + if (scanf(__VA_ARGS__) <= 0) { \ + OC_PRINTF("ERROR Invalid input\n"); \ + while ((c = getchar()) != EOF && c != '\n') \ + ; \ + fflush(stdin); \ + } \ + } while (0) + +#ifdef OC_SECURITY +static void +reset_light(unsigned int index) +{ + (void)index; + size_t device_index = oc_bridge_get_virtual_device_index( + (uint8_t *)virtual_lights[index].uuid, OC_UUID_LEN, + virtual_lights[index].eco_system); + if (device_index != 0) { + oc_reset_device(device_index); + OC_PRINTF("device %zu is being reset!!\n", device_index); + virtual_lights[index].discovered = false; + virtual_lights[index].added_to_bridge = false; + } +} + +static void +reset_device(void) +{ + OC_PRINTF("################################################\n"); + OC_PRINTF("[0] Reset Bridge\n"); + OC_PRINTF(" Reseting the Bridge will reset all Virtual\n"); + OC_PRINTF(" Devices exposed by the Bridge.\n"); + OC_PRINTF("-----------------------------------------------\n"); + OC_PRINTF("[1] Reset 'Light 1'\n"); + OC_PRINTF("[2] Reset 'Light 2'\n"); + OC_PRINTF("[3] Reset 'Light 3'\n"); + OC_PRINTF("[4] Reset 'Light 4'\n"); + OC_PRINTF("[5] Reset 'Light 5'\n"); + OC_PRINTF("################################################\n"); + OC_PRINTF("Select option: \n"); + int c = 1000; + SCANF("%d", &c); + switch (c) { + case 0: + oc_reset_device(0u); + break; + case 1: + reset_light(0u); + break; + case 2: + reset_light(1u); + break; + case 3: + reset_light(2u); + break; + case 4: + reset_light(3u); + break; + case 5: + reset_light(4u); + break; + default: + break; + } +} +#endif /* OC_SECURITY */ + +static void +delete_light(unsigned int index) +{ + size_t device_index = oc_bridge_get_virtual_device_index( + (uint8_t *)virtual_lights[index].uuid, OC_UUID_LEN, + virtual_lights[index].eco_system); + if (device_index != 0) { + oc_bridge_delete_virtual_device(device_index); + virtual_lights[index].discovered = false; + virtual_lights[index].added_to_bridge = false; + } +} + +static void +delete_device(void) +{ + OC_PRINTF("################################################\n"); + OC_PRINTF("[1] Delete 'Light 1'\n"); + OC_PRINTF("[2] Delete 'Light 2'\n"); + OC_PRINTF("[3] Delete 'Light 3'\n"); + OC_PRINTF("[4] Delete 'Light 4'\n"); + OC_PRINTF("[5] Delete 'Light 5'\n"); + OC_PRINTF("################################################\n"); + OC_PRINTF("Select option: \n"); + int c = 1000; + SCANF("%d", &c); + switch (c) { + case 1: + delete_light(0u); + break; + case 2: + delete_light(1u); + break; + case 3: + delete_light(2u); + break; + case 4: + delete_light(3u); + break; + case 5: + delete_light(4u); + break; + default: + break; + } +} + +static bool +directoryFound(const char *path) +{ + struct stat info; + if (stat(path, &info) != 0) { + return false; + } + if (info.st_mode & S_IFDIR) { + return true; + } + return false; +} + +int +main(void) +{ + int init; +#if defined(_WIN32) + InitializeCriticalSection(&cs); + InitializeConditionVariable(&cv); + InitializeCriticalSection(&app_sync_lock); +#elif defined(__linux__) + struct sigaction sa; + sigfillset(&sa.sa_mask); + sa.sa_flags = 0; + sa.sa_handler = handle_signal; + sigaction(SIGINT, &sa, NULL); +#endif + +#if 0 + oc_log_set_level(OC_LOG_LEVEL_DEBUG); +#endif + + static const oc_handler_t handler = { .init = app_init, + .signal_event_loop = signal_event_loop, + .register_resources = + register_resources }; + + oc_set_con_res_announced(false); + // max app data size set to 13k large enough to hold full IDD + oc_set_max_app_data_size(13312); +#ifdef OC_STORAGE + if (!directoryFound("dummy_bridge_linux_creds")) { + printf( + "Creating dummy_bridge_linux_creds directory for persistant storage."); +#ifdef WIN32 + CreateDirectory("dummy_bridge_linux_creds", NULL); +#else + mkdir("dummy_bridge_linux_creds", 0755); +#endif + } + oc_storage_config("./dummy_bridge_linux_creds/"); +#endif /* OC_STORAGE */ + + init = oc_main_init(&handler); + if (init < 0) + return init; + +#if defined(_WIN32) + event_thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ocf_event_thread, + NULL, 0, NULL); + if (NULL == event_thread) { + return -1; + } +#elif defined(__linux__) + if (pthread_create(&event_thread, NULL, &ocf_event_thread, NULL) != 0) { + return -1; + } +#endif + + int c; + while (quit != 1) { + display_menu(); + SCANF("%d", &c); + switch (c) { + case 0: + continue; + break; + case 1: + discover_light(0u); + break; + case 2: + discover_light(1u); + break; + case 3: + discover_light(2u); + break; + case 4: + discover_light(3u); + break; + case 5: + discover_light(4u); + break; + case 6: + display_summary(); + break; + case 7: + discover_vitual_devices = !discover_vitual_devices; + break; + case 8: + display_ascii_ui = !display_ascii_ui; + break; +#ifdef OC_SECURITY + case 9: + reset_device(); + break; +#endif /* OC_SECURITY */ + case 10: + delete_device(); + break; + case 99: + handle_signal(0); + break; + default: + break; + } + } + +#if defined(_WIN32) + WaitForSingleObject(event_thread, INFINITE); +#elif defined(__linux__) + pthread_join(event_thread, NULL); +#endif + return 0; +} diff --git a/apps/dummy_bridge_virtual_light_IDD.cbor b/apps/dummy_bridge_virtual_light_IDD.cbor new file mode 100644 index 0000000000..d63df467fd Binary files /dev/null and b/apps/dummy_bridge_virtual_light_IDD.cbor differ diff --git a/apps/dummy_bridge_virtual_light_IDD.json b/apps/dummy_bridge_virtual_light_IDD.json new file mode 100644 index 0000000000..a5a719a0d0 --- /dev/null +++ b/apps/dummy_bridge_virtual_light_IDD.json @@ -0,0 +1,299 @@ +{ + "schemes": [ + "http" + ], + "swagger": "2.0", + "consumes": [ + "application/json" + ], + "paths": { + "/bridge/light/switch": { + "post": { + "responses": { + "200": { + "schema": { + "$ref": "#/definitions/BinarySwitch" + }, + "description": "" + } + }, + "parameters": [ + { + "$ref": "#/parameters/interface-a" + }, + { + "schema": { + "$ref": "#/definitions/BinarySwitch" + }, + "in": "body", + "required": true, + "name": "body" + } + ], + "description": "" + }, + "get": { + "responses": { + "200": { + "schema": { + "$ref": "#/definitions/BinarySwitch" + }, + "description": "" + } + }, + "parameters": [ + { + "$ref": "#/parameters/interface-a" + } + ], + "description": "" + } + }, + "/oic/p": { + "get": { + "responses": { + "200": { + "schema": { + "$ref": "#/definitions/Platform" + }, + "description": "" + } + }, + "parameters": [ + { + "$ref": "#/parameters/interface-r" + } + ], + "description": "" + } + } + }, + "definitions": { + "BinarySwitch": { + "properties": { + "n": { + "type": "string", + "maxLength": 64, + "readOnly": true, + "description": "Friendly name of the Resource" + }, + "rt": { + "minItems": 1, + "items": { + "enum": [ + "oic.r.switch.binary" + ], + "type": "string", + "maxLength": 64 + }, + "description": "", + "uniqueItems": true, + "readOnly": true, + "default": [ + "oic.r.switch.binary" + ], + "type": "array" + }, + "if": { + "minItems": 2, + "items": { + "enum": [ + "oic.if.a", + "oic.if.baseline" + ], + "type": "string" + }, + "description": "", + "uniqueItems": true, + "readOnly": true, + "type": "array" + }, + "value": { + "type": "boolean", + "description": "" + } + }, + "required": [ + "value" + ], + "type": "object" + }, + "Platform": { + "properties": { + "id": { + "type": "string", + "readOnly": true, + "maxLength": 64, + "description": "" + }, + "if": { + "minItems": 2, + "items": { + "enum": [ + "oic.if.r", + "oic.if.baseline" + ], + "type": "string", + "maxLength": 64 + }, + "description": "", + "uniqueItems": true, + "readOnly": true, + "type": "array" + }, + "n": { + "type": "string", + "readOnly": true, + "maxLength": 64, + "description": "" + }, + "mnmn": { + "type": "string", + "readOnly": true, + "maxLength": 64, + "description": "" + }, + "mnnct": { + "minItems": 1, + "items": { + "minimum": 1, + "type": "integer", + "description": "" + }, + "readOnly": true, + "type": "array", + "description": "" + }, + "mnml": { + "format": "uri", + "type": "string", + "readOnly": true, + "maxLength": 256, + "description": "" + }, + "mnsel": { + "type": "string", + "readOnly": true, + "maxLength": 64, + "description": "" + }, + "mnpv": { + "type": "string", + "readOnly": true, + "maxLength": 64, + "description": "" + }, + "mnfv": { + "type": "string", + "readOnly": true, + "maxLength": 64, + "description": "" + }, + "rt": { + "minItems": 1, + "items": { + "enum": [ + "oic.wk.p" + ], + "type": "string", + "maxLength": 64 + }, + "description": "", + "uniqueItems": true, + "readOnly": true, + "default": [ + "oic.wk.p" + ], + "type": "array" + }, + "mnos": { + "type": "string", + "readOnly": true, + "maxLength": 64, + "description": "" + }, + "mnhw": { + "type": "string", + "readOnly": true, + "maxLength": 64, + "description": "" + }, + "vid": { + "type": "string", + "readOnly": true, + "maxLength": 64, + "description": "" + }, + "mnmo": { + "type": "string", + "readOnly": true, + "maxLength": 64, + "description": "" + }, + "mndt": { + "readOnly": true, + "type": "string", + "pattern": "^([0-9]{4})-(1[0-2]|0[1-9])-(3[0-1]|2[0-9]|1[0-9]|0[1-9])$", + "description": "" + }, + "pi": { + "readOnly": true, + "type": "string", + "pattern": "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$", + "description": "" + }, + "st": { + "format": "date-time", + "readOnly": true, + "type": "string", + "description": "" + }, + "mnsl": { + "format": "uri", + "type": "string", + "readOnly": true, + "maxLength": 256, + "description": "" + } + }, + "required": [ + "pi", + "mnmn" + ], + "type": "object" + } + }, + "info": { + "version": "20190215", + "title": "serverlite24521", + "termsOfService": "https://openconnectivityfoundation.github.io/core/DISCLAIMER.md", + "license": { + "x-copyright": "copyright 2016-2017, 2019 Open Connectivity Foundation, Inc. All rights reserved.", + "url": "https://github.com/openconnectivityfoundation/core/blob/e28a9e0a92e17042ba3e83661e4c0fbce8bdc4ba/LICENSE.md", + "name": "OCF Data Model License" + } + }, + "parameters": { + "interface-a": { + "enum": [ + "oic.if.a", + "oic.if.baseline" + ], + "type": "string", + "in": "query", + "name": "if" + }, + "interface-r": { + "enum": [ + "oic.if.r", + "oic.if.baseline" + ], + "type": "string", + "in": "query", + "name": "if" + } + }, + "produces": [ + "application/json" + ] +} \ No newline at end of file diff --git a/include/oc_api.h b/include/oc_api.h index cd90168935..336919bc16 100644 --- a/include/oc_api.h +++ b/include/oc_api.h @@ -1245,6 +1245,7 @@ void oc_resource_set_request_handler(oc_resource_t *resource, oc_method_t method, oc_request_callback_t callback, void *user_data) OC_NONNULL(1); + #ifdef OC_OSCORE /** * @brief sets the support of the secure multicast feature diff --git a/include/oc_bridge.h b/include/oc_bridge.h new file mode 100644 index 0000000000..a8de1e2b17 --- /dev/null +++ b/include/oc_bridge.h @@ -0,0 +1,271 @@ +/****************************************************************** + * + * Copyright 2020 Intel Corporation + * Copyright 2023 ETRI Joo-Chul Kevin Lee (rune@etri.re.kr) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************/ + +/** + * @file + * Functions to aid bridging IoTivity to other eco-systems + */ +#ifndef OC_BRIDGE_H +#define OC_BRIDGE_H + +#include "oc_uuid.h" +#include "util/oc_list.h" +#include "oc_helpers.h" +#include "oc_api.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * internal struct that holds the values that build the `oic.r.vodlist` + * properties. + */ +typedef struct oc_vods_s +{ + struct oc_vods_s *next; + oc_string_t name; + oc_uuid_t di; + oc_string_t econame; +} oc_vods_t; + +typedef struct oc_virtual_device_s +{ + struct oc_virtual_device_s *next; + uint8_t *v_id; + size_t v_id_size; + oc_string_t econame; + size_t index; ///< index of `g_oc_device_info[]` where + ///< the corresponding Device is stored. + + bool is_vod_online; ///< false: Device itself is still alive, + ///< but it was removed from "oic.r.vodlist:vods" +} oc_virtual_device_t; + +/** + * Add an oic.d.bridge device. + * + * The oic.r.vodlist resource will be registered to the bridge device. + * + * @param[in] name the user readable name of the device + * @param[in] spec_version The version of the OCF Server. + * This is the "icv" device property + * @param[in] data_model_version Spec version of the resource and device + * specifications to which this device data model + * is implemented. This is the "dmv" device + * property + * @param[in] add_device_cb callback function invoked during oc_add_device(). + * The purpose is to add additional device properties + * that are not supplied to + * oc_bridge_add_bridge_device() function call. + * @param[in] data context pointer that is passed to the oc_add_device_cb_t + * + * @return + * `0` on success + * `-1` on failure + */ +OC_API +int oc_bridge_add_bridge_device(const char *name, const char *spec_version, + const char *data_model_version, + oc_add_device_cb_t add_device_cb, void *data); + +/** + * Add a virtual ocf device to the the stack. + * + * This function is called to add a newly discovered non-ocf device to a bridge + * device. This will typically be called in response to the non-ocf devices + * discovery mechanism. + * + * The `oc_bridge_add_virtual_device()` function may be called as many times as + * needed. Each call will add a new device to the stack with its own port + * address. Each device is automatically assigned a device index number. Unlike + * the `oc_add_device()` function this number is not incremented by one but + * assigned an index number based on avalibility. The index assigned to the + * virtual device will be returned from the function call. The function + * `oc_bridge_get_virtual_device_index()` can also be used to get the logical + * device index number after this function call. + * + * The function `oc_bridge_add_bridge_device()` must be called before this + * function. + * + * @param virtual_device_id a unique identifier that identifies the virtual + * device this could be a UUID, serial number or other + * means of uniquely identifying the device + * @param virtual_device_id_size size in bytes of the virtual_device_id param + * @param econame ecosystem name of the bridged device which is exposed by this + * virtual device + * @param uri the The device URI. The wellknown default URI "/oic/d" is hosted + * by every server. Used to device specific information. + * @param rt the resource type + * @param name the user readable name of the device + * @param spec_version The version of the OCF Server. This is the "icv" device + * property + * @param data_model_version Spec version of the resource and device + * specifications to which this device data model is + * implemented. This is the "dmv" device property + * @param add_device_cb callback function invoked during oc_add_device(). The + * purpose is to add additional device properties that are + * not supplied to oc_add_device() function call. + * @param data context pointer that is passed to the oc_add_device_cb_t + * + * @return + * - the logical index of the virtual device on success + * - `0` on failure since a bridge device is required to add virtual devices + a zero index cannot be assigned to a virtual device. + * + * @note device index is cast from size_t to int and may lose information. + * The `oc_bridge_add_virtual_device()` function can be used to get + * the non-cast device index. + * @note The function `oc_bridge_add_bridge_device()` must be called before this + * function. + * + * @see init + */ +OC_API +size_t oc_bridge_add_virtual_device( + const uint8_t *virtual_device_id, size_t virtual_device_id_size, + const char *econame, const char *uri, const char *rt, const char *name, + const char *spec_version, const char *data_model_version, + oc_add_device_cb_t add_device_cb, void *data); + +/** + * @brief add new vodentry for an existing VOD to "oic.r.vodlist:vods". + * This function is usually called after + * `oc_bridge_remove_virtual_device()` is called. This function DOES NOT add new + * Device to `g_oc_device_info[]`, but just re-registre existing VOD to + * "oic.r.vodlist:vods" list. + * + * @param device_index Device index of VOD to be online + * @return 0: success, -1: failure + */ +OC_API +int oc_bridge_add_vod(size_t device_index); + +/** + * If the non-ocf device is no longer reachable this can be used to remove + * the virtual device from the bridge device. + * + * This will shutdown network connectivity for the device and will update + * the vodslist resource found on the bridge. + * + * Any any persistant settings will remain unchanged. If the virtual device has + * already been onboarded and permission settings have been modified when the + * device is added again using `oc_bridge_add_virtual_device` those + * persistant settings will still be in place. + * + * @param device_index the index of the virtual device + * + * @return + * - `0` on succes + * - `-1` on failure + */ +OC_API +int oc_bridge_remove_virtual_device(size_t device_index); + +/** + * This will remove the virtual device and free memory associated with that + * device. + * + * Delete virtual device will remove all persistant settings. If the virtual + * device is added again the onboarding and device permissions will need to be + * setup as if the device were a new device. + * + * @param device_index index of teh virtual device + * + * @return + * - `0` on success + * - `-1` on failure + */ +OC_API +int oc_bridge_delete_virtual_device(size_t device_index); + +/** + * Get the logical device index for the virtual device + * + * @param virtual_device_id a unique identifier that identifies the virtual + * device this could be a UUID, serial number or other + * means of uniquely identifying the device + * @param virtual_device_id_size size in bytes of the virtual_device_id param + * @param econame ecosystem name of the bridged virtual device + * + * @return + * - the logical index of the virtual device on success + * - `0` on failure since a bridge device is required to add virtual devices + * a zero index cannot be assigned to a virtual device. + */ +OC_API +size_t oc_bridge_get_virtual_device_index(const uint8_t *virtual_device_id, + size_t virtual_device_id_size, + const char *econame); + +/** + * Use the device index of the virtual device to look up the virtual device + * info. + * + * @param virtual_device_index the logical index of the virtual device + * + * @return + * - a pointer to the oc_virtual_device_t upon success + * - NULL if no virtual device was found using the provided index + */ +OC_API +oc_virtual_device_t *oc_bridge_get_vod_mapping_info( + size_t virtual_device_index); + +/** + * find VOD mapping entry which is corresponding to an item of + * "oic.r.vodlist:vods" + * + * @param vod an item of "oic.r.vodlist:vods" + * + * @return + * - a pointer to the oc_virtual_device_t upon success + * - NULL if no virtual device was found corresponding to the `vod` + */ +OC_API +oc_virtual_device_t *oc_bridge_get_vod_mapping_info2(const oc_vods_t *vod); + +/** + * @brief return entry of "oic.r.vodlist:vods" list + * @param di Device id of the VOD to be returned + * @return VOD entry (oc_vods_t) + */ +OC_API +oc_vods_t *oc_bridge_get_vod(oc_uuid_t di); + +/** + * @brief return the list of current active VODs + * (`g_vods` list) + * + * @return + * - head of `g_vods` list + */ +OC_API +oc_vods_t *oc_bridge_get_vod_list(void); + +/** + * @brief Print out all contents of g_oc_device_info[] array + */ +OC_API +void oc_bridge_print_device_list(void); + +#ifdef __cplusplus +} +#endif +#endif // OC_BRIDGE_H diff --git a/include/oc_core_res.h b/include/oc_core_res.h index 84b6d2e7a6..c20a974e39 100644 --- a/include/oc_core_res.h +++ b/include/oc_core_res.h @@ -64,6 +64,14 @@ typedef struct oc_device_info_t oc_string_t dmv; ///< data model version oc_core_add_device_cb_t add_device_cb; ///< callback when device is changed void *data; ///< user data + +#ifdef OC_HAS_FEATURE_BRIDGE + oc_string_t + ecoversion; ///< Version of ecosystem that a Bridged Device belongs to. + ///< Typical version string format is like n.n (e.g. 5.0) + bool + is_removed; ///< true: this device was previously allocated and then removed +#endif } oc_device_info_t; /** @@ -89,6 +97,18 @@ oc_uuid_t *oc_core_get_device_id(size_t device); */ oc_device_info_t *oc_core_get_device_info(size_t device); +#ifdef OC_HAS_FEATURE_BRIDGE +/** + * @brief retrieve the device whose device is di + * + * @param di device id to be used for search + * @param device device index (index of g_oc_device_info[]) + * of the device whose device id is di + * @return 0 if found + */ +int oc_core_get_device_index(oc_uuid_t di, size_t *device); +#endif + /** * @brief retrieve the platform information * diff --git a/include/oc_ri.h b/include/oc_ri.h index 7a6d44373b..589c06db97 100644 --- a/include/oc_ri.h +++ b/include/oc_ri.h @@ -537,6 +537,20 @@ oc_interface_mask_t oc_ri_get_interface_mask(const char *iface, oc_resource_t *oc_ri_get_app_resource_by_uri(const char *uri, size_t uri_len, size_t device); +#ifdef OC_HAS_FEATURE_BRIDGE +/** + * @brief return a resource which belongs to the device whenever it is called + * + * @param device the device index + * @param reset (reset == 1): search from the beginning of the list, + * (reset == 0): resume search from the next of the last found + * item. + * @return non-NULL found resource + * @return NULL no more resource or end of list + */ +oc_resource_t *oc_ri_get_app_resource_by_device(size_t device, bool reset); +#endif + /** * @brief retrieve list of resources * @@ -619,9 +633,19 @@ bool oc_ri_on_delete_resource_add_callback(oc_ri_delete_resource_cb_t cb) OC_API bool oc_ri_on_delete_resource_remove_callback(oc_ri_delete_resource_cb_t cb) OC_NONNULL(); - #endif /* OC_SERVER */ +#ifdef OC_HAS_FEATURE_BRIDGE +/** + * @brief remove the resource mapped to specific Device + * from the list of application resources + * + * @param index index of `g_oc_device_info[]` + */ +OC_API +void oc_ri_delete_app_resources_per_device(size_t index); +#endif /* OC_HAS_FEATURE_BRIDGE */ + /** * @brief retrieve the query value at the nth position * diff --git a/include/oc_uuid.h b/include/oc_uuid.h index 74d694eb77..f59a89b450 100644 --- a/include/oc_uuid.h +++ b/include/oc_uuid.h @@ -28,6 +28,7 @@ #include "oc_export.h" #include "util/oc_compiler.h" +#include "util/oc_features.h" #include #include #include @@ -141,6 +142,16 @@ void oc_gen_uuid(oc_uuid_t *uuid) OC_NONNULL(); OC_API bool oc_uuid_is_equal(oc_uuid_t first, oc_uuid_t second); +/** + * @brief Check if the uuid is null or not + * + * @param uuid A uuid to be checked + * @return true If the uuid is null + * @return false Otherwise + */ +OC_API +bool oc_uuid_is_nil(oc_uuid_t uuid); + #ifdef __cplusplus } #endif diff --git a/include/oc_vod_map.h b/include/oc_vod_map.h new file mode 100644 index 0000000000..dc5b21805d --- /dev/null +++ b/include/oc_vod_map.h @@ -0,0 +1,145 @@ +/****************************************************************** + * + * Copyright 2020 Intel Corporation + * Copyright 2023 ETRI Joo-Chul Kevin Lee (rune@etri.re.kr) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************/ + +#include "oc_rep.h" +#include "oc_bridge.h" +#include "util/oc_list.h" +#include +#include + +#ifndef OC_VOD_MAP_H +#define OC_VOD_MAP_H + +#ifdef __cplusplus +extern "C" { +#endif +/** + * vod mapping list example : + * { + * "vods" : [ + * {"vod_id":"virtual_device_id-1", "econame": "UPnP", "index":1}, + * {"vod_id":"virtual_device_id-2", "econame": "ZigBee", "index":2} + * ], + * "next_index": 3 + * } + */ + +/** + * @struct oc_vod_mapping_list_t + * - vods : list of VOD (oc_virtual_device_t) + * - next_index : index of g_oc_device_info[]. + * next new VOD will be added to g_oc_device_info[next_index] + */ +typedef struct oc_vod_mapping_list_s +{ + OC_LIST_STRUCT(vods); + size_t next_index; ///< index of g_oc_device_info[]. new VOD will be added to + ///< this position +} oc_vod_mapping_list_t; + +/* + * open vod_map file from creds directory and populate `oc_vod_mapping_list_t` + * initilize this from the add_bridge + */ +/** + * @brief + * - initialize VOD list : `g_vod_mapping_list.vods` + * - initialize next_index with `g_device_count` + * - load existing `g_vod_mapping_list` from disk + */ +void oc_vod_map_init(void); + +/* + * release all of the memory + */ +void oc_vod_map_free(void); + +/* + * Reset the vod map as if no VODs had been discovered. + */ +void oc_vod_map_reset(void); + +/** + * @brief find Device in `g_vod_mapping_list.vods` list and return + * Device index of it (index of g_oc_device_info[]). + * + * @param vod_id id to be used as VOD's ID + * (UUID, serial number, or any other identifier that can + * identify the VOD) + * @param vod_id_size size of vod_id + * @param econame econame string + * + * @return index of the vod + * @return 0 if not found + */ +size_t oc_vod_map_get_vod_index(const uint8_t *vod_id, size_t vod_id_size, + const char *econame); + +/** + * + * @brief add new VOD mapping entry (identified by vod_id) to the proper + * position of `g_vod_mapping_list.vods` list, and update + * `g_vod_mapping_list.next_index`. finally, write updated vod_map file. + * + * @param vod_id id to be used as VOD's ID + * (UUID, serial number, or any other identifier that can + * identify the VOD) + * @param vod_id_size size of vod_id + * @param econame econame string + * + * @return index of just added vod (index of `g_oc_device_info[]`) + */ +size_t oc_vod_map_add_mapping_entry(const uint8_t *vod_id, + const size_t vod_id_size, + const char *econame); + +/* + * Remove the vod_id at the given device index + * This will update the next_index so freed indexes + * can be reused. The virtual device associated + * with this index should + */ +void oc_vod_map_remove_mapping_entry(size_t device_index); + +/* + * Walk the vodmap and return the econame at the given index + */ +void oc_vod_map_get_econame(oc_string_t *econame, size_t device_index); + +/** + * @brief retrieve oc_virtual_device_t entry mapped to `device_index` + * @param device_index device index + * @return + * - oc_virtual_device_t * + * - NULL on error + */ +oc_virtual_device_t *oc_vod_map_get_mapping_entry(size_t device_index); + +/** + * @brief retrieve list of all oc_virtual_device_t instances + * + * @return head of g_vod_mapping_list.vods + */ +oc_virtual_device_t *oc_vod_map_get_mapping_list(void); + +#ifdef __cplusplus +} +#endif + +#endif // OC_VOD_MAP_H diff --git a/port/linux/Makefile b/port/linux/Makefile index dff8dddb23..e9c69f5e82 100644 --- a/port/linux/Makefile +++ b/port/linux/Makefile @@ -285,6 +285,12 @@ ifeq ($(PUSH_DEBUG), 1) endif endif +# for Bridging +ifeq ($(BRIDGE), 1) + EXTRA_CFLAGS += -DOC_BRIDGE + SAMPLES += dummy_bridge_linux +endif + ifneq ($(SECURE),0) SRC += $(addprefix ../../security/, oc_acl.c oc_ael.c oc_audit.c oc_certs.c oc_certs_generate.c oc_certs_validate.c \ oc_cred.c oc_cred_util.c oc_csr.c oc_doxm.c oc_entropy.c oc_keypair.c oc_oscore_engine.c oc_oscore_crypto.c \ @@ -640,6 +646,10 @@ push_configurator_multithread_linux: libiotivity-lite-client-server.a $(ROOT_DIR @mkdir -p $@_creds ${CC} -o $@ ../../apps/$@.c libiotivity-lite-client-server.a -DOC_CLIENT -DOC_SERVER ${CFLAGS} ${LIBS} +dummy_bridge_linux: libiotivity-lite-client-server.a $(ROOT_DIR)/apps/dummy_bridge_linux.c + @mkdir -p $@_creds + ${CC} -o $@ ../../apps/$@.c libiotivity-lite-client-server.a -DOC_CLIENT -DOC_SERVER ${CFLAGS} ${LIBS} + $(SO_DPP_OBJ): $(ROOT_DIR)/apps/streamlined_onboarding/ocf_dpp.c ${CC} -o $@ -c $^ ${CFLAGS} ${SECURITY_HEADERS} ${LIBS} diff --git a/port/linux/ipadapter.c b/port/linux/ipadapter.c index 406d1fb2f2..2525b60f81 100644 --- a/port/linux/ipadapter.c +++ b/port/linux/ipadapter.c @@ -1529,6 +1529,12 @@ void oc_connectivity_shutdown(size_t device) { ip_context_t *dev = oc_get_ip_context_for_device(device); + + if (dev == NULL) { + OC_WRN("no ip-context is found for Device (Device index: %zu)", device); + return; + } + OC_ATOMIC_STORE8(dev->terminate, 1); do { if (write(dev->shutdown_pipe[1], "\n", 1) < 0) { diff --git a/security/oc_acl.c b/security/oc_acl.c index f5694e635d..daea03d1bd 100644 --- a/security/oc_acl.c +++ b/security/oc_acl.c @@ -77,6 +77,48 @@ oc_sec_acl_init(void) } } +#ifdef OC_HAS_FEATURE_BRIDGE +static void +_init_acl(size_t device_index) +{ + memset(&g_aclist[device_index], 0, sizeof(oc_sec_acl_t)); + OC_LIST_STRUCT_INIT(&g_aclist[device_index], subjects); +} + +void +oc_sec_acl_new_device(size_t device_index, bool need_realloc) +{ +#ifdef OC_DYNAMIC_ALLOCATION + if ((device_index == (oc_core_get_num_devices() - 1)) && need_realloc) { + /* + * if `g_oc_device_info[device_index]` is newly allocated entry... + */ + g_aclist = (oc_sec_acl_t *)realloc(g_aclist, oc_core_get_num_devices() * + sizeof(oc_sec_acl_t)); + if (!g_aclist) { + oc_abort("Insufficient memory"); + } + + _init_acl(device_index); + + size_t i = 0; + while (i < device_index) { + OC_LIST_STRUCT_REINIT(&g_aclist[i], subjects); + i++; + } + } else if (device_index < oc_core_get_num_devices()) { + /* + * if `g_oc_device_info[device_index]` is an existing entry... + */ + oc_sec_acl_clear(device_index, NULL, NULL); + _init_acl(device_index); + } else { + OC_ERR("device index error ! (%zu)", device_index); + } +#endif /* OC_DYNAMIC_ALLOCATION */ +} +#endif /* OC_HAS_FEATURE_BRIDGE */ + oc_sec_acl_t * oc_sec_get_acl(size_t device) { @@ -1011,7 +1053,13 @@ void oc_sec_acl_free(void) { for (size_t device = 0; device < oc_core_get_num_devices(); ++device) { - oc_sec_acl_clear(device, NULL, NULL); +#ifdef OC_HAS_FEATURE_BRIDGE + if (oc_core_get_device_info(device)->is_removed == false) { +#endif /* OC_HAS_FEATURE_BRIDGE */ + oc_sec_acl_clear(device, NULL, NULL); +#ifdef OC_HAS_FEATURE_BRIDGE + } +#endif /* OC_HAS_FEATURE_BRIDGE */ } #ifdef OC_DYNAMIC_ALLOCATION if (g_aclist != NULL) { diff --git a/security/oc_acl_internal.h b/security/oc_acl_internal.h index 392fbfba36..8e36a078f0 100644 --- a/security/oc_acl_internal.h +++ b/security/oc_acl_internal.h @@ -36,6 +36,20 @@ extern "C" { #define OC_ACE_WC_ALL_PUBLIC_STR "-" void oc_sec_acl_init(void); + +#ifdef OC_HAS_FEATURE_BRIDGE +/** + * @brief increase existing memory for acl for all Devices + * by the size of `oc_sec_acl_t` + * + * @param[in] device_index index of `g_oc_device_info[]` where new Device is + * stored + * @param[in] need_realloc indicates whether reallocation of memory for SVR is + * needed or not + */ +void oc_sec_acl_new_device(size_t device_index, bool need_realloc); +#endif /* OC_HAS_FEATURE_BRIDGE */ + void oc_sec_acl_free(void); void oc_sec_acl_default(size_t device); bool oc_sec_encode_acl(size_t device, oc_interface_mask_t iface_mask, diff --git a/security/oc_ael.c b/security/oc_ael.c index a7904b9998..8a363d5dde 100644 --- a/security/oc_ael.c +++ b/security/oc_ael.c @@ -127,11 +127,58 @@ oc_sec_ael_init(void) } } +#ifdef OC_HAS_FEATURE_BRIDGE +static void +_init_ael(size_t device_index) +{ + memset(&ael[device_index], 0, sizeof(oc_sec_ael_t)); + OC_LIST_STRUCT_INIT(&ael[device_index], events); +} + +void +oc_sec_ael_new_device(size_t device_index, bool need_realloc) +{ +#ifdef OC_DYNAMIC_ALLOCATION + if ((device_index == (oc_core_get_num_devices() - 1)) && need_realloc) { + /* + * if `g_oc_device_info[device_index]` is newly allocated entry... + */ + ael = (oc_sec_ael_t *)realloc(ael, oc_core_get_num_devices() * + sizeof(oc_sec_ael_t)); + if (!ael) { + oc_abort("Insufficient memory"); + } + + _init_ael(device_index); + + size_t i = 0; + while (i < device_index) { + OC_LIST_STRUCT_REINIT(&ael[i], events); + i++; + } + } else if (device_index < oc_core_get_num_devices()) { + /* + * if `g_oc_device_info[device_index]` is existing entry... + */ + oc_sec_ael_reset(device_index); + _init_ael(device_index); + } + +#endif /* OC_DYNAMIC_ALLOCATION */ +} +#endif /* OC_HAS_FEATURE_BRIDGE */ + void oc_sec_ael_free(void) { for (size_t device = 0; device < oc_core_get_num_devices(); device++) { - oc_sec_ael_reset(device); +#ifdef OC_HAS_FEATURE_BRIDGE + if (oc_core_get_device_info(device)->is_removed == false) { +#endif /* OC_HAS_FEATURE_BRIDGE */ + oc_sec_ael_reset(device); +#ifdef OC_HAS_FEATURE_BRIDGE + } +#endif /* OC_HAS_FEATURE_BRIDGE */ } #ifdef OC_DYNAMIC_ALLOCATION free(ael); @@ -139,6 +186,14 @@ oc_sec_ael_free(void) #endif /* OC_DYNAMIC_ALLOCATION */ } +#ifdef OC_HAS_FEATURE_BRIDGE +void +oc_sec_ael_free_device(size_t index) +{ + oc_sec_ael_reset(index); +} +#endif + void oc_sec_ael_default(size_t device) { diff --git a/security/oc_ael_internal.h b/security/oc_ael_internal.h index 16babf4b95..78ee3e840b 100644 --- a/security/oc_ael_internal.h +++ b/security/oc_ael_internal.h @@ -92,6 +92,12 @@ typedef struct oc_sec_ael_t } oc_sec_ael_t; void oc_sec_ael_init(void); + +#ifdef OC_HAS_FEATURE_BRIDGE +void oc_sec_ael_new_device(size_t device_index, bool need_realloc); +void oc_sec_ael_free_device(size_t index); +#endif /* OC_HAS_FEATURE_BRIDGE */ + void oc_sec_ael_free(void); void oc_sec_ael_default(size_t device); diff --git a/security/oc_cred.c b/security/oc_cred.c index d8eb1bfe5d..c56260be6a 100644 --- a/security/oc_cred.c +++ b/security/oc_cred.c @@ -92,6 +92,48 @@ oc_sec_cred_init(void) } } +#ifdef OC_HAS_FEATURE_BRIDGE +static void +_init_cred(size_t device_index) +{ + memset(&g_devices[device_index], 0, sizeof(oc_sec_creds_t)); + OC_LIST_STRUCT_INIT(&g_devices[device_index], creds); +} + +void +oc_sec_cred_new_device(size_t device_index, bool need_realloc) +{ +#ifdef OC_DYNAMIC_ALLOCATION + if ((device_index == (oc_core_get_num_devices() - 1)) && need_realloc) { + /* + * if `g_oc_device_info[device_index]` is newly allocated entry... + */ + g_devices = (oc_sec_creds_t *)realloc(g_devices, oc_core_get_num_devices() * + sizeof(oc_sec_creds_t)); + if (!g_devices) { + oc_abort("Insufficient memory"); + } + + _init_cred(device_index); + + size_t i = 0; + while (i < device_index) { + OC_LIST_STRUCT_REINIT(&g_devices[i], creds); + i++; + } + } else if (device_index < oc_core_get_num_devices()) { + /* + * if `g_oc_device_info[device_index]` is existing entry... + */ + oc_sec_cred_clear(device_index, NULL, NULL); + _init_cred(device_index); + } else { + OC_ERR("device index error ! (%zu)", device_index); + } +#endif /* OC_DYNAMIC_ALLOCATION */ +} +#endif /* OC_HAS_FEATURE_BRIDGE */ + static oc_sec_cred_t * cred_get_by_credid(int credid, bool roles_resource, const oc_tls_peer_t *client, size_t device) @@ -306,7 +348,13 @@ void oc_sec_cred_deinit(void) { for (size_t device = 0; device < oc_core_get_num_devices(); device++) { - oc_sec_cred_clear(device, NULL, NULL); +#ifdef OC_HAS_FEATURE_BRIDGE + if (oc_core_get_device_info(device)->is_removed == false) { +#endif /* OC_HAS_FEATURE_BRIDGE */ + oc_sec_cred_clear(device, NULL, NULL); +#ifdef OC_HAS_FEATURE_BRIDGE + } +#endif /* OC_HAS_FEATURE_BRIDGE */ } #ifdef OC_DYNAMIC_ALLOCATION if (g_devices != NULL) { diff --git a/security/oc_cred_internal.h b/security/oc_cred_internal.h index 1b5172ba09..31abdc2bf6 100644 --- a/security/oc_cred_internal.h +++ b/security/oc_cred_internal.h @@ -86,6 +86,20 @@ oc_sec_cred_t *oc_sec_cred_remove_from_device_by_credid(int credid, void oc_sec_cred_default(size_t device); void oc_sec_cred_init(void); void oc_sec_cred_deinit(void); + +#ifdef OC_HAS_FEATURE_BRIDGE +/** + * @brief increase existing memory for cred for all Devices + * by the size of `oc_sec_creds_t` + * + * @param[in] device_index index of `g_oc_device_info[]` where new Device is + * stored + * @param[in] need_realloc indicates whether reallocation of memory for SVR is + * needed or not* + */ +void oc_sec_cred_new_device(size_t device_index, bool need_realloc); +#endif /* OC_HAS_FEATURE_BRIDGE */ + void oc_sec_encode_cred(size_t device, oc_interface_mask_t iface_mask, bool to_storage); bool oc_sec_decode_cred(const oc_rep_t *rep, oc_sec_cred_t **owner, diff --git a/security/oc_doxm.c b/security/oc_doxm.c index ca6b9d2d20..5213b01234 100644 --- a/security/oc_doxm.c +++ b/security/oc_doxm.c @@ -206,6 +206,27 @@ oc_sec_doxm_init(void) oc_set_select_oxms_cb(NULL, NULL); } +#ifdef OC_HAS_FEATURE_BRIDGE +void +oc_sec_doxm_new_device(size_t device_index, bool need_realloc) +{ +#ifdef OC_DYNAMIC_ALLOCATION + if ((device_index == (oc_core_get_num_devices() - 1)) && need_realloc) { + g_doxm = (oc_sec_doxm_t *)realloc(g_doxm, oc_core_get_num_devices() * + sizeof(oc_sec_doxm_t)); + if (!g_doxm) { + oc_abort("Insufficient memory"); + } + memset(&g_doxm[device_index], 0, sizeof(oc_sec_doxm_t)); + } else if (device_index < oc_core_get_num_devices()) { + memset(&g_doxm[device_index], 0, sizeof(oc_sec_doxm_t)); + } else { + OC_ERR("device index error ! (%zu)", device_index); + } +#endif /* OC_DYNAMIC_ALLOCATION */ +} +#endif /* OC_HAS_FEATURE_BRIDGE */ + static void doxm_evaluate_supported_oxms(size_t device) { diff --git a/security/oc_doxm_internal.h b/security/oc_doxm_internal.h index 55c01e112e..ba5b647211 100644 --- a/security/oc_doxm_internal.h +++ b/security/oc_doxm_internal.h @@ -66,6 +66,18 @@ typedef struct /** @brief Allocate and initialize global variables */ void oc_sec_doxm_init(void); +#ifdef OC_HAS_FEATURE_BRIDGE +/** + * by the size of `oc_sec_doxm_t` + * + * @param[in] device_index index of `g_oc_device_info[]` where new Device is + * stored + * @param[in] need_realloc indicates whether reallocation of memory for SVR is + * needed or not + */ +void oc_sec_doxm_new_device(size_t device_index, bool need_realloc); +#endif /* OC_HAS_FEATURE_BRIDGE */ + /** @brief Deallocate global variables */ void oc_sec_doxm_free(void); diff --git a/security/oc_pstat.c b/security/oc_pstat.c index fc9e6673a1..6d5a995347 100644 --- a/security/oc_pstat.c +++ b/security/oc_pstat.c @@ -97,6 +97,28 @@ oc_sec_pstat_init(void) #endif /* OC_DYNAMIC_ALLOCATION */ } +#ifdef OC_HAS_FEATURE_BRIDGE +void +oc_sec_pstat_new_device(size_t device_index, bool need_realloc) +{ +#ifdef OC_DYNAMIC_ALLOCATION + if ((device_index == (oc_core_get_num_devices() - 1)) && need_realloc) { + g_pstat = (oc_sec_pstat_t *)realloc(g_pstat, oc_core_get_num_devices() * + sizeof(oc_sec_pstat_t)); + if (!g_pstat) { + oc_abort("Insufficient memory"); + } + memset(&g_pstat[device_index], 0, sizeof(oc_sec_pstat_t)); + } else if (device_index < oc_core_get_num_devices()) { + memset(&g_pstat[device_index], 0, sizeof(oc_sec_pstat_t)); + } else { + OC_ERR("device index error ! (%zu)", device_index); + } +#endif /* OC_DYNAMIC_ALLOCATION */ + +} +#endif /* OC_HAS_FEATURE_BRIDGE */ + static bool nil_uuid(const oc_uuid_t *uuid) { @@ -787,6 +809,10 @@ void oc_reset_devices_in_RFOTM(void) { for (size_t device = 0; device < oc_core_get_num_devices(); device++) { +#ifdef OC_HAS_FEATURE_BRIDGE + if (oc_core_get_device_info(device)->is_removed) + continue; +#endif if (g_pstat[device].s == OC_DOS_RFOTM) { oc_pstat_reset_device(device, true); } diff --git a/security/oc_pstat_internal.h b/security/oc_pstat_internal.h index 1b49bb51cf..4b7e9019d2 100644 --- a/security/oc_pstat_internal.h +++ b/security/oc_pstat_internal.h @@ -64,6 +64,19 @@ void oc_sec_pstat_init(void); /** @brief Deallocate global variables */ void oc_sec_pstat_free(void); +#ifdef OC_HAS_FEATURE_BRIDGE +/** + * @brief increase existing memory for pstat for all Devices + * by the size of `oc_sec_pstat_t` + * + * @param[in] device_index index of `g_oc_device_info[]` where new Device is + * stored + * @param[in] need_realloc indicates whether reallocation of memory for SVR is + * needed or not + */ +void oc_sec_pstat_new_device(size_t device_index, bool need_realloc); +#endif /* OC_HAS_FEATURE_BRIDGE */ + /** * @brief Get pstat resource representation for given device * diff --git a/security/oc_sdi.c b/security/oc_sdi.c index 5b137c3edb..58e0b29937 100644 --- a/security/oc_sdi.c +++ b/security/oc_sdi.c @@ -55,6 +55,28 @@ oc_sec_sdi_init(void) #endif /* OC_DYNAMIC_ALLOCATION */ } +#ifdef OC_HAS_FEATURE_BRIDGE +void +oc_sec_sdi_new_device(size_t device_index, bool need_realloc) +{ +#ifdef OC_DYNAMIC_ALLOCATION + if ((device_index == (oc_core_get_num_devices() - 1)) && need_realloc) { + g_sdi = (oc_sec_sdi_t *)realloc(g_sdi, oc_core_get_num_devices() * + sizeof(oc_sec_sdi_t)); + if (!g_sdi) { + oc_abort("Insufficient memory"); + } + memset(&g_sdi[device_index], 0, sizeof(oc_sec_sdi_t)); + } else if (device_index < oc_core_get_num_devices()) { + oc_free_string(&(g_sdi[device_index].name)); + memset(&g_sdi[device_index], 0, sizeof(oc_sec_sdi_t)); + } else { + OC_ERR("device index error ! (%zu)", device_index); + } +#endif /* OC_DYNAMIC_ALLOCATION */ +} +#endif /* OC_HAS_FEATURE_BRIDGE */ + void oc_sec_sdi_free(void) { @@ -64,7 +86,13 @@ oc_sec_sdi_free(void) } #endif /* OC_DYNAMIC_ALLOCATION */ for (size_t device = 0; device < oc_core_get_num_devices(); ++device) { - oc_free_string(&(g_sdi[device].name)); +#ifdef OC_HAS_FEATURE_BRIDGE + if (oc_core_get_device_info(device)->is_removed == false) { +#endif /* OC_HAS_FEATURE_BRIDGE */ + oc_free_string(&(g_sdi[device].name)); +#ifdef OC_HAS_FEATURE_BRIDGE + } +#endif /* OC_HAS_FEATURE_BRIDGE */ } #ifdef OC_DYNAMIC_ALLOCATION diff --git a/security/oc_sdi_internal.h b/security/oc_sdi_internal.h index 06aaa4fc69..bdd57c92d3 100644 --- a/security/oc_sdi_internal.h +++ b/security/oc_sdi_internal.h @@ -49,6 +49,10 @@ typedef struct */ void oc_sec_sdi_init(void); +#ifdef OC_HAS_FEATURE_BRIDGE +void oc_sec_sdi_new_device(size_t device_index, bool need_realloc); +#endif /* OC_HAS_FEATURE_BRIDGE */ + /** * @brief Deallocate all sdi resource data. */ diff --git a/security/oc_sp.c b/security/oc_sp.c index b87546e4b6..359cae0b3e 100644 --- a/security/oc_sp.c +++ b/security/oc_sp.c @@ -75,6 +75,43 @@ oc_sec_sp_init(void) } } +#ifdef OC_HAS_FEATURE_BRIDGE +static void +_init_sp(size_t device_index) +{ + memset(&g_sp[device_index], 0, sizeof(oc_sec_sp_t)); + memset(&g_sp_mfg_default[device_index], 0, sizeof(oc_sec_sp_t)); + + g_sp_mfg_default[device_index].current_profile = OC_SP_BASELINE; + g_sp_mfg_default[device_index].supported_profiles = OC_SP_BASELINE; + g_sp_mfg_default[device_index].credid = -1; +} + +void +oc_sec_sp_new_device(size_t device_index, bool need_realloc) +{ +#ifdef OC_DYNAMIC_ALLOCATION + if ((device_index == (oc_core_get_num_devices() - 1)) && need_realloc) { + g_sp = (oc_sec_sp_t *)realloc(g_sp, oc_core_get_num_devices() * sizeof(oc_sec_sp_t)); + if (!g_sp) { + oc_abort("Insufficient memory"); + } + + g_sp_mfg_default = (oc_sec_sp_t *)realloc(g_sp_mfg_default, oc_core_get_num_devices() * sizeof(oc_sec_sp_t)); + if (!g_sp_mfg_default) { + oc_abort("Insufficient memory"); + } + + _init_sp(device_index); + } else if (device_index < oc_core_get_num_devices()) { + _init_sp(device_index); + } else { + OC_ERR("device index error ! (%zu)", device_index); + } +#endif /* OC_DYNAMIC_ALLOCATION */ +} +#endif /* OC_HAS_FEATURE_BRIDGE */ + void oc_sec_sp_free(void) { diff --git a/security/oc_sp_internal.h b/security/oc_sp_internal.h index 880583d114..ff8c089751 100644 --- a/security/oc_sp_internal.h +++ b/security/oc_sp_internal.h @@ -51,6 +51,10 @@ typedef struct /** @brief Allocate and initialize global variables */ void oc_sec_sp_init(void); +#ifdef OC_HAS_FEATURE_BRIDGE +void oc_sec_sp_new_device(size_t device_index, bool need_realloc); +#endif /* OC_HAS_FEATURE_BRIDGE */ + /** @brief Deallocate global variables */ void oc_sec_sp_free(void); diff --git a/security/oc_store.c b/security/oc_store.c index 95d54fba9c..798b272f70 100644 --- a/security/oc_store.c +++ b/security/oc_store.c @@ -92,7 +92,7 @@ void oc_sec_load_pstat(size_t device) { if (oc_storage_data_load("pstat", device, store_decode_pstat, NULL) <= 0) { - OC_DBG("failed to load pstat from storage for device(%zu)", device); + OC_ERR("failed to load pstat from storage for device(%zu)", device); oc_sec_pstat_default(device); return; } diff --git a/security/oc_svr.c b/security/oc_svr.c index ed35991c80..53a4fbb046 100644 --- a/security/oc_svr.c +++ b/security/oc_svr.c @@ -34,6 +34,9 @@ #include "oc_sp_internal.h" #include "oc_svr_internal.h" #include "port/oc_log_internal.h" +#ifdef OC_HAS_FEATURE_BRIDGE +#include "oc_store.h" +#endif void oc_sec_svr_create(void) @@ -70,6 +73,67 @@ oc_sec_svr_create(void) } } +#ifdef OC_HAS_FEATURE_BRIDGE +void +oc_sec_svr_create_new_device(size_t device_index, bool need_realloc) +{ + oc_sec_doxm_new_device(device_index, need_realloc); + oc_sec_pstat_new_device(device_index, need_realloc); + oc_sec_acl_new_device(device_index, need_realloc); + oc_sec_cred_new_device(device_index, need_realloc); + oc_sec_ael_new_device(device_index, need_realloc); + oc_sec_sp_new_device(device_index, need_realloc); + oc_sec_sdi_new_device(device_index, need_realloc); + + oc_sec_doxm_create_resource(device_index); + oc_core_populate_resource(OCF_SEC_PSTAT, device_index, "/oic/sec/pstat", + OC_IF_RW | OC_IF_BASELINE, OC_IF_RW, + OC_DISCOVERABLE | OC_OBSERVABLE, get_pstat, 0, + post_pstat, 0, 1, "oic.r.pstat"); + oc_core_populate_resource(OCF_SEC_ACL, device_index, "/oic/sec/acl2", + OC_IF_RW | OC_IF_BASELINE, OC_IF_RW, + OC_DISCOVERABLE | OC_SECURE, get_acl, 0, post_acl, + delete_acl, 1, "oic.r.acl2"); + oc_sec_cred_create_resource(device_index); + oc_core_populate_resource(OCF_SEC_AEL, device_index, "/oic/sec/ael", + OC_IF_RW | OC_IF_BASELINE, OC_IF_RW, + OC_DISCOVERABLE | OC_SECURE, get_ael, 0, post_ael, + 0, 1, "oic.r.ael"); + + oc_sec_sp_create_resource(device_index); + oc_sec_sdi_create_resource(device_index); +#ifdef OC_PKI + oc_sec_csr_create_resource(device_index); + oc_sec_roles_create_resource(device_index); +#endif /* OC_PKI */ +} + +void +oc_sec_svr_init_new_device(size_t device_index) +{ + oc_sec_load_unique_ids(device_index); + OC_DBG("oc_core_add_new_device_at_index(): loading pstat(%zu)", device_index); + oc_sec_load_pstat(device_index); + OC_DBG("oc_core_add_new_device_at_index(): loading doxm(%zu)", device_index); + oc_sec_load_doxm(device_index); + OC_DBG("oc_core_add_new_device_at_index(): loading cred(%zu)", device_index); + oc_sec_load_cred(device_index); + OC_DBG("oc_core_add_new_device_at_index(): loading acl(%zu)", device_index); + oc_sec_load_acl(device_index); + OC_DBG("oc_core_add_new_device_at_index(): loading sp(%zu)", device_index); + oc_sec_load_sp(device_index); + OC_DBG("oc_core_add_new_device_at_index(): loading ael(%zu)", device_index); + oc_sec_load_ael(device_index); +#ifdef OC_PKI + OC_DBG("oc_core_add_new_device_at_index(): loading ECDSA keypair(%zu)", device_index); + oc_sec_load_ecdsa_keypair(device_index); +#endif /* OC_PKI */ + OC_DBG("oc_core_add_new_device_at_index(): loading sdi(%zu)", device_index); + oc_sec_load_sdi(device_index); +} + +#endif /* OC_HAS_FEATURE_BRIDGE */ + void oc_sec_svr_free(void) { diff --git a/security/oc_svr_internal.h b/security/oc_svr_internal.h index 173dc6856d..47757207c1 100644 --- a/security/oc_svr_internal.h +++ b/security/oc_svr_internal.h @@ -19,6 +19,9 @@ #ifndef OC_SVR_INTERNAL_H #define OC_SVR_INTERNAL_H +#include "util/oc_features.h" +#include + #ifdef __cplusplus extern "C" { #endif @@ -28,6 +31,28 @@ extern "C" { */ void oc_sec_svr_create(void); +#ifdef OC_HAS_FEATURE_BRIDGE +/** + * @brief add SVR for the Device which is added dynamically. + * new Device should be added to `g_oc_device_info[]` + * before calling this function. + * + * @param[in] device_index index of `g_oc_device_info[]` where new Device is + * stored + * @param[in] need_realloc indicates whether reallocation of memory for SVR is + * needed or not + */ +void oc_sec_svr_create_new_device(size_t device_index, bool need_realloc); + +/** + * @brief update SVR with stored values, + * if there is no store data, initialize with default value. + * + * @param[in] device_index index of Device stored in `g_oc_device_info[]` + */ +void oc_sec_svr_init_new_device(size_t device_index); +#endif /* OC_HAS_FEATURE_BRIDGE */ + /** * @brief Deinitialize secure vertical resources; */ diff --git a/security/unittest/acltest.cpp b/security/unittest/acltest.cpp index a8d1b48841..b76194325d 100644 --- a/security/unittest/acltest.cpp +++ b/security/unittest/acltest.cpp @@ -1,6 +1,7 @@ /****************************************************************** * * Copyright 2022 Daniel Adam, All Rights Reserved. + * Copyright 2024 ETRI Joo-Chul Kevin Lee, All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"), * you may not use this file except in compliance with the License. @@ -34,6 +35,11 @@ #include "api/oc_push_internal.h" #endif /* OC_HAS_FEATURE_PUSH */ +#ifdef OC_HAS_FEATURE_BRIDGE +#include "oc_bridge.h" +#include +#endif /* OC_HAS_FEATURE_BRIDGE */ + #include "gtest/gtest.h" #include @@ -219,4 +225,39 @@ TEST_F(TestAcl, oc_sec_check_acl_in_RFOTM) } #endif /* OC_HAS_FEATURE_RESOURCE_ACCESS_IN_RFOTM */ +#ifdef OC_HAS_FEATURE_BRIDGE +static bool +IsAclEntryInitialized(const oc_sec_acl_t *aclEntry) +{ + /* + * resource owner should be null + * subject list should be empty + */ + if ((oc_uuid_is_nil(aclEntry->rowneruuid)) && + !oc_list_length(aclEntry->subjects)) { + return true; + } + return false; +} + +/* + * oc_sec_acl_new_device(device_index, need_realloc) + */ +TEST_F(TestAcl, AclNewDevice) +{ + /* + * overwrite entry in the existing position + */ + auto aclEntry = oc_sec_get_acl(device_id_); + auto orgAcl = std::make_unique(); + + memcpy(orgAcl.get(), aclEntry, sizeof(oc_sec_acl_t)); + + oc_sec_acl_new_device(device_id_, false); + EXPECT_EQ(true, IsAclEntryInitialized(aclEntry)); + + memcpy(aclEntry, orgAcl.get(), sizeof(oc_sec_acl_t)); +} +#endif /* OC_HAS_FEATURE_BRIDGE */ + #endif /* OC_SECURITY */ diff --git a/security/unittest/credtest.cpp b/security/unittest/credtest.cpp index 3d9ad4bcfd..b724a2e848 100644 --- a/security/unittest/credtest.cpp +++ b/security/unittest/credtest.cpp @@ -1,6 +1,7 @@ /**************************************************************************** * * Copyright (c) 2023 plgd.dev s.r.o. + * Copyright (c) 2024 ETRI Joo-Chul Kevin Lee * * Licensed under the Apache License, Version 2.0 (the "License"), * you may not use this file except in compliance with the License. @@ -41,6 +42,11 @@ #include "api/oc_push_internal.h" #endif /* OC_HAS_FEATURE_PUSH */ +#ifdef OC_HAS_FEATURE_BRIDGE +#include "oc_bridge.h" +#include +#endif /* OC_HAS_FEATURE_BRIDGE */ + #include #include #include @@ -82,10 +88,7 @@ class TestCreds : public testing::Test { oc_network_event_handler_mutex_destroy(); } - void TearDown() override - { - oc_sec_cred_clear(kDeviceID, nullptr, nullptr); - } + void TearDown() override { oc_sec_cred_clear(kDeviceID, nullptr, nullptr); } static size_t countCreds(size_t device) { @@ -405,4 +408,40 @@ TEST_F(TestCreds, Serialize_Fail) #endif /* OC_PKI && (OC_DYNAMIC_ALLOCATION || OC_TEST) */ +#ifdef OC_HAS_FEATURE_BRIDGE +static bool +IsCredsEntryInitialized(const oc_sec_creds_t *credsEntry) +{ + /* + * resource owner should be null + * subject list should be empty + */ + if ((oc_uuid_is_nil(credsEntry->rowneruuid)) && + !oc_list_length(credsEntry->creds)) { + return true; + } + + return false; +} + +/* + * oc_sec_cred_new_device(device_index, need_realloc) + */ +TEST_F(TestCreds, CredNewDevice) +{ + /* + * overwrite entry in the existing position + */ + auto credsEntry = oc_sec_get_creds(kDeviceID); + auto orgCreds = std::make_unique(); + + memcpy(orgCreds.get(), credsEntry, sizeof(oc_sec_creds_t)); + + oc_sec_cred_new_device(kDeviceID, false); + EXPECT_EQ(true, IsCredsEntryInitialized(credsEntry)); + + memcpy(credsEntry, orgCreds.get(), sizeof(oc_sec_creds_t)); +} +#endif /* OC_HAS_FEATURE_BRIDGE */ + #endif /* OC_SECURITY */ diff --git a/security/unittest/doxmtest.cpp b/security/unittest/doxmtest.cpp index 61faecaf04..40b2332bfe 100644 --- a/security/unittest/doxmtest.cpp +++ b/security/unittest/doxmtest.cpp @@ -1,6 +1,8 @@ /****************************************************************** * * Copyright 2023 Daniel Adam, All Rights Reserved. + * Copyright 2024 ETRI Joo-Chul Kevin Lee, All Rights Reserved. + * * * Licensed under the Apache License, Version 2.0 (the "License"), * you may not use this file except in compliance with the License. @@ -35,6 +37,11 @@ #include "tests/gtest/Resource.h" #include "tests/gtest/Storage.h" +#ifdef OC_HAS_FEATURE_BRIDGE +#include "oc_bridge.h" +#include +#endif /* OC_HAS_FEATURE_BRIDGE */ + #include #include #include @@ -1076,4 +1083,38 @@ TEST_F(TestDoxmWithServer, Owned_F) #endif /* OC_DYNAMIC_ALLOCATION */ } +#ifdef OC_HAS_FEATURE_BRIDGE +static bool +IsDoxmEntryInitialized(const oc_sec_doxm_t *doxmEntry) +{ + auto emptyDoxm = std::make_unique(); + memset(emptyDoxm.get(), 0, sizeof(oc_sec_doxm_t)); + + if (!memcmp(doxmEntry, emptyDoxm.get(), sizeof(oc_sec_doxm_t))) { + return true; + } + return false; +} + +TEST_F(TestDoxmWithServer, DoxmNewDevice) +{ + /* + * overwrite entry in the existing position + */ + /* + * add new acl entry to the end of the array + */ + auto doxmEntry = oc_sec_get_doxm(kDeviceID); + auto orgDoxm = std::make_unique(); + + memcpy(orgDoxm.get(), doxmEntry, sizeof(oc_sec_doxm_t)); + + oc_sec_doxm_new_device(kDeviceID, false); + + EXPECT_EQ(true, IsDoxmEntryInitialized(doxmEntry)); + + memcpy(doxmEntry, orgDoxm.get(), sizeof(oc_sec_doxm_t)); +} +#endif /* OC_HAS_FEATURE_BRIDGE */ + #endif /* OC_SECURITY */ diff --git a/security/unittest/pstattest.cpp b/security/unittest/pstattest.cpp index 4385810248..e6925de2c8 100644 --- a/security/unittest/pstattest.cpp +++ b/security/unittest/pstattest.cpp @@ -1,6 +1,7 @@ /****************************************************************** * * Copyright 2023 Daniel Adam, All Rights Reserved. + * Copyright 2024 ETRI Joo-Chul Kevin Lee, All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"), * you may not use this file except in compliance with the License. @@ -34,6 +35,10 @@ #include "tests/gtest/Resource.h" #include "util/oc_features.h" +#ifdef OC_HAS_FEATURE_BRIDGE +#include +#endif /* OC_HAS_FEATURE_BRIDGE */ + #ifdef OC_SOFTWARE_UPDATE #include "api/oc_swupdate_internal.h" #endif /* OC_SOFTWARE_UPDATE */ @@ -164,15 +169,9 @@ class TestPstatWithServer : public testing::Test { #endif /* OC_HAS_FEATURE_RESOURCE_ACCESS_IN_RFOTM */ } - static void TearDownTestCase() - { - oc::TestDevice::StopServer(); - } + static void TearDownTestCase() { oc::TestDevice::StopServer(); } - void TearDown() override - { - oc::TestDevice::Reset(); - } + void TearDown() override { oc::TestDevice::Reset(); } }; #ifdef OC_HAS_FEATURE_RESOURCE_ACCESS_IN_RFOTM @@ -218,4 +217,34 @@ TEST_F(TestPstatWithServer, DeleteRequest_Fail) error_code); } +#ifdef OC_HAS_FEATURE_BRIDGE +static bool +IsPstatEntryInitialized(const oc_sec_pstat_t *pstatEntry) +{ + auto emptyPstat = std::make_unique(); + memset(emptyPstat.get(), 0, sizeof(oc_sec_pstat_t)); + + if (!memcmp(pstatEntry, emptyPstat.get(), sizeof(oc_sec_pstat_t))) { + return true; + } + return false; +} + +TEST_F(TestPstatWithServer, PstatNewDevice) +{ + /* + * overwrite entry in the existing position + */ + auto pstatEntry = oc_sec_get_pstat(kDeviceID); + auto orgPstat = std::make_unique(); + + memcpy(orgPstat.get(), pstatEntry, sizeof(oc_sec_pstat_t)); + oc_sec_pstat_new_device(kDeviceID, false); + + EXPECT_EQ(true, IsPstatEntryInitialized(pstatEntry)); + + memcpy(pstatEntry, orgPstat.get(), sizeof(oc_sec_pstat_t)); +} +#endif /* OC_HAS_FEATURE_BRIDGE */ + #endif /* OC_SECURITY */ diff --git a/security/unittest/sditest.cpp b/security/unittest/sditest.cpp index a8efa095ca..92262eb26e 100644 --- a/security/unittest/sditest.cpp +++ b/security/unittest/sditest.cpp @@ -1,6 +1,7 @@ /****************************************************************** * * Copyright 2023 Daniel Adam, All Rights Reserved. + * Copyright 2024 ETRI Joo-Chul Kevin Lee, All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"), * you may not use this file except in compliance with the License. @@ -41,6 +42,10 @@ #include #include +#ifdef OC_HAS_FEATURE_BRIDGE +#include +#endif /* OC_HAS_FEATURE_BRIDGE */ + using namespace std::chrono_literals; static const std::string testStorage{ "storage_test" }; @@ -219,10 +224,7 @@ class TestSdiWithServer : public testing::Test { } } - void TearDown() override - { - oc::TestDevice::Reset(); - } + void TearDown() override { oc::TestDevice::Reset(); } }; TEST_F(TestSdiWithServer, GetResourceByIndex) @@ -400,4 +402,34 @@ TEST_F(TestSdiWithServer, DumpAndLoad) oc_free_string(&def.name); } +#ifdef OC_HAS_FEATURE_BRIDGE +static bool +IsSdiEntryInitialized(const oc_sec_sdi_t *sdiEntry) +{ + auto emptySdi = std::make_unique(); + memset(emptySdi.get(), 0, sizeof(oc_sec_sdi_t)); + + if (!memcmp(sdiEntry, emptySdi.get(), sizeof(oc_sec_sdi_t))) { + return true; + } + return false; +} + +TEST_F(TestSdiWithServer, SdiNewDevice) +{ + /* + * overwrite entry in the existing position + */ + auto sdiEntry = oc_sec_sdi_get(kDeviceID); + auto orgSdi = std::make_unique(); + + memcpy(orgSdi.get(), sdiEntry, sizeof(oc_sec_sdi_t)); + oc_sec_sdi_new_device(kDeviceID, false); + + EXPECT_EQ(true, IsSdiEntryInitialized(sdiEntry)); + + memcpy(sdiEntry, orgSdi.get(), sizeof(oc_sec_sdi_t)); +} +#endif /* OC_HAS_FEATURE_BRIDGE */ + #endif /* OC_SECURITY */ diff --git a/security/unittest/sptest.cpp b/security/unittest/sptest.cpp index 86489efb61..813b5fd7f2 100644 --- a/security/unittest/sptest.cpp +++ b/security/unittest/sptest.cpp @@ -1,6 +1,7 @@ /****************************************************************** * * Copyright 2023 Daniel Adam, All Rights Reserved. + * Copyright 2024 ETRI Joo-Chul Kevin Lee, All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"), * you may not use this file except in compliance with the License. @@ -40,6 +41,10 @@ #include "tests/gtest/Storage.h" #include "util/oc_macros_internal.h" +#ifdef OC_HAS_FEATURE_BRIDGE +#include +#endif /* OC_HAS_FEATURE_BRIDGE */ + #ifdef OC_HAS_FEATURE_PUSH #include "api/oc_push_internal.h" #endif /* OC_HAS_FEATURE_PUSH */ @@ -301,20 +306,11 @@ class TestSecurityProfileWithServer : public testing::Test { #endif /* OC_HAS_FEATURE_RESOURCE_ACCESS_IN_RFOTM */ } - static void TearDownTestCase() - { - oc::TestDevice::StopServer(); - } + static void TearDownTestCase() { oc::TestDevice::StopServer(); } - void SetUp() override - { - oc_sec_sp_default(kDeviceID); - } + void SetUp() override { oc_sec_sp_default(kDeviceID); } - void TearDown() override - { - oc::TestDevice::Reset(); - } + void TearDown() override { oc::TestDevice::Reset(); } }; #ifdef OC_HAS_FEATURE_RESOURCE_ACCESS_IN_RFOTM @@ -468,4 +464,39 @@ TEST_F(TestSecurityProfileWithServer, DeleteRequest_Fail) error_code); } +#ifdef OC_HAS_FEATURE_BRIDGE +static bool +IsSpEntryInitialized(const oc_sec_sp_t *sdiEntry) +{ + auto emptySp = std::make_unique(); + memset(emptySp.get(), 0, sizeof(oc_sec_sp_t)); + + if (/*!memcmp(sdiEntry, emptySp.get(), sizeof(oc_sec_sp_t)) + &&*/ + (sdiEntry->current_profile == OC_SP_BASELINE) && + (sdiEntry->supported_profiles == OC_SP_BASELINE) && + (sdiEntry->credid == -1)) { + return true; + } + return false; +} + +TEST_F(TestSecurityProfile, SpNewDevice) +{ + /* + * overwrite entry in the existing position + */ + auto spEntry = oc_sec_sp_get(kDeviceID); + auto orgSp = std::make_unique(); + + memcpy(orgSp.get(), spEntry, sizeof(oc_sec_sp_t)); + oc_sec_sp_new_device(kDeviceID, false); + oc_sec_sp_default(kDeviceID); + + EXPECT_EQ(true, IsSpEntryInitialized(spEntry)); + + memcpy(spEntry, orgSp.get(), sizeof(oc_sec_sp_t)); +} +#endif /* OC_HAS_FEATURE_BRIDGE */ + #endif /* OC_SECURITY */ diff --git a/sonar-project.properties b/sonar-project.properties index e2156596da..644f51c8af 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -6,7 +6,7 @@ sonar.organization=iotivity-lite #sonar.projectVersion=1.0 # Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows. -#sonar.sources=. +# sonar.sources=. # TODO: Java, Python, HTML, JavaScript and CSS are disabled until a maintainer is found sonar.exclusions=apps/**,deps/**,docker/**,patches/**,tests/**,tools/**,**/*.java,**/*.py,**/*.html,**/*.js,**/*.css diff --git a/util/oc_features.h b/util/oc_features.h index d9baa95825..125a2f955d 100644 --- a/util/oc_features.h +++ b/util/oc_features.h @@ -39,6 +39,11 @@ #define OC_HAS_FEATURE_PUSH #endif +#if defined(OC_BRIDGE) && defined(OC_SERVER) && defined(OC_CLIENT) && \ + defined(OC_DYNAMIC_ALLOCATION) +#define OC_HAS_FEATURE_BRIDGE +#endif + #if defined(OC_SECURITY) && defined(OC_RESOURCE_ACCESS_IN_RFOTM) #define OC_HAS_FEATURE_RESOURCE_ACCESS_IN_RFOTM #endif diff --git a/util/oc_list.h b/util/oc_list.h index 5e8a8a20ca..5d2159da06 100644 --- a/util/oc_list.h +++ b/util/oc_list.h @@ -132,6 +132,11 @@ extern "C" { oc_list_init((struct_ptr)->name); \ } while (0) +#define OC_LIST_STRUCT_REINIT(struct_ptr, name) \ + do { \ + (struct_ptr)->name = &((struct_ptr)->OC_LIST_CONCAT(name, _list)); \ + } while (0) + /** * The linked list type. */