diff options
-rw-r--r-- | .gitlab/issue_templates/mytemplate.md | 3 | ||||
-rw-r--r-- | .gitlab/merge_request_templates/mytemplate.md | 3 | ||||
-rw-r--r-- | conf.d/cmake/config.cmake | 7 | ||||
-rw-r--r-- | conf.d/packaging/deb/agl-service-platform-info.dsc | 18 | ||||
-rw-r--r-- | conf.d/packaging/deb/debian.agl-service-platform-info.install | 1 | ||||
-rw-r--r-- | conf.d/packaging/deb/debian.changelog | 5 | ||||
-rw-r--r-- | conf.d/packaging/deb/debian.compat | 1 | ||||
-rw-r--r-- | conf.d/packaging/deb/debian.control | 19 | ||||
-rw-r--r-- | conf.d/packaging/deb/debian.rules | 14 | ||||
-rw-r--r-- | conf.d/packaging/rpm/agl-service-platform-info.spec | 66 | ||||
-rw-r--r-- | src/CMakeLists.txt | 5 | ||||
-rw-r--r-- | src/platform-info-apidef.h | 8 | ||||
-rw-r--r-- | src/platform-info-binding.c | 151 | ||||
-rw-r--r-- | src/platform-info-binding.h | 4 | ||||
-rw-r--r-- | src/platform-info-devices.c | 500 | ||||
-rw-r--r-- | src/platform-info-devices.h | 51 | ||||
-rw-r--r-- | test/afb-test/tests/aft-BasicAPITests.lua | 13 |
17 files changed, 701 insertions, 168 deletions
diff --git a/.gitlab/issue_templates/mytemplate.md b/.gitlab/issue_templates/mytemplate.md new file mode 100644 index 0000000..25d91d8 --- /dev/null +++ b/.gitlab/issue_templates/mytemplate.md @@ -0,0 +1,3 @@ +**Please use https://gerrit.automotivelinux.org for code contributions.** +See also: https://docs.automotivelinux.org/ chapter "How to contribute". + diff --git a/.gitlab/merge_request_templates/mytemplate.md b/.gitlab/merge_request_templates/mytemplate.md new file mode 100644 index 0000000..25d91d8 --- /dev/null +++ b/.gitlab/merge_request_templates/mytemplate.md @@ -0,0 +1,3 @@ +**Please use https://gerrit.automotivelinux.org for code contributions.** +See also: https://docs.automotivelinux.org/ chapter "How to contribute". + diff --git a/conf.d/cmake/config.cmake b/conf.d/cmake/config.cmake index 14ec2f2..c410ff5 100644 --- a/conf.d/cmake/config.cmake +++ b/conf.d/cmake/config.cmake @@ -19,11 +19,11 @@ # Project Info # ------------------ set(PROJECT_NAME "agl-service-platform-info") -set(PROJECT_VERSION 6.90) +set(PROJECT_VERSION 1.0) set(API_NAME platform-info) set(PROJECT_PRETTY_NAME "Platform Information provider binding") set(PROJECT_DESCRIPTION "A binding meant to provide system, platform, build information for others bindings and apps") -set(PROJECT_URL "https://github.com/iotbzh/agl-service-platform-info") +set(PROJECT_URL "https://gerrit.automotivelinux.org/gerrit/apps/agl-service-platform-info") set(PROJECT_ICON "icon.png") set(PROJECT_AUTHOR "Forlot, Romain") set(PROJECT_AUTHOR_MAIL "romain.forlot@iot.bzh") @@ -63,9 +63,10 @@ set (gcc_minimal_version 4.9) # PKG_CONFIG required packages # ----------------------------- set (PKG_REQUIRED_LIST - json-c>=0.12 + json-c>=0.13 afb-daemon afb-helpers + libudev ) # You can also consider to include libsystemd diff --git a/conf.d/packaging/deb/agl-service-platform-info.dsc b/conf.d/packaging/deb/agl-service-platform-info.dsc deleted file mode 100644 index 0c603de..0000000 --- a/conf.d/packaging/deb/agl-service-platform-info.dsc +++ /dev/null @@ -1,18 +0,0 @@ -Format: 1.0 -Source: agl-service-os-info -Binary: agl-service-os-info-bin -Architecture: any -Version: 2.0-0 -Maintainer: Iot-Team <secretaria@iot.bzh> -Standards-Version: 3.8.2 -Homepage: https://github.com/iotbzh/agl-service-os-info -Build-Depends: debhelper (>= 5), - pkg-config, - cmake, - gcc, - g++, - libjson-c-dev , - libsystemd-dev (>= 222), - agl-app-framework-binder-dev , - agl-libmicrohttpd-dev (>= 0.9.55) -Debtransform-Tar: agl-service-os-info_1.0.orig.tar.gz diff --git a/conf.d/packaging/deb/debian.agl-service-platform-info.install b/conf.d/packaging/deb/debian.agl-service-platform-info.install deleted file mode 100644 index 6399692..0000000 --- a/conf.d/packaging/deb/debian.agl-service-platform-info.install +++ /dev/null @@ -1 +0,0 @@ -/opt/AGL/* diff --git a/conf.d/packaging/deb/debian.changelog b/conf.d/packaging/deb/debian.changelog deleted file mode 100644 index de0a835..0000000 --- a/conf.d/packaging/deb/debian.changelog +++ /dev/null @@ -1,5 +0,0 @@ -agl-service-os-info (1.0-0) UNRELEASED; urgency=low - - * init build - - -- Iot-Team <romain.forlot@iot.bzh> Wed, 7 Nov 2018 10:50:38 +0100 diff --git a/conf.d/packaging/deb/debian.compat b/conf.d/packaging/deb/debian.compat deleted file mode 100644 index ec63514..0000000 --- a/conf.d/packaging/deb/debian.compat +++ /dev/null @@ -1 +0,0 @@ -9 diff --git a/conf.d/packaging/deb/debian.control b/conf.d/packaging/deb/debian.control deleted file mode 100644 index 4eb8b99..0000000 --- a/conf.d/packaging/deb/debian.control +++ /dev/null @@ -1,19 +0,0 @@ -Priority: optional -Maintainer: Iot-Team <secretaria@iot.bzh> -Source: agl-service-os-info -Build-Depends: debhelper (>= 5), - pkg-config, - cmake, - gcc, - g++, - libjson-c-dev , - libsystemd-dev (>= 222), - agl-app-framework-binder-dev , - agl-libmicrohttpd-dev (>= 0.9.55) -Standards-Version: 3.8.2 -Homepage: https://github.com/iotbzh/agl-service-os-info - -Package: agl-service-os-info -Section: libs -Architecture: any -Description: Provide an AGL OS information Binding diff --git a/conf.d/packaging/deb/debian.rules b/conf.d/packaging/deb/debian.rules deleted file mode 100644 index 5bc5fc2..0000000 --- a/conf.d/packaging/deb/debian.rules +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/make -f -# -*- makefile -*- -# Uncomment this to turn on verbose mode. -#export DH_VERBOSE=1 - -%: - bash /etc/profile.d/AGL-app-framework-binder.sh - dh $@ - -override_dh_auto_configure: - dh_auto_configure -- -DCMAKE_INSTALL_PREFIX=/opt/AGL/afm/applications/ - -override_dh_auto_install: - dh_auto_install --destdir=/usr/src/packages/BUILD/debian/tmp diff --git a/conf.d/packaging/rpm/agl-service-platform-info.spec b/conf.d/packaging/rpm/agl-service-platform-info.spec deleted file mode 100644 index d495571..0000000 --- a/conf.d/packaging/rpm/agl-service-platform-info.spec +++ /dev/null @@ -1,66 +0,0 @@ -########################################################################### -# Copyright 2015, 2016, 2017 IoT.bzh -# -# author: Iot-Team <secretaria@iot.bzh> -# -# 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. -########################################################################### - - -Name: agl-helloworld-service -Version: 1.0 -Release: 1 -Group: AGL -License: APL2.0 -Summary: Provide an AGL Helloworld Binding -Url: https://github.com/iotbzh/helloworld-service -Source0: %{name}_%{version}.orig.tar.gz - -BuildRequires: cmake -BuildRequires: gcc gcc-c++ -BuildRequires: pkgconfig(json-c) -BuildRequires: pkgconfig(libsystemd) >= 222 -BuildRequires: pkgconfig(afb-daemon) -BuildRequires: pkgconfig(libmicrohttpd) >= 0.9.55 - - -BuildRoot: %{_tmppath}/%{name}-%{version}-build - -%define _prefix /opt/AGL/helloworld-service -%define __cmake cmake - -%description -Provide an AGL Helloworld Binding - -%prep -%setup -q - -%build -%cmake -DCMAKE_INSTALL_PREFIX:PATH=%{_libdir} -make %{?_smp_mflags} - -%install -CURDIR=$(pwd) -[ -d build ] && cd build -make populate -mkdir -p %{?buildroot}%{_prefix} -cp -r package/* %{?buildroot}%{_prefix} - -cd $CURDIR -find %{?buildroot}%{_prefix} -type d -exec echo "%dir {}" \;>> pkg_file -find %{?buildroot}%{_prefix} -type f -exec echo "{}" \;>> pkg_file -sed -i 's@%{?buildroot}@@g' pkg_file - - -%files -f pkg_file -%defattr(-,root,root) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 45bc242..ae1da6a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -18,7 +18,10 @@ PROJECT_TARGET_ADD(platform-info) # Define project Targets - ADD_LIBRARY(${TARGET_NAME} MODULE platform-info-binding.c) + ADD_LIBRARY(${TARGET_NAME} MODULE + platform-info-binding.c + platform-info-devices.c + ) # Binder exposes a unique public entry point SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES diff --git a/src/platform-info-apidef.h b/src/platform-info-apidef.h index c09da63..4dba262 100644 --- a/src/platform-info-apidef.h +++ b/src/platform-info-apidef.h @@ -5,6 +5,7 @@ static const struct afb_auth _afb_auths_platform_info[] = { void afv_get(afb_req_t req); void afv_set(afb_req_t req); + void afv_scan(afb_req_t req); void afv_subscribe(afb_req_t req); void afv_unsubscribe(afb_req_t req); @@ -24,6 +25,13 @@ static const struct afb_verb_v3 _afb_verbs_platform_info[] = { .session = AFB_SESSION_NONE }, { + .verb = "scan", + .callback = afv_scan, + .auth = NULL, + .info = "Scan system devices", + .session = AFB_SESSION_NONE + }, + { .verb = "unsubscribe", .callback = afv_unsubscribe, .auth = NULL, diff --git a/src/platform-info-binding.c b/src/platform-info-binding.c index 5e78fd9..8aecdfe 100644 --- a/src/platform-info-binding.c +++ b/src/platform-info-binding.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016-2019 "IoT.bzh" + * Copyright (C) 2016-2020 "IoT.bzh" * * Author "Romain Forlot" <romain.forlot@iot.bzh> * @@ -23,6 +23,7 @@ #include <afb/afb-binding.h> #include "platform-info-binding.h" +#include "platform-info-devices.h" #ifndef PLATFORM_INFO_DIR #define PLATFORM_INFO_DIR "/etc/platform-info" @@ -33,30 +34,42 @@ #endif void afv_get(afb_req_t req) { - char *json_path = NULL, *full_json_path = NULL; - json_object *platform_info = (json_object*) afb_api_get_userdata(req->api); - json_object *args = afb_req_json(req), *result = NULL; - - switch (json_object_get_type(args)) { - case json_type_null: - result = platform_info; - break; - case json_type_string: - full_json_path = strdupa(json_object_get_string(args)); - result = platform_info; - for(json_path = strtok(full_json_path, "."); json_path && *json_path; json_path = strtok(NULL, ".")) { - if(! json_object_object_get_ex(result, json_path, &result)) { - afb_req_fail(req, "A key hasn't been found in JSON path.", json_path); - return; + pinfo_api_ctx_t *api_ctx = (pinfo_api_ctx_t*)afb_api_get_userdata(req->api); + + if(api_ctx) { + json_object *result = NULL; + json_object *args = afb_req_json(req); + + switch (json_object_get_type(args)) { + case json_type_null: + result = api_ctx->info; + break; + case json_type_string: { + char *json_path = NULL; + char *full_json_path = NULL; + + full_json_path = strdupa(json_object_get_string(args)); + result = api_ctx->info; + + for(json_path = strtok(full_json_path, "."); + json_path && *json_path; + json_path = strtok(NULL, ".")) { + if(! json_object_object_get_ex(result, json_path, &result)) { + afb_req_fail(req, "A key hasn't been found in JSON path.", json_path); + return; + } } + break; } - break; - default: - afb_req_fail(req, "Type error", "Argument type is unknown, you must provide a string only"); - return; - } + default: + afb_req_fail(req, "Type error", "Argument type is unknown, you must provide a string only"); + return; + } - afb_req_success(req, json_object_get(result), NULL); + afb_req_success(req, json_object_get(result), NULL); + } else { + afb_req_fail(req,"failed","The API contains no context!"); + } } void afv_set(afb_req_t req) { @@ -75,27 +88,60 @@ void afv_set(afb_req_t req) { afb_req_success(req, NULL, NULL); } -// TODO RFOR: interface with inotify and udev +// TODO RFOR: interface with inotify void afv_unsubscribe(afb_req_t req) { - afb_req_success(req, NULL, NULL); + pinfo_client_ctx_t* client_ctx = NULL; + client_ctx = (pinfo_client_ctx_t*)afb_req_context_get(req); + + if(client_ctx) { + const char* event_str = afb_req_value(req,"event"); + + if(event_str && strcmp("monitor-devices",event_str) == 0) { + afb_req_unsubscribe(req,client_ctx->ev_devs_changed); + afb_req_context_clear(req); + afb_req_success(req,NULL, NULL); + } else { + afb_req_fail(req,"failed","No 'event' value provided."); + } + } else { + afb_req_fail(req,"failed","No context available for the client."); + } } void afv_subscribe(afb_req_t req) { - afb_req_success(req, NULL, NULL); + const char* event_str = NULL; + event_str = afb_req_value(req,"event"); + + if(event_str && strcmp("monitor-devices",event_str) == 0) { + if(!afb_req_context_get(req)) { + if(pinfo_device_monitor(req) == PINFO_OK) { + afb_req_success(req,NULL,NULL); + } else { + afb_req_fail(req,"failed","Unable to create new context"); + } + } else { + afb_req_fail(req,"failed","The client already subscribed."); + } + } else { + afb_req_fail(req,"failed","Invalid event subscription"); + } } -int init(afb_api_t api) { +static json_object* +afv_static_info(const char* dir) { struct dirent* dir_ent = NULL; + DIR* dir_handle = opendir(dir); + json_object* static_info = NULL; - DIR* dir_handle = opendir(PLATFORM_INFO_DIR); if (! dir_handle) { AFB_ERROR("The directory %s does not exist.", PLATFORM_INFO_DIR); - return -1; + return NULL; } - json_object *json_file = json_object_new_object(), *current_file = NULL; + static_info = json_object_new_object(); while( (dir_ent = readdir(dir_handle)) != NULL) { if(dir_ent->d_type == DT_REG && dir_ent->d_name[0] != '.') { + json_object* current_file = NULL; size_t filepath_len = strlen(PLATFORM_INFO_DIR) + strlen(dir_ent->d_name) + 2; char *filepath = alloca(filepath_len); @@ -117,14 +163,53 @@ int init(afb_api_t api) { continue; } #endif - wrap_json_object_add(json_file, current_file); - AFB_DEBUG("JSON loaded: %s", json_object_get_string(json_file)); + wrap_json_object_add(static_info, current_file); + AFB_DEBUG("JSON loaded: %s", + json_object_to_json_string_ext(current_file,JSON_C_TO_STRING_PRETTY)); } } + return static_info; +} + +void afv_scan(afb_req_t req) { + json_object* jres = NULL; + json_object* jfilter = NULL; + json_object* jmask = NULL; + json_object* jargs = afb_req_json(req); + + if(json_object_is_type(jargs,json_type_object)) { + json_object_object_get_ex(jargs,"filter",&jfilter); + json_object_object_get_ex(jargs,"mask",&jmask); + } + + jres = pinfo_device_scan(jfilter,jmask); + + if(jres) { + afb_req_success(req,jres,"Scan success"); + } else { + afb_req_fail(req,"failed","Scan failed"); + } +} + +int init(afb_api_t api) { // Initializing the platform_info binding object and associated it to // the api - afb_api_set_userdata(api, (void*)json_file); + pinfo_api_ctx_t *api_ctx = NULL; + int ret = PINFO_OK; + + api_ctx = malloc(sizeof(*api_ctx)); + + if(api_ctx) { + api_ctx->info = afv_static_info(PLATFORM_INFO_DIR); + AFB_API_DEBUG(api,"The API static data: %s", + json_object_to_json_string_ext(api_ctx->info, JSON_C_TO_STRING_PRETTY)); + api_ctx->client_count = 0; + afb_api_set_userdata(api, (void*)api_ctx); + } else { + AFB_API_WARNING(api,"Failed to load the static data"); + ret = PINFO_ERR; + } - return 0; + return ret; } diff --git a/src/platform-info-binding.h b/src/platform-info-binding.h index 183fa86..8943080 100644 --- a/src/platform-info-binding.h +++ b/src/platform-info-binding.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016, 2018 "IoT.bzh" + * Copyright (C) 2016, 2018, 2020 "IoT.bzh" * * Author "Romain Forlot" <romain.forlot@iot.bzh> * @@ -23,5 +23,3 @@ struct key_search_t { afb_api_t api; json_object **result; }; - -json_object *platform_info; diff --git a/src/platform-info-devices.c b/src/platform-info-devices.c new file mode 100644 index 0000000..785423b --- /dev/null +++ b/src/platform-info-devices.c @@ -0,0 +1,500 @@ +/* + * Copyright (C) 2020 "IoT.bzh" + * + * + * 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 <string.h> +#include <sys/select.h> +#include <sys/types.h> +#include <dirent.h> +#include <pthread.h> +#include <libudev.h> +#include <unistd.h> +#include "platform-info-devices.h" + +#define JSON_NEW_CONST_KEY \ + (JSON_C_OBJECT_KEY_IS_CONSTANT | JSON_C_OBJECT_ADD_KEY_IS_NEW) +#define UNUSED(x) (void)(x); + +static void* pinfo_device_client_new(void* req_ctx); +static void pinfo_device_client_free(void* client_ctx); +static void* pinfo_device_monitor_loop(pinfo_client_ctx_t* ctx); +static void pinfo_device_monitor_detect(pinfo_client_ctx_t* ctx, struct json_object* jdevice); +static json_object* pinfo_device_udevice_to_jdevice(struct udev_device* udevice, struct json_object* jmask); +static void pinfo_device_jdev_destructor(json_object* jdevice, void* udevice); +static int pinfo_device_filter_monitoring(pinfo_client_ctx_t* ctx); +static void pinfo_device_filter_scan(struct udev_enumerate* udev_enum, struct json_object* jfilter); +static json_object* pinfo_device_udevice_to_jlist( + struct udev_device* udevice, + struct udev_list_entry*(*udevice_elist)(struct udev_device*), + const char*(*elist_val_getter)(struct udev_device*,const char*), + const unsigned int jflags); +static json_object* pinfo_device_udevice_to_jlist_mask( + struct udev_device* udevice, + const char*(*udevice_val_getter)(struct udev_device*,const char*), + json_object* jkeys, + unsigned jcpy_flags); + +static void +pinfo_device_monitor_detect(pinfo_client_ctx_t* ctx, struct json_object* jdevice) +{ + afb_event_push((afb_event_t)ctx->ev_devs_changed,jdevice); +} + +int +pinfo_device_monitor(afb_req_t req) { + int res = PINFO_ERR; + + if(afb_req_context(req,0,pinfo_device_client_new,pinfo_device_client_free,req)) { + res = PINFO_OK; + } + + return res; +} + +static void +pinfo_device_client_free(void* client_ctx) { + if(client_ctx) { + pinfo_client_ctx_t* ctx = (pinfo_client_ctx_t*)client_ctx; + if(ctx) { + pthread_cancel(ctx->th); + udev_monitor_unref(ctx->umon_hndl); + udev_unref(ctx->udev_ctx); + afb_event_unref((afb_event_t)ctx->ev_devs_changed); + json_object_put(ctx->filter); + json_object_put(ctx->mask); + ctx->api_ctx->client_count--; + AFB_DEBUG("Client context released, client count: %d",ctx->api_ctx->client_count); + } + } +} + +static void* +pinfo_device_client_new(void* req_ctx) { + pinfo_client_ctx_t* ctx = NULL; + afb_req_t req = (afb_req_t)(req_ctx); + + if(req) { + ctx = malloc(sizeof(*ctx)); + if(ctx) { + ctx->udev_ctx = udev_new(); + if(ctx->udev_ctx) { + ctx->umon_hndl = udev_monitor_new_from_netlink(ctx->udev_ctx,"udev"); + ctx->filter = NULL; + ctx->mask = NULL; + ctx->umon_cb = NULL; + + if(ctx->umon_hndl) { + json_object* jval = NULL; + json_object* jargs = afb_req_json(req); + pthread_attr_t attr; + + ctx->ev_devs_changed = afb_api_make_event(req->api,"device_changed"); + if(afb_event_is_valid(ctx->ev_devs_changed)) { + if(afb_req_subscribe(req,ctx->ev_devs_changed) == PINFO_OK) { + ctx->umon_cb = (void(*)(void*,struct json_object*))pinfo_device_monitor_detect; + if(json_object_object_get_ex(jargs,"filter",&jval) && + json_object_is_type(jval,json_type_object)) { + json_object_deep_copy(jval,&ctx->filter,NULL); + } + + if(json_object_object_get_ex(jargs,"mask",&jval) && + json_object_is_type(jval,json_type_object)) { + json_object_deep_copy(jval,&ctx->mask,NULL); + } + + if(pinfo_device_filter_monitoring(ctx) == PINFO_OK) { + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED); + if(pthread_create(&ctx->th,&attr, + (void*(*)(void*))&pinfo_device_monitor_loop,(void*)ctx) == PINFO_OK) { + pinfo_api_ctx_t* api_ctx = NULL; + api_ctx = (pinfo_api_ctx_t*)afb_api_get_userdata(req->api); + AFB_REQ_DEBUG(req,"New client event-loop have been started ..."); + //New client (new session) get a pointer of API context + //to access global(API) context + if(api_ctx) { + ctx->api_ctx = api_ctx; + api_ctx->client_count++; + AFB_REQ_INFO(req,"New client context allocated, client count: %d", + api_ctx->client_count); + } + } else { + AFB_REQ_ERROR(req,"Failed to run the new session monitoring thread."); + pinfo_device_client_free(ctx); + ctx = NULL; + } + pthread_attr_destroy(&attr); + } else { + AFB_REQ_ERROR(req,"Failed to apply the device monitoring filters."); + } + } else { + AFB_REQ_ERROR(req,"New client event subscribing failed."); + } + } else { + AFB_REQ_ERROR(req,"Invalid AFB event generated for new client."); + } + } else { + AFB_REQ_ERROR(req, + "Failed to generate new udev monitoring object for the new client."); + } + } else { + AFB_REQ_ERROR(req, + "Failed to generate new udev object for the new client."); + } + } else { + AFB_REQ_ERROR(req,"Failed to allocate memory for new client context."); + } + } + + return (void*)ctx; +} + +static void +pinfo_device_jdev_destructor(struct json_object* jdevice, void* udevice) +{ + UNUSED(jdevice) + udev_device_unref((struct udev_device*)udevice); +} + +static int +pinfo_device_filter_monitoring(pinfo_client_ctx_t* ctx) { + int res = PINFO_OK; + + if(ctx && ctx->umon_hndl && + json_object_is_type(ctx->filter,json_type_object)) { + json_object* jprop_filter = NULL; + json_object* jval = NULL; + + if(json_object_object_get_ex(ctx->filter,"properties",&jprop_filter)) { + const char* subsys_str = NULL; + const char* devtype_str = NULL; + + if(json_object_object_get_ex(jprop_filter,"SUBSYSTEM",&jval) && + json_object_is_type(jval,json_type_string)) { + subsys_str = json_object_get_string(jval); + } + jval = NULL; + if(json_object_object_get_ex(jprop_filter,"DEVTYPE",&jval) && + json_object_is_type(jval,json_type_string)) { + devtype_str = json_object_get_string(jval); + } + if(devtype_str || subsys_str) { + res |= udev_monitor_filter_add_match_subsystem_devtype( + ctx->umon_hndl,subsys_str,devtype_str); + } + } + + if(json_object_object_get_ex(ctx->filter,"tags",&jval)) { + if(json_object_is_type(jval,json_type_array)) { + const size_t tags_count = json_object_array_length(jval); + + if(tags_count > 0) { + int tag_idx = 0; + json_object *tag_item = NULL; + + for(tag_item = json_object_array_get_idx(jval,0); + tag_item && tag_idx < tags_count; + tag_item = json_object_array_get_idx(jval,++tag_idx)) { + + if(json_object_is_type(tag_item,json_type_string)) { + res |= udev_monitor_filter_add_match_tag( + ctx->umon_hndl, + json_object_get_string(tag_item) + ); + } else { + AFB_WARNING("Client passed invalid tag type,\ + avoid the tag expression"); + } + } + } else { + //Empty tags array + } + } else { + AFB_WARNING("Client passed invalid value for 'tags' field,\ + the value type should be a json array with json string items,\ + avoid the tags filtering"); + } + } + } + + return res; +} + + +static json_object* +pinfo_device_udevice_to_jlist( struct udev_device* udevice, + struct udev_list_entry*(*udevice_elist)(struct udev_device*), + const char*(*elist_val_get)(struct udev_device*,const char*), + const unsigned int jflags) { + json_object* jobject = NULL; + + if(udevice && udevice_elist) { + struct udev_list_entry* elist = udevice_elist(udevice); + struct udev_list_entry* elist_head = NULL; + + jobject = json_object_new_object(); + udev_list_entry_foreach(elist_head,elist) { + const char* skey = udev_list_entry_get_name(elist_head); + const char* svalue = elist_val_get(udevice,skey); + + if(skey && svalue) { + json_object_object_add_ex(jobject,skey,json_object_new_string(svalue),jflags); + } + } + } + + return jobject; +} + +static json_object* +pinfo_device_udevice_to_jlist_mask( struct udev_device* udevice, + const char*(*udevice_val_getter)(struct udev_device*,const char*), + json_object* jkeys, + unsigned jcpy_flags) { + json_object* jdev = NULL; + + if(json_object_is_type(jkeys,json_type_array)) { + const int keys_count = json_object_array_length(jkeys); + if(keys_count > 0) { + int key_idx = 0; + jdev = json_object_new_object(); + + for(;key_idx < keys_count; ++key_idx) { + const char* skey = json_object_get_string( + json_object_array_get_idx(jkeys,key_idx)); + const char* svalue = udevice_val_getter(udevice,skey); + + if(svalue) { + json_object_object_add_ex(jdev,skey,json_object_new_string(svalue),jcpy_flags); + } + } + } + } + + return jdev; +} + +static json_object* +pinfo_device_udevice_to_jdevice(struct udev_device* udevice, json_object* jmask) { + json_object *jdev = NULL; + + if(udevice) { + json_object* jprops = NULL; + json_object* jattrs = NULL; + + jdev = json_object_new_object(); + if(json_object_is_type(jmask,json_type_object)) { + json_object* jprops_mask = NULL; + json_object* jattrs_mask = NULL; + + if(json_object_object_get_ex(jmask,"properties",&jprops_mask)) { + jprops = pinfo_device_udevice_to_jlist_mask( + udevice, + udev_device_get_property_value, + jprops_mask, + JSON_NEW_CONST_KEY + ); + } + if(json_object_object_get_ex(jmask,"attributes",&jattrs_mask)) { + jattrs = pinfo_device_udevice_to_jlist_mask( + udevice, + udev_device_get_sysattr_value, + jattrs_mask, + JSON_NEW_CONST_KEY + ); + } + } + + if(!jprops) { + jprops = pinfo_device_udevice_to_jlist( + udevice, + udev_device_get_properties_list_entry, + udev_device_get_property_value, + JSON_NEW_CONST_KEY + ); + } + + if(!jattrs) { + jattrs = pinfo_device_udevice_to_jlist( + udevice, + udev_device_get_sysattr_list_entry, + udev_device_get_sysattr_value, + JSON_NEW_CONST_KEY + ); + } + + if(json_object_is_type(jprops,json_type_object) && + json_object_object_length(jprops) > 0) { + json_object_object_add_ex( + jdev, + "properties", + jprops, + JSON_NEW_CONST_KEY + ); + } + + if(json_object_is_type(jattrs,json_type_object) && + json_object_object_length(jattrs) > 0) { + json_object_object_add_ex( + jdev, + "attributes", + jattrs, + JSON_NEW_CONST_KEY + ); + } + + json_object_set_userdata(jdev,udevice,(json_object_delete_fn*)pinfo_device_jdev_destructor); + } + + return jdev; +} + +static void* +pinfo_device_monitor_loop(pinfo_client_ctx_t* ctx) { + if(ctx && ctx->umon_hndl) { + int fd; + int res = 0; + + res = udev_monitor_enable_receiving(ctx->umon_hndl); + + if(res >= 0) { + fd = udev_monitor_get_fd(ctx->umon_hndl); + while (1) { + int ret; + fd_set fds; + + FD_ZERO(&fds); + FD_SET(fd,&fds); + + ret = select(fd+1, &fds, NULL, NULL, NULL); + if(ret > 0 && FD_ISSET(fd,&fds)) { + struct udev_device* detected_dev = NULL; + + detected_dev = udev_monitor_receive_device(ctx->umon_hndl); + if(detected_dev) { + json_object* jdetected_dev = NULL; + jdetected_dev = pinfo_device_udevice_to_jdevice(detected_dev,ctx->mask); + if(jdetected_dev) { + ctx->umon_cb(ctx,jdetected_dev); + } + } + } + } + } + } + return NULL; +} + +static void +pinfo_device_filter_scan(struct udev_enumerate* udev_enum, json_object* jfilter) { + if(udev_enum && jfilter) { + if(json_object_is_type(jfilter,json_type_object)) { + json_object* jval = NULL; + + if(json_object_object_get_ex(jfilter,"tags",&jval)) { + if(json_object_is_type(jval,json_type_array)) { + int tag_idx = 0; + const int tags_count = json_object_array_length(jval); + + if(tags_count > 0) { + json_object* jtag = NULL; + + for(jtag = json_object_array_get_idx(jval,0); + jtag && tag_idx < tags_count; + jtag = json_object_array_get_idx(jtag,++tag_idx)) { + udev_enumerate_add_match_tag(udev_enum,json_object_get_string(jtag)); + } + } else { + //Empty json array for tags array + } + } else if(json_object_is_type(jval,json_type_string)) { + udev_enumerate_add_match_tag(udev_enum,json_object_get_string(jval)); + } else { + AFB_WARNING("Client passed invalid value for 'tags' field,\ + the value type should be a json array with json string items\ + or a json string, avoid the tags filtering"); + } + } + + if(json_object_object_get_ex(jfilter,"properties",&jval) && + json_object_is_type(jval,json_type_object)) { + if(json_object_object_length(jval) > 0) { + json_object_object_foreach(jval,key,value) { + udev_enumerate_add_match_property(udev_enum,key,json_object_get_string(value)); + } + } + } + + if(json_object_object_get_ex(jfilter,"attributes",&jval) && + json_object_is_type(jval,json_type_object)) { + if(json_object_object_length(jval) > 0) { + json_object_object_foreach(jval,key,value) { + udev_enumerate_add_match_sysattr(udev_enum,key,json_object_get_string(value)); + } + } + } + } + } +} + +json_object* +pinfo_device_scan(json_object *jfilter, json_object* jmask) { + json_object* jdevs_arr = NULL; + struct udev* udev_ctx = NULL; + + udev_ctx = udev_new(); + jdevs_arr = json_object_new_array(); + + if(udev_ctx) { + struct udev_enumerate *dev_enum = NULL; + + dev_enum = udev_enumerate_new(udev_ctx); + if(dev_enum) { + struct udev_list_entry *dev_elist = NULL; + struct udev_list_entry *dev_elist_head = NULL; + pinfo_device_filter_scan(dev_enum,jfilter); + + udev_enumerate_scan_devices(dev_enum); + dev_elist = udev_enumerate_get_list_entry(dev_enum); + if(dev_elist) { + struct udev_device *udevice = NULL; + struct json_object *jdevice = NULL; + int udev_num = 0; + int jdev_num = 0; + + udev_list_entry_foreach(dev_elist_head,dev_elist) { + const char* path = udev_list_entry_get_name(dev_elist_head); + udev_num++; + udevice = udev_device_new_from_syspath((struct udev*)udev_ctx,path); + jdevice = pinfo_device_udevice_to_jdevice(udevice,jmask); + if(jdevice) { + json_object_array_add(jdevs_arr,jdevice); + jdev_num++; + } else { + udev_device_unref(udevice); + } + } + AFB_INFO("[SCAN]: %d device detected, %d device info reported back.",udev_num,jdev_num); + } else { + AFB_WARNING("No device found or enumeration failed"); + } + } else { + AFB_DEBUG("Unable to allocate enumeration object."); + } + udev_enumerate_unref(dev_enum); + } + + return jdevs_arr; +} diff --git a/src/platform-info-devices.h b/src/platform-info-devices.h new file mode 100644 index 0000000..59944d6 --- /dev/null +++ b/src/platform-info-devices.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2020 "IoT.bzh" + * + * + * 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. + */ + +#ifndef PLATFORM_INFO_DEVICE_H +#define PLATFORM_INFO_DEVICE_H + +#include <afb/afb-binding.h> +#include "json-c/json.h" + +#define PINFO_OK (0) +#define PINFO_ERR (-1) + +struct udev; +struct udev_monitor; +struct pthread_t; + +typedef struct { + struct json_object* info; + int client_count; +}pinfo_api_ctx_t; + +typedef struct { + struct udev *udev_ctx; + struct udev_monitor *umon_hndl; + struct json_object *filter; + struct json_object *mask; + pinfo_api_ctx_t *api_ctx; + void(*umon_cb)(void* client_ctx, struct json_object* jdevice); + pthread_t th; + afb_event_t ev_devs_changed; +}pinfo_client_ctx_t; + + +int pinfo_device_monitor(afb_req_t req); +struct json_object* pinfo_device_scan(json_object *jfilter, json_object* jmask); + +#endif diff --git a/test/afb-test/tests/aft-BasicAPITests.lua b/test/afb-test/tests/aft-BasicAPITests.lua index b2d4bdd..77b645a 100644 --- a/test/afb-test/tests/aft-BasicAPITests.lua +++ b/test/afb-test/tests/aft-BasicAPITests.lua @@ -52,6 +52,8 @@ _AFT.setBeforeAll(function() end end) +local ev_mon_arg = "monitor-devices" + -- This tests the 'get' verb of the platform-info API _AFT.testVerbStatusSuccess(testPrefix.."get", "platform-info", "get", ".layers.agl-manifest") @@ -61,8 +63,11 @@ _AFT.testVerbStatusSuccess(testPrefix.."get", "platform-info", "get", {}) -- This tests the 'set' verb of the platform-info API _AFT.testVerbStatusSuccess(testPrefix.."set", "platform-info", "set", {arg=".build.layers.agl-manifest.revision", value="test"}) --- This tests the 'subscribe' verb of the platform-info API -_AFT.testVerbStatusSuccess(testPrefix.."subscribe", "platform-info", "subscribe", {}) +-- This tests the 'subscribe' verb of the platform-info API for monitoring devices +_AFT.testVerbStatusSuccess(testPrefix.."subscribe", "platform-info", "subscribe", {event=ev_mon_arg}) + +-- This tests the 'unsubscribe' verb of the platform-info API for monitoring devices +_AFT.testVerbStatusSuccess(testPrefix.."unsubscribe", "platform-info", "unsubscribe", {event=ev_mon_arg}) --- This tests the 'unsubscribe' verb of the platform-info API -_AFT.testVerbStatusSuccess(testPrefix.."unsubscribe", "platform-info", "unsubscribe", {})
\ No newline at end of file +-- This tests the 'scan' verb of the platform-info API +_AFT.testVerbStatusSuccess(testPrefix.."scan", "platform-info", "scan", {})
\ No newline at end of file |