diff options
author | Sebastien Douheret <sebastien.douheret@iot.bzh> | 2018-04-12 09:42:39 +0200 |
---|---|---|
committer | Sebastien Douheret <sebastien.douheret@iot.bzh> | 2018-07-10 23:59:53 +0200 |
commit | d67d4335f05635d06b433f7d3fa0f6a4e401ec92 (patch) | |
tree | b5d57ea1620278f65008f8faf34648f58d04da42 |
Initial commit
Change-Id: Ia434e5b4869ea19b0b78b1c586c44c15cb93c7e8
Signed-off-by: Sebastien Douheret <sebastien.douheret@iot.bzh>
38 files changed, 2099 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f8ce442 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +build* +package +nbproject/private +.stfolder +.*.sw* +*.tar.gz +__* +htdocs/node_modules +htdocs/package-lock.json diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..1900866 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,9 @@ +[submodule "conf.d/app-templates"] + path = conf.d/app-templates + url = https://gerrit.automotivelinux.org/gerrit/p/apps/app-templates.git +[submodule "ctl-utilities"] + path = ctl-utilities + url = https://gerrit.automotivelinux.org/gerrit/apps/app-controller-submodule +[submodule "afb-helpers"] + path = afb-helpers + url = https://gerrit.automotivelinux.org/gerrit/apps/app-afb-helpers-submodule diff --git a/.vscode/.cmaketools.json b/.vscode/.cmaketools.json new file mode 100644 index 0000000..0a3d8ea --- /dev/null +++ b/.vscode/.cmaketools.json @@ -0,0 +1,10 @@ +{ + "variant": { + "label": "Debug", + "keywordSettings": { + "buildType": "debug" + }, + "description": "Emit debug information without performing optimizations" + }, + "activeEnvironments": [] +}
\ No newline at end of file diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..1c01abe --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,86 @@ +{ + "configurations": [ + { + "name": "Mac", + "includePath": [ + "/usr/include", + "/usr/local/include", + "${workspaceFolder}" + ], + "defines": [], + "intelliSenseMode": "clang-x64", + "browse": { + "path": [ + "/usr/include", + "/usr/local/include", + "${workspaceFolder}" + ], + "limitSymbolsToIncludedHeaders": true, + "databaseFilename": "" + }, + "macFrameworkPath": [ + "/System/Library/Frameworks", + "/Library/Frameworks" + ] + }, + { + "name": "Linux", + "includePath": [ + "${workspaceFolder}", + "/opt/AGL/include", + "/usr/include/c++/4.8", + "/usr/include/c++/4.8/x86_64-suse-linux", + "/usr/include/c++/4.8/backward", + "/usr/lib64/gcc/x86_64-suse-linux/4.8/include", + "/usr/local/include", + "/usr/lib64/gcc/x86_64-suse-linux/4.8/include-fixed", + "/usr/x86_64-suse-linux/include", + "/usr/include", + "${workspaceFolder}/afb-helpers" + ], + "defines": [], + "intelliSenseMode": "clang-x64", + "browse": { + "path": [ + "${workspaceFolder}", + "/opt/AGL/include", + "/usr/include/c++/4.8", + "/usr/include/c++/4.8/x86_64-suse-linux", + "/usr/include/c++/4.8/backward", + "/usr/lib64/gcc/x86_64-suse-linux/4.8/include", + "/usr/local/include", + "/usr/lib64/gcc/x86_64-suse-linux/4.8/include-fixed", + "/usr/x86_64-suse-linux/include", + "/usr/include" + ], + "limitSymbolsToIncludedHeaders": true, + "databaseFilename": "" + }, + "compilerPath": "/usr/bin/gcc", + "cStandard": "c11", + "cppStandard": "c++14" + }, + { + "name": "Win32", + "includePath": [ + "C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/include", + "${workspaceFolder}" + ], + "defines": [ + "_DEBUG", + "UNICODE", + "_UNICODE" + ], + "intelliSenseMode": "msvc-x64", + "browse": { + "path": [ + "C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/include/*", + "${workspaceFolder}" + ], + "limitSymbolsToIncludedHeaders": true, + "databaseFilename": "" + } + } + ], + "version": 3 +}
\ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..b6f6fc5 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,40 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "(gdb) Launch", + "type": "cppdbg", + "request": "launch", + "program": "/opt/AGL/bin/afb-daemon", + "args": [ + "--port=5678", + "--ws-client=unix:/tmp/supervisor", + "--workdir=${workspaceRoot}/build/package/", + "--ldpaths=lib", + "--roothttp=htdocs", + "--token=", + "--tracereq=common", + "-vvv" + ], + "additionalSOLibSearchPath": "${workspaceRoot}/build/package/lib", + "stopAtEntry": false, + "cwd": "${workspaceRoot}/build/package", + "environment": [ + ], + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ], + "showDisplayString": true, + "preLaunchTask": "Build" + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..d2877c4 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,49 @@ +{ + "files.associations": { + "*.bb": "bat", + "*.inc": "bat", + "*.bbclass": "bat", + "*.rules": "shellscript", + "json.h": "c", + "afb-binding.h": "c", + "string.h": "c", + "stdio.h": "c", + "wrap-json.h": "c", + "curl-wrap.h": "c", + "afb-binding-v2.h": "c", + "afb-daemon-itf.h": "c", + "afb-service-itf.h": "c", + "afb-req.h": "c", + "afb-req-v2.h": "c", + "xds-service.h": "c", + "xds-service-api.h": "c", + "*.tcc": "c", + "functional": "c", + "array": "c", + "stdbool.h": "c", + "cstring": "c", + "xds-service-apidef.h": "c", + "tuple": "c", + "type_traits": "c", + "utility": "c", + "supervisor-service.h": "c", + "typeinfo": "c", + "istream": "c" + }, + + // Words to add to dictionary for a workspace. + "cSpell.words": [ + "callbinder", + "gotevent", + "ldpaths", + "lightgreen", + "mysecret", + "onevent", + "openapi", + "outevt", + "replyerr", + "replyok", + "reqid", + "roothttp" + ] +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..d1709f7 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,33 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "Initial Build", + "type": "shell", + "command": "rm -rf build && mkdir -p build && cd build && pwd && cmake ..", + "problemMatcher": [ + "$gcc" + ] + }, + { + "label": "Build", + "type": "shell", + "command": "clear && cd build && make", + "presentation": { + "echo": true, + "reveal": "always", + "focus": false, + "panel": "shared" + }, + "problemMatcher": [ + "$gcc" + ], + "group": { + "kind": "build", + "isDefault": true + } + } + ] +} diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..f757721 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,3 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 3.3) + +include(${CMAKE_CURRENT_SOURCE_DIR}/conf.d/cmake/config.cmake) @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 IoT.bzh + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..155cb10 --- /dev/null +++ b/README.md @@ -0,0 +1,58 @@ +# agl-service-xds + +An AGL binding used to control collected data. Those data may come from +`agl-low-collector` or from AGL `supervision`. + +**UNDER DEVELOPMENT - this binding is not fully functional, it's a proof of concept for now** + +## Setup + +```bash +git clone --recursive https://github.com/iotbzh/agl-server-xds +cd agl-server-xds +``` + +## Build for AGL + +```bash +#setup your build environement +. /xdt/sdk/environment-setup-aarch64-agl-linux +#build your application +./conf.d/autobuild/agl/autobuild package +``` + +## Build for 'native' Linux distros (Fedora, openSUSE, Debian, Ubuntu, ...) + +```bash +./conf.d/autobuild/linux/autobuild package +``` + +You can also use binary package from OBS: [opensuse.org/LinuxAutomotive][opensuse.org/LinuxAutomotive] + +## Test + +### Native setup + +Here are commands used to setup some bindings in order to test on `xds-service` natively on a Linux host: + +```bash +afs-supervisor --port 1712 --token HELLO --ws-server=unix:/tmp/supervisor -vv + +cd $ROOT_DIR/app-framework-binder +afb-daemon -t '' -p 5555 -M --roothttp test --ws-server unix:ave --name test_server -vv +afb-daemon -t '' -p 4444 -M --roothttp test --no-ldpaths --ws-client unix:ave --name test_client -vv + +cd $ROOT_DIR/agl-service-harvester +afb-daemon --port=1234 --workdir=./build/package --ldpaths=lib --roothttp=htdocs --token= --tracereq=common -vv --ws-server unix:/tmp/harvester + +cd $ROOT_DIR/agl-service-xds +./conf.d/autobuild/linux/autobuild build +afb-daemon --port=5678 --workdir=./build/package --ldpaths=lib --roothttp=htdocs --token= --ws-client=unix:/tmp/supervisor --ws-client=unix:/tmp/harvester -vv + +``` + +## Deploy + +### AGL + +TBD diff --git a/afb-helpers b/afb-helpers new file mode 160000 +Subproject 9e880552ea9f375de636b535c04b4729aaa1fa9 diff --git a/conf.d/app-templates b/conf.d/app-templates new file mode 160000 +Subproject a3c312ece0a77310a9d8ecde1dd0f1267646f4f diff --git a/conf.d/autobuild/agl/autobuild b/conf.d/autobuild/agl/autobuild new file mode 100755 index 0000000..83097ab --- /dev/null +++ b/conf.d/autobuild/agl/autobuild @@ -0,0 +1,67 @@ +#!/usr/bin/make -f +# Copyright (C) 2015, 2016 "IoT.bzh" +# Author "Romain Forlot" <romain.forlot@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. + +THISFILE := $(lastword $(MAKEFILE_LIST)) +BUILD_DIR := $(abspath $(dir $(THISFILE)/../../../../..)/build) +DEST := ${BUILD_DIR}/target + +.PHONY: all clean distclean configure build package help update + +all: help + +help: + @echo "List of targets available:" + @echo "" + @echo "- all" + @echo "- clean" + @echo "- distclean" + @echo "- configure" + @echo "- build: compilation, link and prepare files for package into a widget" + @echo "- package: output a widget file '*.wgt'" + @echo "- install: install in your ${CMAKE_INSTALL_DIR} directory" + @echo "" + @echo "Usage: ./conf.d/autobuild/agl/autobuild package DEST=${HOME}/opt" + @echo "Don't use your build dir as DEST as wgt file is generated at this location" + +update: configure + @cmake --build ${BUILD_DIR} --target autobuild + +clean: + @([ -d ${BUILD_DIR} ] && make -C ${BUILD_DIR} clean) || echo Nothing to clean + +distclean: + @rm -rf ${BUILD_DIR} + +configure: ${BUILD_DIR}/Makefile + +build: configure + @cmake --build ${BUILD_DIR} ${BUILD_ARGS} --target all + +package: build + @mkdir -p ${BUILD_DIR}/$@/bin + @mkdir -p ${BUILD_DIR}/$@/etc + @mkdir -p ${BUILD_DIR}/$@/lib + @mkdir -p ${BUILD_DIR}/$@/htdocs + @mkdir -p ${BUILD_DIR}/$@/var + @cmake --build ${BUILD_DIR} --target widget + @mkdir -p ${DEST} && cp ${BUILD_DIR}/*wgt ${DEST} + +install: build + @cmake --build ${BUILD_DIR} --target install + +${BUILD_DIR}/Makefile: + @[ -d ${BUILD_DIR} ] || mkdir -p ${BUILD_DIR} + @[ -f ${BUILD_DIR}/Makefile ] || (cd ${BUILD_DIR} && cmake ${CONFIGURE_ARGS} ..) diff --git a/conf.d/autobuild/linux/autobuild b/conf.d/autobuild/linux/autobuild new file mode 100755 index 0000000..83097ab --- /dev/null +++ b/conf.d/autobuild/linux/autobuild @@ -0,0 +1,67 @@ +#!/usr/bin/make -f +# Copyright (C) 2015, 2016 "IoT.bzh" +# Author "Romain Forlot" <romain.forlot@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. + +THISFILE := $(lastword $(MAKEFILE_LIST)) +BUILD_DIR := $(abspath $(dir $(THISFILE)/../../../../..)/build) +DEST := ${BUILD_DIR}/target + +.PHONY: all clean distclean configure build package help update + +all: help + +help: + @echo "List of targets available:" + @echo "" + @echo "- all" + @echo "- clean" + @echo "- distclean" + @echo "- configure" + @echo "- build: compilation, link and prepare files for package into a widget" + @echo "- package: output a widget file '*.wgt'" + @echo "- install: install in your ${CMAKE_INSTALL_DIR} directory" + @echo "" + @echo "Usage: ./conf.d/autobuild/agl/autobuild package DEST=${HOME}/opt" + @echo "Don't use your build dir as DEST as wgt file is generated at this location" + +update: configure + @cmake --build ${BUILD_DIR} --target autobuild + +clean: + @([ -d ${BUILD_DIR} ] && make -C ${BUILD_DIR} clean) || echo Nothing to clean + +distclean: + @rm -rf ${BUILD_DIR} + +configure: ${BUILD_DIR}/Makefile + +build: configure + @cmake --build ${BUILD_DIR} ${BUILD_ARGS} --target all + +package: build + @mkdir -p ${BUILD_DIR}/$@/bin + @mkdir -p ${BUILD_DIR}/$@/etc + @mkdir -p ${BUILD_DIR}/$@/lib + @mkdir -p ${BUILD_DIR}/$@/htdocs + @mkdir -p ${BUILD_DIR}/$@/var + @cmake --build ${BUILD_DIR} --target widget + @mkdir -p ${DEST} && cp ${BUILD_DIR}/*wgt ${DEST} + +install: build + @cmake --build ${BUILD_DIR} --target install + +${BUILD_DIR}/Makefile: + @[ -d ${BUILD_DIR} ] || mkdir -p ${BUILD_DIR} + @[ -f ${BUILD_DIR}/Makefile ] || (cd ${BUILD_DIR} && cmake ${CONFIGURE_ARGS} ..) diff --git a/conf.d/cmake/config.cmake b/conf.d/cmake/config.cmake new file mode 100644 index 0000000..face63f --- /dev/null +++ b/conf.d/cmake/config.cmake @@ -0,0 +1,209 @@ +########################################################################### +# Copyright 2018 IoT.bzh +# +# author: Sebastien Douheret <sebastien@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. +########################################################################### + +# Project Info +# ------------------ +set(PROJECT_NAME xds-service) +set(PROJECT_VERSION "1.0") +set(PROJECT_PRETTY_NAME "XDS collector service for AGL") +set(PROJECT_DESCRIPTION "Provide an AGL XDS collector Binding") +set(PROJECT_URL "https://github.com/iotbzh/agl-service-xds") +set(PROJECT_ICON "icon.png") +set(PROJECT_AUTHOR "Sebastien Douheret") +set(PROJECT_AUTHOR_MAIL "sebastien@iot.bzh") +set(PROJECT_LICENSE "APL2.0") +set(PROJECT_LANGUAGES "C") + +# Where are stored default templates files from submodule or subtree app-templates in your project tree +# relative to the root project directory +set(PROJECT_APP_TEMPLATES_DIR "conf.d/app-templates") + +# Where are stored your external libraries for your project. This is 3rd party library that you don't maintain +# but used and must be built and linked. +# set(PROJECT_LIBDIR "libs") + +# Where are stored data for your application. Pictures, static resources must be placed in that folder. +# set(PROJECT_RESOURCES "data") + +# Which directories inspect to find CMakeLists.txt target files +# set(PROJECT_SRC_DIR_PATTERN "*") + +# Compilation Mode (DEBUG, RELEASE) +# ---------------------------------- +set(CMAKE_BUILD_TYPE "DEBUG") +#set(USE_EFENCE 1) + +# Kernel selection if needed. You can choose between a +# mandatory version to impose a minimal version. +# Or check Kernel minimal version and just print a Warning +# about missing features and define a preprocessor variable +# to be used as preprocessor condition in code to disable +# incompatibles features. Preprocessor define is named +# KERNEL_MINIMAL_VERSION_OK. +# +# NOTE*** FOR NOW IT CHECKS KERNEL Yocto environment and +# Yocto SDK Kernel version. +# ----------------------------------------------- +#set (kernel_mandatory_version 4.8) +#set (kernel_minimal_version 4.8) + +# Compiler selection if needed. Impose a minimal version. +# ----------------------------------------------- +set (gcc_minimal_version 4.9) + +# PKG_CONFIG required packages +# ----------------------------- +set (PKG_REQUIRED_LIST + json-c + libsystemd>=222 + afb-daemon>=4.0 + libmicrohttpd>=0.9.55 +) + +# Prefix path where will be installed the files +# Default: /usr/local (need root permission to write in) +# ------------------------------------------------------ +set(CMAKE_INSTALL_PREFIX $ENV{HOME}/opt) + +# Customize link option +# ----------------------------- +#list(APPEND link_libraries -an-option) +list(APPEND link_libraries afb-helpers) + +# Compilation options definition +# Use CMake generator expressions to specify only for a specific language +# Values are prefilled with default options that is currently used. +# Either separate options with ";", or each options must be quoted separately +# DO NOT PUT ALL OPTION QUOTED AT ONCE , COMPILATION COULD FAILED ! +# ---------------------------------------------------------------------------- +#set(COMPILE_OPTIONS +# -Wall +# -Wextra +# -Wconversion +# -Wno-unused-parameter +# -Wno-sign-compare +# -Wno-sign-conversion +# -Werror=maybe-uninitialized +# -Werror=implicit-function-declaration +# -ffunction-sections +# -fdata-sections +# -fPIC +# CACHE STRING "Compilation flags") +#set(C_COMPILE_OPTIONS "" CACHE STRING "Compilation flags for C language.") +#set(CXX_COMPILE_OPTIONS "" CACHE STRING "Compilation flags for C++ language.") +#set(PROFILING_COMPILE_OPTIONS +# -g +# -O0 +# -pg +# -Wp,-U_FORTIFY_SOURCE +# CACHE STRING "Compilation flags for PROFILING build type.") +#set(DEBUG_COMPILE_OPTIONS +# -g +# -ggdb +# -Wp,-U_FORTIFY_SOURCE +# CACHE STRING "Compilation flags for DEBUG build type.") +#set(CCOV_COMPILE_OPTIONS +# -g +# -O2 +# --coverage +# CACHE STRING "Compilation flags for CCOV build type.") +#set(RELEASE_COMPILE_OPTIONS +# -g +# -O2 +# CACHE STRING "Compilation flags for RELEASE build type.") + +# (BUG!!!) as PKG_CONFIG_PATH does not work [should be an env variable] +# --------------------------------------------------------------------- +set(CMAKE_PREFIX_PATH ${CMAKE_INSTALL_PREFIX}/lib64/pkgconfig ${CMAKE_INSTALL_PREFIX}/lib/pkgconfig) +set(LD_LIBRARY_PATH ${CMAKE_INSTALL_PREFIX}/lib64 ${CMAKE_INSTALL_PREFIX}/lib) + +# Optional location for config.xml.in +# ----------------------------------- +#set(WIDGET_ICON "\"conf.d/wgt/${PROJECT_ICON}\"" CACHE PATH "Path to the widget icon") +#set(WIDGET_CONFIG_TEMPLATE "\"${CMAKE_CURRENT_SOURCE_DIR}/conf.d/wgt/config.xml.in\"" CACHE PATH "Path to widget config file template (config.xml.in)") + +# Mandatory widget Mimetype specification of the main unit +# -------------------------------------------------------------------------- +# Choose between : +#- text/html : HTML application, +# content.src designates the home page of the application +# +#- application/vnd.agl.native : AGL compatible native, +# content.src designates the relative path of the binary. +# +# - application/vnd.agl.service: AGL service, content.src is not used. +# +#- ***application/x-executable***: Native application, +# content.src designates the relative path of the binary. +# For such application, only security setup is made. +# +set(WIDGET_TYPE application/vnd.agl.service) + +# Mandatory Widget entry point file of the main unit +# -------------------------------------------------------------- +# This is the file that will be executed, loaded, +# at launch time by the application framework. +# +set(WIDGET_ENTRY_POINT config.xml) + +# Optional dependencies order +# --------------------------- +#set(EXTRA_DEPENDENCIES_ORDER) + +# Optional Extra global include path +# ----------------------------------- +#set(EXTRA_INCLUDE_DIRS) + +# Optional extra libraries +# ------------------------- +#set(EXTRA_LINK_LIBRARIES) + +# Optional force binding installation +# ------------------------------------ +# set(BINDINGS_INSTALL_PREFIX PrefixPath ) + +# Optional force binding Linking flag +# ------------------------------------ +# set(BINDINGS_LINK_FLAG LinkOptions ) + +# Optional force package prefix generation, like widget +# ----------------------------------------------------- +# set(PKG_PREFIX DestinationPath) + +# Optional Application Framework security token +# and port use for remote debugging. +#------------------------------------------------------------ +set(AFB_TOKEN "" CACHE PATH "Default binder security token") +set(AFB_REMPORT "5678" CACHE PATH "Default binder listening port") + +# Print a helper message when every thing is finished +# ---------------------------------------------------- +set(CLOSING_MESSAGE "Typical binding launch: afb-daemon --port=${AFB_REMPORT} --workdir=${CMAKE_BINARY_DIR}/package --ldpaths=lib --roothttp=htdocs --token=\"${AFB_TOKEN}\" --verbose --ws-client=unix:/tmp/supervisor --ws-client=unix:/tmp/harvester") +set(PACKAGE_MESSAGE "Install widget file using in the target : afm-util install ${PROJECT_NAME}.wgt") + +# Optional schema validator about now only XML, LUA and JSON +# are supported +#------------------------------------------------------------ +#set(LUA_CHECKER "luac" "-p" CACHE STRING "LUA compiler") +#set(XML_CHECKER "xmllint" CACHE STRING "XML linter") +#set(JSON_CHECKER "json_verify" CACHE STRING "JSON linter") + +# This include is mandatory and MUST happens at the end +# of this file, else you expose you to unexpected behavior +# ----------------------------------------------------------- +include(${PROJECT_APP_TEMPLATES_DIR}/cmake/common.cmake) diff --git a/conf.d/packaging/agl-xds-service.dsc b/conf.d/packaging/agl-xds-service.dsc new file mode 100644 index 0000000..d99d92d --- /dev/null +++ b/conf.d/packaging/agl-xds-service.dsc @@ -0,0 +1,18 @@ +Format: 1.0 +Source: agl-xds-service +Binary: agl-xds-service-bin +Architecture: any +Version: 2.0-0 +Maintainer: Iot-Team <secretaria@iot.bzh> +Standards-Version: 3.8.2 +Homepage: https://github.com/iotbzh/xds-service +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-xds-service_1.0.orig.tar.gz diff --git a/conf.d/packaging/agl-xds-service.spec b/conf.d/packaging/agl-xds-service.spec new file mode 100644 index 0000000..b8b96ab --- /dev/null +++ b/conf.d/packaging/agl-xds-service.spec @@ -0,0 +1,66 @@ +########################################################################### +# Copyright 2018 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-xds-service +Version: 1.0 +Release: 1 +Group: AGL +License: APL2.0 +Summary: Provide an AGL XDS collector Binding +Url: https://github.com/iotbzh/xds-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/xds-service +%define __cmake cmake + +%description +Provide an AGL xds collector 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/conf.d/packaging/debian.agl-xds-service.install b/conf.d/packaging/debian.agl-xds-service.install new file mode 100644 index 0000000..5858efd --- /dev/null +++ b/conf.d/packaging/debian.agl-xds-service.install @@ -0,0 +1,2 @@ +/opt/AGL/* +/etc/profile.d/* diff --git a/conf.d/packaging/debian.changelog b/conf.d/packaging/debian.changelog new file mode 100644 index 0000000..721428c --- /dev/null +++ b/conf.d/packaging/debian.changelog @@ -0,0 +1,5 @@ +agl-xds-service (1.0-0) UNRELEASED; urgency=low + + * init build + + -- Iot-Team <secretaria@iot.bzh> Mon, 2 Apr 2018 10:50:38 +0100 diff --git a/conf.d/packaging/debian.compat b/conf.d/packaging/debian.compat new file mode 100644 index 0000000..45a4fb7 --- /dev/null +++ b/conf.d/packaging/debian.compat @@ -0,0 +1 @@ +8 diff --git a/conf.d/packaging/debian.control b/conf.d/packaging/debian.control new file mode 100644 index 0000000..aab9de9 --- /dev/null +++ b/conf.d/packaging/debian.control @@ -0,0 +1,19 @@ +Priority: optional +Maintainer: Iot-Team <secretaria@iot.bzh> +Source: agl-xds-service +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/xds-service + +Package: agl-xds-service +Section: libs +Architecture: any +Description: Provide an AGL xds Binding diff --git a/conf.d/packaging/debian.rules b/conf.d/packaging/debian.rules new file mode 100644 index 0000000..7a5bcb6 --- /dev/null +++ b/conf.d/packaging/debian.rules @@ -0,0 +1,87 @@ +#!/usr/bin/make -f +# -*- makefile -*- +# Uncomment this to turn on verbose mode. +export DH_VERBOSE=1 + +# These are used for cross-compiling and for saving the configure script +# from having to guess our platform (since we know it already) +DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE) +DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE) + + +CFLAGS = -Wall -g + +ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) + CFLAGS += -O0 +else + CFLAGS += -O2 +endif +ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS))) + INSTALL_PROGRAM += -s +endif + +configure: configure-stamp +configure-stamp: + dh_testdir + # + touch configure-stamp + +build: build-stamp +build-stamp: configure-stamp + dh_testdir + mkdir -p build + cd build;cmake ../ -DCMAKE_INSTALL_PREFIX:PATH=/opt/AGL/xds-service -DCMAKE_INSTALL_LIBDIR:PATH=lib/$(DEB_HOST_MULTIARCH);$(MAKE) + # + touch build-stamp + +clean: + #dh_testdir + dh_testroot + rm -f configure-stamp build-stamp + [ ! -f Makefile ] || $(MAKE) distclean + #dh_clean + +install: build + dh_testdir + dh_testroot + dh_prep + dh_installdirs + # Add here commands to install the package into debian/tmp + mkdir -p debian/tmp/opt/AGL/xds-service;cd build;make populate;cp -r package/* ../debian/tmp/opt/AGL/xds-service/ + mkdir -p debian/tmp/etc/profile.d + echo '#---------- AGL xds-service options Start ---------" ' > debian/tmp/etc/profile.d/AGL_xds-service.sh + echo '# Object: AGL cmake option for binder/bindings' >> debian/tmp/etc/profile.d/AGL_xds-service.sh + echo 'export LD_LIBRARY_PATH=/opt/AGL/xds-service/lib/$(DEB_HOST_MULTIARCH):$$LD_LIBRARY_PATH' >> debian/tmp/etc/profile.d/AGL_xds-service.sh + echo 'export LIBRARY_PATH=/opt/AGL/xds-service/lib/$(DEB_HOST_MULTIARCH):$$LIBRARY_PATH' >> debian/tmp/etc/profile.d/AGL_xds-service.sh + echo 'export PATH=/opt/AGL/xds-service/bin:$$PATH' >> debian/tmp/etc/profile.d/AGL_xds-service.sh + echo '#---------- AGL options End ---------' >> debian/tmp/etc/profile.d/AGL_xds-service.sh + # Move all files in their corresponding package + dh_install --list-missing -s --sourcedir=debian/tmp + # empty dependency_libs in .la files + #sed -i "/dependency_libs/ s/'.*'/''/" `find debian/ -name '*.la'` + +# Build architecture-independent files here. +binary-indep: build install +# We have nothing to do by default. + +# Build architecture-dependent files here. +binary-arch: build install + dh_testdir + dh_testroot + dh_installchangelogs + dh_installdocs + dh_installexamples + dh_installman + dh_link + dh_strip + dh_compress + dh_fixperms + dh_makeshlibs -V + dh_installdeb + dh_shlibdeps --dpkg-shlibdeps-params=--ignore-missing-info + dh_gencontrol + dh_md5sums + dh_builddeb + +binary: binary-indep binary-arch +.PHONY: build clean binary-indep binary-arch binary install diff --git a/conf.d/wgt/config.xml.in b/conf.d/wgt/config.xml.in new file mode 100644 index 0000000..7824795 --- /dev/null +++ b/conf.d/wgt/config.xml.in @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<widget xmlns="http://www.w3.org/ns/widgets" id="@PROJECT_NAME@" version="@PROJECT_VERSION@"> + <name>@PROJECT_NAME@</name> + <icon src="@PROJECT_ICON@"/> + <content src="@WIDGET_ENTRY_POINT@" type="@WIDGET_TYPE@"/> + <description>@PROJECT_DESCRIPTION@</description> + <author>@PROJECT_AUTHOR@ <@PROJECT_AUTHOR_MAIL@></author> + <license>@PROJECT_LICENSE@</license> + <feature name="urn:AGL:widget:provided-api"> + <param name="xds-service" value="ws" /> + </feature> + <feature name="urn:AGL:widget:required-api"> + <param name="lib/afb-xds-service.so" value="local" /> + <param name="harvester" value="ws" /> + <param name="supervisor" value="ws" /> + </feature> +</widget> diff --git a/htdocs/AFB-websock.js b/htdocs/AFB-websock.js new file mode 100644 index 0000000..3d4831f --- /dev/null +++ b/htdocs/AFB-websock.js @@ -0,0 +1,177 @@ +var urlWS; +var urlhttp; + +AFB = function(base, initialtoken){ + +urlWS = "ws://"+window.location.host+"/"+base; +urlhttp = "http://"+window.location.host+"/"+base; + +/*********************************************/ +/**** ****/ +/**** AFB_context ****/ +/**** ****/ +/*********************************************/ +var AFB_context; +{ + var UUID = undefined; + var TOKEN = initialtoken; + + var context = function(token, uuid) { + this.token = token; + this.uuid = uuid; + } + + context.prototype = { + get token() {return TOKEN;}, + set token(tok) {if(tok) TOKEN=tok;}, + get uuid() {return UUID;}, + set uuid(id) {if(id) UUID=id;} + }; + + AFB_context = new context(); +} +/*********************************************/ +/**** ****/ +/**** AFB_websocket ****/ +/**** ****/ +/*********************************************/ +var AFB_websocket; +{ + var CALL = 2; + var RETOK = 3; + var RETERR = 4; + var EVENT = 5; + + var PROTO1 = "x-afb-ws-json1"; + + AFB_websocket = function(onopen, onabort) { + var u = urlWS; + if (AFB_context.token) { + u = u + '?x-afb-token=' + AFB_context.token; + if (AFB_context.uuid) + u = u + '&x-afb-uuid=' + AFB_context.uuid; + } + this.ws = new WebSocket(u, [ PROTO1 ]); + this.pendings = {}; + this.awaitens = {}; + this.counter = 0; + this.ws.onopen = onopen.bind(this); + this.ws.onerror = onerror.bind(this); + this.ws.onclose = onclose.bind(this); + this.ws.onmessage = onmessage.bind(this); + this.onopen = onopen; + this.onabort = onabort; + this.onclose = onabort; + } + + function onerror(event) { + var f = this.onabort; + if (f) { + delete this.onopen; + delete this.onabort; + f && f(this); + } + this.onerror && this.onerror(this); + } + + function onopen(event) { + var f = this.onopen; + delete this.onopen; + delete this.onabort; + f && f(this); + } + + function onclose(event) { + for (var id in this.pendings) { + var ferr = this.pendings[id].onerror; + ferr && ferr(null, this); + } + this.pendings = {}; + this.onclose && this.onclose(); + } + + function fire(awaitens, name, data) { + var a = awaitens[name]; + if (a) + a.forEach(function(handler){handler(data);}); + var i = name.indexOf("/"); + if (i >= 0) { + a = awaitens[name.substring(0,i)]; + if (a) + a.forEach(function(handler){handler(data);}); + } + a = awaitens["*"]; + if (a) + a.forEach(function(handler){handler(data);}); + } + + function reply(pendings, id, ans, offset) { + if (id in pendings) { + var p = pendings[id]; + delete pendings[id]; + var f = p[offset]; + f(ans); + } + } + + function onmessage(event) { + var obj = JSON.parse(event.data); + var code = obj[0]; + var id = obj[1]; + var ans = obj[2]; + AFB_context.token = obj[3]; + switch (code) { + case RETOK: + reply(this.pendings, id, ans, 0); + break; + case RETERR: + reply(this.pendings, id, ans, 1); + break; + case EVENT: + default: + fire(this.awaitens, id, ans); + break; + } + } + + function close() { + this.ws.close(); + this.onabort(); + } + + function call(method, request) { + return new Promise((function(resolve, reject){ + var id, arr; + do { + id = String(this.counter = 4095 & (this.counter + 1)); + } while (id in this.pendings); + this.pendings[id] = [ resolve, reject ]; + arr = [CALL, id, method, request ]; + if (AFB_context.token) arr.push(AFB_context.token); + this.ws.send(JSON.stringify(arr)); + }).bind(this)); + } + + function onevent(name, handler) { + var id = name; + var list = this.awaitens[id] || (this.awaitens[id] = []); + list.push(handler); + } + + AFB_websocket.prototype = { + close: close, + call: call, + onevent: onevent + }; +} +/*********************************************/ +/**** ****/ +/**** ****/ +/**** ****/ +/*********************************************/ +return { + context: AFB_context, + ws: AFB_websocket +}; +}; + diff --git a/htdocs/CMakeLists.txt b/htdocs/CMakeLists.txt new file mode 100644 index 0000000..7aa0ce1 --- /dev/null +++ b/htdocs/CMakeLists.txt @@ -0,0 +1,33 @@ +########################################################################### +# Copyright 2015, 2016, 2017 IoT.bzh +# +# author: Fulup Ar Foll <fulup@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. +########################################################################### + + + +################################################## +# HTML Testing Files +################################################## +PROJECT_TARGET_ADD(htdocs) + + file(GLOB SOURCE_FILES "*.html" "*.js" "*.jpg" "*.css" "assets") + + add_input_files("${SOURCE_FILES}") + + SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES + LABELS "HTDOCS" + OUTPUT_NAME ${TARGET_NAME} + ) diff --git a/htdocs/assets/background_iot_bzh_light.jpg b/htdocs/assets/background_iot_bzh_light.jpg Binary files differnew file mode 100644 index 0000000..f47d2ee --- /dev/null +++ b/htdocs/assets/background_iot_bzh_light.jpg diff --git a/htdocs/assets/favicon.ico b/htdocs/assets/favicon.ico Binary files differnew file mode 100644 index 0000000..eeb7ab7 --- /dev/null +++ b/htdocs/assets/favicon.ico diff --git a/htdocs/assets/iot-bzh-logo-small.png b/htdocs/assets/iot-bzh-logo-small.png Binary files differnew file mode 100644 index 0000000..2c3b2ae --- /dev/null +++ b/htdocs/assets/iot-bzh-logo-small.png diff --git a/htdocs/index.html b/htdocs/index.html new file mode 100644 index 0000000..28d58ab --- /dev/null +++ b/htdocs/index.html @@ -0,0 +1,48 @@ +<html> + +<head> + <title>XDS data collector binding Test</title> + <link rel="stylesheet" href="iotbzh-Binding.css"> + <link rel="icon" type="image/x-icon" href="assets/favicon.ico"> + <script type="text/javascript" src="AFB-websock.js"></script> + <script type="text/javascript" src="iotbzh-Binding.js"></script> +</head> + +<body class="page-content" onload="init()"> + + <img src="assets/iot-bzh-logo-small.png"> + + <h1>XDS data collector prototype Test</h1> + + <button id="connected" onclick="init()">Binder WS Fail</button> + <button id="monitoring" onclick="window.open('/monitoring/monitor.html','_monitor_ctl')">Debug/Monitoring</a> + </button> + + <br> + <br> + + <div> + <button onclick="callbinder('xds-service','list' ,{});">List</button> + </li> + + <button onclick="callbinder('xds-service','trace' ,{'ws': 'unix:ave'});">Trace ws unix:ave</button> + </li> + + </div> + + <br> + + <div id="main" style="visibility:hidden"> + <ol> + <li>Question + <pre id="question"></pre> + </li> + <li>Response + <pre id="output"></pre> + </li> + <li>Events: + <pre id="outevt"></pre> + </li> + </ol> + </div> +</body> diff --git a/htdocs/iotbzh-Binding.css b/htdocs/iotbzh-Binding.css new file mode 100644 index 0000000..96a04cc --- /dev/null +++ b/htdocs/iotbzh-Binding.css @@ -0,0 +1,59 @@ +body.page-content { + height: 100%; + width: auto; + margin-top: 20px; + background: url("assets/background_iot_bzh_light.jpg") 0 0 no-repeat; + background-size: cover; + background-position: center; +} + +img { + float: right; +} + +#question, +#output, +#outevt { + white-space: pre-wrap; +} + +button { + margin-right: 10px; + padding: 6px 8px; + font-size: large; +} + +pre { + outline: 1px solid #ccc; + padding: 5px; + margin: 5px; + background-color: white; + opacity: 0.85; + min-height: 5pc; + max-height: 10pc; + overflow: auto; +} + +pre#output { + max-height: 30pc; +} + +.string { + color: green; +} + +.number { + color: darkorange; +} + +.boolean { + color: blue; +} + +.null { + color: magenta; +} + +.key { + color: red; +} diff --git a/htdocs/iotbzh-Binding.js b/htdocs/iotbzh-Binding.js new file mode 100644 index 0000000..ae32f83 --- /dev/null +++ b/htdocs/iotbzh-Binding.js @@ -0,0 +1,149 @@ +var afb = new AFB("api", "mysecret"); +var ws; +var evtIdx = 0; +var count = 0; + + +//********************************************** +// Logger +//********************************************** +var log = { + command: function (api, verb, query) { + console.log("subscribe api=" + api + " verb=" + verb + " query=", query); + var question = urlWS + "/" + api + "/" + verb + "?query=" + JSON.stringify(query); + log._write("question", count + ": " + log.syntaxHighlight(question)); + }, + + event: function (obj) { + console.log("gotevent:" + JSON.stringify(obj)); + log._write("outevt", (evtIdx++) + ": " + JSON.stringify(obj)); + }, + + reply: function (obj) { + console.log("replyok:" + JSON.stringify(obj)); + log._write("output", count + ": OK: " + log.syntaxHighlight(obj)); + }, + + error: function (obj) { + console.log("replyerr:" + JSON.stringify(obj)); + log._write("output", count + ": ERROR: " + log.syntaxHighlight(obj)); + }, + + _write: function (element, msg) { + var el = document.getElementById(element); + el.innerHTML += msg + '\n'; + + // auto scroll down + setTimeout(function () { + el.scrollTop = el.scrollHeight; + }, 100); + }, + + syntaxHighlight: function (json) { + if (typeof json !== 'string') { + json = JSON.stringify(json, undefined, 2); + } + json = json.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>'); + return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) { + var cls = 'number'; + if (/^"/.test(match)) { + if (/:$/.test(match)) { + cls = 'key'; + } else { + cls = 'string'; + } + } else if (/true|false/.test(match)) { + cls = 'boolean'; + } else if (/null/.test(match)) { + cls = 'null'; + } + return '<span class="' + cls + '">' + match + '</span>'; + }); + }, +}; + +//********************************************** +// Generic function to call binder +//*********************************************** +function callbinder(api, verb, query) { + log.command(api, verb, query); + + // ws.call return a Promise + return ws.call(api + '/' + verb, query) + .then(function (res) { + log.reply(res); + count++; + return res; + }) + .catch(function (err) { + log.reply(err); + count++; + throw err; + }); +}; + +//********************************************** +// +//********************************************** +var topo = null; + +function getTopo() { + callbinder('supervisor', 'list', null) + .then(list => { + var reqs = []; + for (pid in list.response) { + this.pid = pid; + reqs.push( + callbinder('supervisor', 'config', { + "pid": pid + }) + .then((res) => { + return { + 'pid': this.pid, + 'ws_servers': res.response.ws_servers, + 'ws_clients': res.response.ws_clients + } + }) + ); + }; + return Promise.all(reqs); + }) + .then(daemons => { + console.log("daemons count ", daemons.length); + daemons.forEach(dd => { + console.log(dd); + }); + topo = daemons; + }) + .catch(err => { + console.log() + }); +}; + + + + + +//********************************************** +// Init - establish Websocket connection +//********************************************** +function init(elemID, api, verb, query) { + + function onopen() { + // Request initial authorization + callbinder('xds-service', 'auth', ''); + + document.getElementById("main").style.visibility = "visible"; + document.getElementById("connected").innerHTML = "Binder WS Active"; + document.getElementById("connected").style.background = "lightgreen"; + ws.onevent("*", log.event); + } + + function onabort() { + document.getElementById("main").style.visibility = "hidden"; + document.getElementById("connected").innerHTML = "Connected Closed"; + document.getElementById("connected").style.background = "red"; + } + + ws = new afb.ws(onopen, onabort); +} diff --git a/xds-service/CMakeLists.txt b/xds-service/CMakeLists.txt new file mode 100644 index 0000000..9ce0b81 --- /dev/null +++ b/xds-service/CMakeLists.txt @@ -0,0 +1,42 @@ +########################################################################### +# Copyright 2018 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. +########################################################################### + +# Add target to project dependency list +PROJECT_TARGET_ADD(xds-service) + + # Define project Targets + file(GLOB sourcelist "*.c") + + # Define project Targets + ADD_LIBRARY(${TARGET_NAME} MODULE ${sourcelist}) + + target_compile_options(${TARGET_NAME} + PUBLIC -Wno-unused-variable + ) + + + # Binder exposes a unique public entry point + SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES + PREFIX "afb-" + LABELS "BINDINGV2" + LINK_FLAGS ${BINDINGS_LINK_FLAG} + OUTPUT_NAME ${TARGET_NAME} + ) + + TARGET_LINK_LIBRARIES(${TARGET_NAME} + afb-helpers + ${link_libraries} + ) diff --git a/xds-service/supervisor-service.c b/xds-service/supervisor-service.c new file mode 100644 index 0000000..96955fa --- /dev/null +++ b/xds-service/supervisor-service.c @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2018 "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. + */ +#define _GNU_SOURCE +#include "supervisor-service.h" +#include "xds-service-api.h" +#include <stdbool.h> +#include <stdio.h> +#include <string.h> + +#include "curl-wrap.h" +#include "wrap-json.h" + +#define SRV_SUPERVISOR_NAME "supervisor" + +struct afb_cred { + int refcount; + uid_t uid; + gid_t gid; + pid_t pid; + const char* user; + const char* label; + const char* id; +}; + +static const char* null_str = "null"; + +static void decode_daemons_cb(void* closure, json_object* obj, + const char* resp) +{ + int rc; + struct afb_cred cred; + json_object *j_response, *j_query, *j_config, *j_ws_servers, *j_ws_clients; + json_object *j_name, *j_apis; + DAEMONS_T* daemons = (DAEMONS_T*)closure; + DAEMON_T* daemon = calloc(sizeof(DAEMON_T), 1); + + if (!daemons) + return; + + if ((rc = wrap_json_unpack(obj, "{si si si ss ss ss}", "pid", &cred.pid, + "uid", &cred.uid, "gid", &cred.gid, "id", &cred.id, + "label", &cred.label, "user", &cred.user)) + < 0) { + // TODO + return; + } + + AFB_INFO("Get config of pid %d", cred.pid); + daemon->pid = cred.pid; + + // Get config + wrap_json_pack(&j_query, "{s:i}", "pid", cred.pid); + rc = afb_service_call_sync(SRV_SUPERVISOR_NAME, "config", j_query, &j_response); + if (rc < 0) { + AFB_ERROR("Cannot get config of pid %d", cred.pid); + return; + } + + AFB_DEBUG("%s config result, res=%s", SRV_SUPERVISOR_NAME, + json_object_to_json_string(j_response)); + + if (json_object_object_get_ex(j_response, "response", &j_config)) { + // FIXME : implement free + daemon->config = j_config; + + rc = wrap_json_unpack(j_config, "{s:o s:o s:o}", + "name", &j_name, + "ws_servers", &j_ws_servers, + "ws_clients", &j_ws_clients); + if (rc < 0) { + AFB_ERROR("Error decoding config response %s", wrap_json_get_error_string(rc)); + return; + } + + daemon->name = json_object_is_type(j_name, json_type_null) ? null_str : json_object_get_string(j_name); + daemon->ws_servers = j_ws_servers; + daemon->isServer = (json_object_array_length(j_ws_servers) > 0); + daemon->ws_clients = j_ws_clients; + daemon->isClient = (json_object_array_length(j_ws_clients) > 0); + } + + // Get apis + // '{"pid":6262,"api":"monitor","verb":"get","args":{"apis":true}} + wrap_json_pack(&j_query, "{si ss ss s {sb}}", + "pid", cred.pid, + "api", "monitor", + "verb", "get", + "args", "apis", true); + rc = afb_service_call_sync(SRV_SUPERVISOR_NAME, "do", j_query, &j_response); + if (rc < 0) { + AFB_ERROR("Cannot get apis of pid %d", cred.pid); + return; + } else { + //AFB_DEBUG("%s do ...get apis result, res=%s", SRV_SUPERVISOR_NAME, + // json_object_to_json_string(j_response)); + + if (json_object_object_get_ex(j_response, "response", &j_config) && + json_object_object_get_ex(j_config, "apis", &j_apis)) { + daemon->apis = j_apis; + } + } + daemons->daemons[daemons->count] = daemon; + daemons->count++; +} + +int getDaemons(DAEMONS_T** daemons) +{ + int rc; + json_object *j_response, *j_daemons = NULL; + + *daemons = calloc(sizeof(DAEMONS_T), 1); + + if ((rc = afb_service_call_sync(SRV_SUPERVISOR_NAME, "discover", NULL, + &j_response)) + < 0) { + return rc; + } + + if ((rc = afb_service_call_sync(SRV_SUPERVISOR_NAME, "list", NULL, + &j_response)) + < 0) { + return rc; + } + + AFB_DEBUG("%s list result, res=%s", SRV_SUPERVISOR_NAME, + json_object_to_json_string(j_response)); + + if (json_object_object_get_ex(j_response, "response", &j_daemons)) { + wrap_json_object_for_all(j_daemons, &decode_daemons_cb, *daemons); + } + + return 0; +} + +int trace_exchange(DAEMON_T* svr, DAEMON_T* cli) +{ + int rc; + json_object *j_response, *j_query; + + if (svr == NULL || cli == NULL) { + return -1; + } + + wrap_json_pack(&j_query, "{s:i, s:{s:s}}", "pid", svr->pid, "add", + "request", "common"); + if ((rc = afb_service_call_sync(SRV_SUPERVISOR_NAME, "trace", j_query, + &j_response)) + < 0) { + AFB_ERROR("ERROR trace %d result: %s", svr->pid, + json_object_to_json_string(j_response)); + return rc; + } + + wrap_json_pack(&j_query, "{s:i}", "pid", cli->pid); + if ((rc = afb_service_call_sync(SRV_SUPERVISOR_NAME, "trace", j_query, + &j_response)) + < 0) { + AFB_ERROR("ERROR trace %d result: %s", cli->pid, + json_object_to_json_string(j_response)); + return rc; + } + + return 0; +} + +void supervisor_service_init(void) {} diff --git a/xds-service/supervisor-service.h b/xds-service/supervisor-service.h new file mode 100644 index 0000000..84fe36c --- /dev/null +++ b/xds-service/supervisor-service.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2018 "IoT.bzh" + * Author "Sebastien Douheret" <sebastien@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. + */ +#pragma once + +#include <stdbool.h> +#include "wrap-json.h" + +// FIXME Use chained list instead of static array +#define MAX_CLIENTS 32 +#define MAX_SERVERS 32 +#define MAX_DAEMONS 1024 + + +typedef struct daemon +{ + int pid; + const char* name; + bool isServer; + bool isClient; + //char *ws_clients[MAX_CLIENTS]; + //char *ws_servers[MAX_SERVERS]; + json_object *ws_clients; + json_object *ws_servers; + json_object *config; + json_object *apis; +} DAEMON_T; + +typedef struct daemons_result_ +{ + int count; + DAEMON_T *daemons[MAX_DAEMONS]; +} DAEMONS_T; + + +extern int getDaemons(DAEMONS_T **daemons); +extern int trace_exchange(DAEMON_T *svr, DAEMON_T *cli); +extern void supervisor_service_init(void); diff --git a/xds-service/xds-service-api.c b/xds-service/xds-service-api.c new file mode 100644 index 0000000..08e6a59 --- /dev/null +++ b/xds-service/xds-service-api.c @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2018 "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. + */ +#define _GNU_SOURCE +#include "xds-service-api.h" +#include "supervisor-service.h" +#include "xds-service-apidef.h" +#include <stdio.h> +#include <string.h> +#include <time.h> + +#define SRV_SUPERVISOR_NAME "supervisor" +#define SRV_HARVESTER_NAME "harvester" + +typedef struct metric_t { + char* name; + json_object* data; + struct timespec timestamp; +} METRIC_T; + +void list(struct afb_req request) +{ + json_object *result, *item = NULL; + DAEMONS_T* daemons = NULL; + + getDaemons(&daemons); + if (daemons == NULL) { + afb_req_fail(request, "failed", ""); + return; + } + + result = json_object_new_array(); + + for (int i = 0; i < daemons->count; i++) { + wrap_json_pack(&item, "{si ss sb sb so so so}", + "pid", daemons->daemons[i]->pid, + "name", daemons->daemons[i]->name, + "isServer", daemons->daemons[i]->isServer, + "isClient", daemons->daemons[i]->isClient, + "ws_servers", daemons->daemons[i]->ws_servers, + "ws_clients", daemons->daemons[i]->ws_clients, + "apis", daemons->daemons[i]->apis); + //, "config", daemons->daemons[i]->config); + json_object_array_add(result, item); + } + afb_req_success(request, result, NULL); +} + +void trace(struct afb_req request) +{ + int rc; + json_object* req_args = afb_req_json(request); + json_object* result = NULL; + DAEMONS_T* daemons = NULL; + const char* ws_name; + const char* wsn; + + if (wrap_json_unpack(req_args, "{s:?s}", "ws", &ws_name)) { + afb_req_fail(request, "Failed", "Error processing arguments."); + return; + } + AFB_INFO("Trace ws: %s", ws_name); + + getDaemons(&daemons); + if (daemons == NULL || daemons->count <= 0) { + afb_req_fail(request, "failed", "No daemon found"); + } + + // search server and client pid + DAEMON_T *pid_s = NULL, *pid_c = NULL; + for (int i = 0; i < daemons->count; i++) { + AFB_DEBUG("_DEBUG_ svr %s", + json_object_to_json_string(daemons->daemons[i]->ws_servers)); + AFB_DEBUG("_DEBUG_ cli %s", + json_object_to_json_string(daemons->daemons[i]->ws_clients)); + + json_object* ws_servers = daemons->daemons[i]->ws_servers; + for (int j = 0; j < json_object_array_length(ws_servers); j++) { + + wsn = json_object_get_string(json_object_array_get_idx(ws_servers, j++)); + if (wsn && strstr(wsn, ws_name) != NULL) { + pid_s = daemons->daemons[i]; + break; + } + } + + json_object* ws_clients = daemons->daemons[i]->ws_clients; + for (int j = 0; j < json_object_array_length(ws_clients); j++) { + wsn = json_object_get_string(json_object_array_get_idx(ws_clients, j++)); + if (wsn && strstr(wsn, ws_name) != NULL) { + pid_c = daemons->daemons[i]; + break; + } + } + + if (pid_s != NULL && pid_c != NULL) { + if ((rc = trace_exchange(pid_s, pid_c)) < 0) { + afb_req_fail_f(request, "failed", "Trace error %d", rc); + } + break; + } + } + + if (pid_s == NULL || pid_c == NULL) { + afb_req_fail(request, "failed", "Cannot determine Server or Client"); + return; + } + + afb_req_success_f(request, result, "Tracing Server pid=%d <-> Client pid=%d", + pid_s->pid, pid_c->pid); +} + +static int harvester_post_data(METRIC_T* metric) +{ + + int rc; + json_object *j_res, *j_query; + + if (!metric->timestamp.tv_sec && !metric->timestamp.tv_nsec) { + clock_gettime(CLOCK_MONOTONIC, &metric->timestamp); + } + + rc = wrap_json_pack(&j_query, "{s:s s:i s:{ s:s s:o s:i } }", "host", + "localhost", "port", 8086, "metric", "name", metric->name, + "value", metric->data, "timestamp", + metric->timestamp.tv_sec); + if (rc < 0) { + AFB_ERROR("Error packing metric, rc=%d", rc); + return rc; + } + + AFB_DEBUG("%s write: %s", SRV_HARVESTER_NAME, + json_object_to_json_string(j_query)); + + rc = afb_service_call_sync(SRV_HARVESTER_NAME, "write", j_query, &j_res); + if (rc < 0) { + AFB_ERROR("Error %s write : rc=%d, j_res=%s", SRV_HARVESTER_NAME, rc, + json_object_to_json_string(j_res)); + return rc; + } + return 0; +} + +void xds_event_cb(const char* evtname, json_object* j_event) +{ + int rc; + METRIC_T metric; + const char* type = NULL; + struct json_object* request = NULL; + + AFB_NOTICE("RECV Event %s : %s", evtname, + json_object_to_json_string(j_event)); + + if (strcmp(evtname, "supervisor/trace") != 0) { + return; + } + + if ((rc = wrap_json_unpack(j_event, "{s:?s}", "type", &type)) < 0) { + AFB_ERROR("Cannot decode event type"); + return; + } + + if (strcmp(type, "request") == 0) { + + if (!json_object_object_get_ex(j_event, "request", &request)) { + AFB_ERROR("Cannot decode event request"); + return; + } + metric.name = "trace"; + metric.data = request; + + rc = harvester_post_data(&metric); + if (rc < 0) { + AFB_ERROR("ERROR harvester_post_data: rc %d", rc); + } + } +} + +void auth(struct afb_req request) +{ + afb_req_session_set_LOA(request, 1); + afb_req_success(request, NULL, NULL); +} + +int init() +{ + +#if 0 // DEBUG + DAEMONS_T *daemons = NULL; + getDaemons(&daemons); + + if (daemons) { + if (daemons->count) { + AFB_DEBUG("_DEBUG_ daemons->count %d", daemons->count); + for (int i = 0; i < daemons->count; i++) { + AFB_DEBUG("pid %d : isServer %d, isClient %d, %s %s", + daemons->daemons[i]->pid, daemons->daemons[i]->isServer, + daemons->daemons[i]->isClient, + json_object_to_json_string(daemons->daemons[i]->ws_servers), + json_object_to_json_string(daemons->daemons[i]->ws_clients)); + } + free(daemons); + } else { + AFB_DEBUG("_DEBUG_ no dameons detected !"); + } + } +#endif + + supervisor_service_init(); + + return 0; +} diff --git a/xds-service/xds-service-api.h b/xds-service/xds-service-api.h new file mode 100644 index 0000000..8a95f77 --- /dev/null +++ b/xds-service/xds-service-api.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2018 "IoT.bzh" + * Author "Sebastien Douheret" <sebastien@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. + */ +#pragma once + +#define AFB_BINDING_VERSION 2 +#include <afb/afb-binding.h> +#include "wrap-json.h" + +extern void xds_event_cb(const char *evtname, json_object *j_event); +extern int init(); diff --git a/xds-service/xds-service-apidef.h b/xds-service/xds-service-apidef.h new file mode 100644 index 0000000..5d53d89 --- /dev/null +++ b/xds-service/xds-service-apidef.h @@ -0,0 +1,85 @@ + +static const char _afb_description_v2_xds_service[] = + "{\"openapi\":\"3.0.0\",\"$schema\":\"http://iot.bzh/download/openapi/sch" + "ema-3.0/default-schema.json\",\"info\":{\"description\":\"TBD - TODO\",\"" + "title\":\"xds-service\",\"version\":\"4.0\",\"x-binding-c-generator\":{\"" + "api\":\"xds-service\",\"version\":2,\"prefix\":\"\",\"postfix\":\"\",\"s" + "tart\":null,\"onevent\":\"xds_event_cb\",\"init\":\"init\",\"scope\":\"\"" + ",\"private\":false}},\"servers\":[{}],\"components\":{\"schemas\":{\"afb" + "-reply\":{\"$ref\":\"#/components/schemas/afb-reply-v2\"},\"afb-event\":" + "{\"$ref\":\"#/components/schemas/afb-event-v2\"},\"afb-reply-v2\":{\"tit" + "le\":\"Generic response.\",\"type\":\"object\",\"required\":[\"jtype\",\"" + "request\"],\"properties\":{\"jtype\":{\"type\":\"string\",\"const\":\"af" + "b-reply\"},\"request\":{\"type\":\"object\",\"required\":[\"status\"],\"" + "properties\":{\"status\":{\"type\":\"string\"},\"info\":{\"type\":\"stri" + "ng\"},\"token\":{\"type\":\"string\"},\"uuid\":{\"type\":\"string\"},\"r" + "eqid\":{\"type\":\"string\"}}},\"response\":{\"type\":\"object\"}}},\"af" + "b-event-v2\":{\"type\":\"object\",\"required\":[\"jtype\",\"event\"],\"p" + "roperties\":{\"jtype\":{\"type\":\"string\",\"const\":\"afb-event\"},\"e" + "vent\":{\"type\":\"string\"},\"data\":{\"type\":\"object\"}}}},\"x-permi" + "ssions\":{\"list\":{\"permission\":\"urn:AGL:permission::platform:can:li" + "st \"},\"trace\":{\"permission\":\"urn:AGL:permission::platform:can:trac" + "e \"}},\"responses\":{\"200\":{\"description\":\"A complex object array " + "response\",\"content\":{\"application/json\":{\"schema\":{\"$ref\":\"#/c" + "omponents/schemas/afb-reply\"}}}}}},\"paths\":{\"/auth\":{\"description\"" + ":\"Authenticate session to raise Level Of Assurance of the session\",\"g" + "et\":{\"x-permissions\":{\"$ref\":\"#/components/x-permissions/list\"},\"" + "responses\":{\"200\":{\"$ref\":\"#/components/responses/200\"}}}},\"/lis" + "t\":{\"description\":\"list \",\"get\":{\"x-permissions\":{\"LOA\":1},\"" + "parameters\":[],\"responses\":{\"200\":{\"$ref\":\"#/components/response" + "s/200\"}}}},\"/trace\":{\"description\":\"trace \",\"get\":{\"x-permissi" + "ons\":{\"LOA\":1},\"parameters\":[{\"in\":\"query\",\"name\":\"ws\",\"re" + "quired\":true,\"schema\":{\"type\":\"string\"}}],\"responses\":{\"200\":" + "{\"$ref\":\"#/components/responses/200\"}}}}}}" +; + +static const struct afb_auth _afb_auths_v2_xds_service[] = { + { .type = afb_auth_Permission, .text = "urn:AGL:permission::platform:can:list " } +}; + + void auth(struct afb_req req); + void list(struct afb_req req); + void trace(struct afb_req req); + +static const struct afb_verb_v2 _afb_verbs_v2_xds_service[] = { + { + .verb = "auth", + .callback = auth, + .auth = &_afb_auths_v2_xds_service[0], + .info = "Authenticate session to raise Level Of Assurance of the session", + .session = AFB_SESSION_NONE_V2 + }, + { + .verb = "list", + .callback = list, + .auth = NULL, + .info = "list ", + .session = AFB_SESSION_LOA_1_V2 + }, + { + .verb = "trace", + .callback = trace, + .auth = NULL, + .info = "trace ", + .session = AFB_SESSION_LOA_1_V2 + }, + { + .verb = NULL, + .callback = NULL, + .auth = NULL, + .info = NULL, + .session = 0 + } +}; + +const struct afb_binding_v2 afbBindingV2 = { + .api = "xds-service", + .specification = _afb_description_v2_xds_service, + .info = "TBD - TODO", + .verbs = _afb_verbs_v2_xds_service, + .preinit = NULL, + .init = init, + .onevent = xds_event_cb, + .noconcurrency = 0 +}; + diff --git a/xds-service/xds-service-apidef.json b/xds-service/xds-service-apidef.json new file mode 100644 index 0000000..76e35b7 --- /dev/null +++ b/xds-service/xds-service-apidef.json @@ -0,0 +1,152 @@ +{ + "openapi": "3.0.0", + "$schema": "http://iot.bzh/download/openapi/schema-3.0/default-schema.json", + "info": { + "description": "TBD - TODO", + "title": "xds-service", + "version": "4.0", + "x-binding-c-generator": { + "api": "xds-service", + "version": 2, + "prefix": "", + "postfix": "", + "start": null, + "onevent": "xds_event_cb", + "init": "init", + "scope": "", + "private": false + } + }, + "servers": [{}], + "components": { + "schemas": { + "afb-reply": { + "$ref": "#/components/schemas/afb-reply-v2" + }, + "afb-event": { + "$ref": "#/components/schemas/afb-event-v2" + }, + "afb-reply-v2": { + "title": "Generic response.", + "type": "object", + "required": ["jtype", "request"], + "properties": { + "jtype": { + "type": "string", + "const": "afb-reply" + }, + "request": { + "type": "object", + "required": ["status"], + "properties": { + "status": { + "type": "string" + }, + "info": { + "type": "string" + }, + "token": { + "type": "string" + }, + "uuid": { + "type": "string" + }, + "reqid": { + "type": "string" + } + } + }, + "response": { + "type": "object" + } + } + }, + "afb-event-v2": { + "type": "object", + "required": ["jtype", "event"], + "properties": { + "jtype": { + "type": "string", + "const": "afb-event" + }, + "event": { + "type": "string" + }, + "data": { + "type": "object" + } + } + } + }, + "x-permissions": { + "list": { + "permission": "urn:AGL:permission::platform:can:list " + }, + "trace": { + "permission": "urn:AGL:permission::platform:can:trace " + } + }, + "responses": { + "200": { + "description": "A complex object array response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/afb-reply" + } + } + } + } + } + }, + "paths": { + "/auth": { + "description": "Authenticate session to raise Level Of Assurance of the session", + "get": { + "x-permissions": { + "$ref": "#/components/x-permissions/list" + }, + "responses": { + "200": { + "$ref": "#/components/responses/200" + } + } + } + }, + "/list": { + "description": "list ", + "get": { + "x-permissions": { + "LOA": 1 + }, + "parameters": [], + "responses": { + "200": { + "$ref": "#/components/responses/200" + } + } + } + }, + "/trace": { + "description": "trace ", + "get": { + "x-permissions": { + "LOA": 1 + }, + "parameters": [{ + "in": "query", + "name": "ws", + "required": true, + "schema": { + "type": "string" + } + }], + "responses": { + "200": { + "$ref": "#/components/responses/200" + } + } + } + } + } +} |