aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitlab/issue_templates/mytemplate.md3
-rw-r--r--.gitlab/merge_request_templates/mytemplate.md3
-rw-r--r--conf.d/cmake/config.cmake7
-rw-r--r--conf.d/packaging/deb/agl-service-platform-info.dsc18
-rw-r--r--conf.d/packaging/deb/debian.agl-service-platform-info.install1
-rw-r--r--conf.d/packaging/deb/debian.changelog5
-rw-r--r--conf.d/packaging/deb/debian.compat1
-rw-r--r--conf.d/packaging/deb/debian.control19
-rw-r--r--conf.d/packaging/deb/debian.rules14
-rw-r--r--conf.d/packaging/rpm/agl-service-platform-info.spec66
-rw-r--r--src/CMakeLists.txt5
-rw-r--r--src/platform-info-apidef.h8
-rw-r--r--src/platform-info-binding.c151
-rw-r--r--src/platform-info-binding.h4
-rw-r--r--src/platform-info-devices.c500
-rw-r--r--src/platform-info-devices.h51
-rw-r--r--test/afb-test/tests/aft-BasicAPITests.lua13
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