From f6bc48698587758fb764bae66302002fe148e978 Mon Sep 17 00:00:00 2001 From: José Bollo Date: Wed, 7 Jun 2017 18:40:00 +0200 Subject: Refactor of the documentation --- bindings/CMakeLists.txt | 3 +- bindings/tutorial/CMakeLists.txt | 32 + bindings/tutorial/export.map | 1 + bindings/tutorial/tuto-1.c | 19 + bindings/tutorial/tuto-2.c | 100 +++ doc/FAQ.md | 5 - doc/afb-application-writing.md | 301 ------- doc/afb-bindings-overview.md | 180 ---- doc/afb-bindings-writing.md | 1407 -------------------------------- doc/afb-daemon-vocabulary.md | 98 --- doc/afb-events-guide.md | 475 ----------- doc/afb-overview.md | 315 ------- doc/afb-tests-overview.md | 89 -- doc/index.md | 1 - doc/pictures/AFB_for_services.svg | 238 ------ doc/pictures/AFB_overview.svg | 140 ---- doc/pictures/signaling-basis.svg | 145 ---- doc/pictures/tic-tac-toe.svg | 289 ------- doc/pictures/triskel_iot_bzh.svg | 110 --- docs/FAQ.md | 5 + docs/afb-application-writing.md | 301 +++++++ docs/afb-binding-references.md | 672 +++++++++++++++ docs/afb-binding-writing.md | 465 +++++++++++ docs/afb-daemon-options.md | 200 +++++ docs/afb-daemon-vocabulary.md | 98 +++ docs/afb-desktop-package.md | 40 + docs/afb-events-guide.md | 371 +++++++++ docs/afb-migration-v1-to-v2.md | 611 ++++++++++++++ docs/afb-overview.md | 98 +++ docs/index.md | 1 + docs/pictures/AFB_for_services.svg | 238 ++++++ docs/pictures/basis.odg | Bin 0 -> 15170 bytes docs/pictures/basis.svg | 356 ++++++++ docs/pictures/interconnection.odg | Bin 0 -> 20244 bytes docs/pictures/interconnection.svg | 854 +++++++++++++++++++ docs/pictures/signaling-basis.svg | 145 ++++ docs/pictures/tic-tac-toe.svg | 289 +++++++ docs/pictures/triskel_iot_bzh.svg | 110 +++ mkdocs.yml | 14 +- old-docs/FAQ.md | 5 + old-docs/afb-application-writing.md | 301 +++++++ old-docs/afb-bindings-overview.md | 180 ++++ old-docs/afb-bindings-writing.md | 1407 ++++++++++++++++++++++++++++++++ old-docs/afb-daemon-vocabulary.md | 98 +++ old-docs/afb-events-guide.md | 475 +++++++++++ old-docs/afb-overview.md | 315 +++++++ old-docs/afb-tests-overview.md | 89 ++ old-docs/index.md | 1 + old-docs/pictures/AFB_for_services.svg | 238 ++++++ old-docs/pictures/AFB_overview.svg | 140 ++++ old-docs/pictures/signaling-basis.svg | 145 ++++ old-docs/pictures/tic-tac-toe.svg | 289 +++++++ old-docs/pictures/triskel_iot_bzh.svg | 110 +++ 53 files changed, 8810 insertions(+), 3799 deletions(-) create mode 100644 bindings/tutorial/CMakeLists.txt create mode 100644 bindings/tutorial/export.map create mode 100644 bindings/tutorial/tuto-1.c create mode 100644 bindings/tutorial/tuto-2.c delete mode 100644 doc/FAQ.md delete mode 100644 doc/afb-application-writing.md delete mode 100644 doc/afb-bindings-overview.md delete mode 100644 doc/afb-bindings-writing.md delete mode 100644 doc/afb-daemon-vocabulary.md delete mode 100644 doc/afb-events-guide.md delete mode 100644 doc/afb-overview.md delete mode 100644 doc/afb-tests-overview.md delete mode 120000 doc/index.md delete mode 100644 doc/pictures/AFB_for_services.svg delete mode 100644 doc/pictures/AFB_overview.svg delete mode 100644 doc/pictures/signaling-basis.svg delete mode 100644 doc/pictures/tic-tac-toe.svg delete mode 100644 doc/pictures/triskel_iot_bzh.svg create mode 100644 docs/FAQ.md create mode 100644 docs/afb-application-writing.md create mode 100644 docs/afb-binding-references.md create mode 100644 docs/afb-binding-writing.md create mode 100644 docs/afb-daemon-options.md create mode 100644 docs/afb-daemon-vocabulary.md create mode 100644 docs/afb-desktop-package.md create mode 100644 docs/afb-events-guide.md create mode 100644 docs/afb-migration-v1-to-v2.md create mode 100644 docs/afb-overview.md create mode 120000 docs/index.md create mode 100644 docs/pictures/AFB_for_services.svg create mode 100644 docs/pictures/basis.odg create mode 100644 docs/pictures/basis.svg create mode 100644 docs/pictures/interconnection.odg create mode 100644 docs/pictures/interconnection.svg create mode 100644 docs/pictures/signaling-basis.svg create mode 100644 docs/pictures/tic-tac-toe.svg create mode 100644 docs/pictures/triskel_iot_bzh.svg create mode 100644 old-docs/FAQ.md create mode 100644 old-docs/afb-application-writing.md create mode 100644 old-docs/afb-bindings-overview.md create mode 100644 old-docs/afb-bindings-writing.md create mode 100644 old-docs/afb-daemon-vocabulary.md create mode 100644 old-docs/afb-events-guide.md create mode 100644 old-docs/afb-overview.md create mode 100644 old-docs/afb-tests-overview.md create mode 120000 old-docs/index.md create mode 100644 old-docs/pictures/AFB_for_services.svg create mode 100644 old-docs/pictures/AFB_overview.svg create mode 100644 old-docs/pictures/signaling-basis.svg create mode 100644 old-docs/pictures/tic-tac-toe.svg create mode 100644 old-docs/pictures/triskel_iot_bzh.svg diff --git a/bindings/CMakeLists.txt b/bindings/CMakeLists.txt index 490e29aa..1a1e9016 100644 --- a/bindings/CMakeLists.txt +++ b/bindings/CMakeLists.txt @@ -16,5 +16,6 @@ # limitations under the License. ########################################################################### -ADD_SUBDIRECTORY(samples) ADD_SUBDIRECTORY(intrinsics) +ADD_SUBDIRECTORY(samples) +ADD_SUBDIRECTORY(tutorial) diff --git a/bindings/tutorial/CMakeLists.txt b/bindings/tutorial/CMakeLists.txt new file mode 100644 index 00000000..844e2dfb --- /dev/null +++ b/bindings/tutorial/CMakeLists.txt @@ -0,0 +1,32 @@ +########################################################################### +# Copyright 2015, 2016, 2017 IoT.bzh +# +# author: José Bollo +# +# 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_DIRECTORIES(${include_dirs}) + +MACRO(tuto num) + ADD_LIBRARY(tuto-${num} MODULE tuto-${num}.c) + SET_TARGET_PROPERTIES(tuto-${num} PROPERTIES + PREFIX "" + LINK_FLAGS "-Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/export.map" + ) + TARGET_LINK_LIBRARIES(tuto-${num} ${link_libraries}) +ENDMACRO(tuto) + +tuto(1) +tuto(2) diff --git a/bindings/tutorial/export.map b/bindings/tutorial/export.map new file mode 100644 index 00000000..ee2f4133 --- /dev/null +++ b/bindings/tutorial/export.map @@ -0,0 +1 @@ +{ global: afbBindingV*; local: *; }; diff --git a/bindings/tutorial/tuto-1.c b/bindings/tutorial/tuto-1.c new file mode 100644 index 00000000..433a4ebb --- /dev/null +++ b/bindings/tutorial/tuto-1.c @@ -0,0 +1,19 @@ +#define AFB_BINDING_VERSION 2 +#include + +void hello(afb_req req) +{ + AFB_REQ_DEBUG(req, "hello world"); + afb_req_success(req, NULL, "hello world"); +} + +const afb_verb_v2 verbs[] = { + { .verb="hello", .callback=hello }, + { .verb=NULL } +}; + +const afb_binding_v2 afbBindingV2 = { + .api = "tuto-1", + .verbs = verbs +}; + diff --git a/bindings/tutorial/tuto-2.c b/bindings/tutorial/tuto-2.c new file mode 100644 index 00000000..dc2d55ab --- /dev/null +++ b/bindings/tutorial/tuto-2.c @@ -0,0 +1,100 @@ +#include +#include + +#define AFB_BINDING_VERSION 2 +#include + +afb_event event_login, event_logout; + +void login(afb_req req) +{ + json_object *args, *user, *passwd; + char *usr; + + args = afb_req_json(req); + if (!json_object_object_get_ex(args, "user", &user) + || !json_object_object_get_ex(args, "password", &passwd)) { + AFB_REQ_ERROR(req, "login, bad request: %s", json_object_get_string(args)); + afb_req_fail(req, "bad-request", NULL); + } else if (afb_req_context_get(req)) { + AFB_REQ_ERROR(req, "login, bad state, logout first"); + afb_req_fail(req, "bad-state", NULL); + } else if (strcmp(json_object_get_string(passwd), "please")) { + AFB_REQ_ERROR(req, "login, unauthorized: %s", json_object_get_string(args)); + afb_req_fail(req, "unauthorized", NULL); + } else { + usr = strdup(json_object_get_string(user)); + AFB_REQ_NOTICE(req, "login user: %s", usr); + afb_req_session_set_LOA(req, 1); + afb_req_context_set(req, usr, free); + afb_req_success(req, NULL, NULL); + afb_event_push(event_login, json_object_new_string(usr)); + } +} + +void action(afb_req req) +{ + json_object *args, *val; + char *usr; + + args = afb_req_json(req); + usr = afb_req_context_get(req); + AFB_REQ_NOTICE(req, "action for user %s: %s", usr, json_object_get_string(args)); + if (json_object_object_get_ex(args, "subscribe", &val)) { + if (json_object_get_boolean(val)) { + AFB_REQ_NOTICE(req, "user %s subscribes to events", usr); + afb_req_subscribe(req, event_login); + afb_req_subscribe(req, event_logout); + } else { + AFB_REQ_NOTICE(req, "user %s unsubscribes to events", usr); + afb_req_unsubscribe(req, event_login); + afb_req_unsubscribe(req, event_logout); + } + } + afb_req_success(req, json_object_get(args), NULL); +} + +void logout(afb_req req) +{ + char *usr; + + usr = afb_req_context_get(req); + AFB_REQ_NOTICE(req, "login user %s out", usr); + afb_event_push(event_logout, json_object_new_string(usr)); + afb_req_session_set_LOA(req, 0); + afb_req_context_clear(req); + afb_req_success(req, NULL, NULL); +} + +int preinit() +{ + AFB_NOTICE("preinit"); + return 0; +} + +int init() +{ + AFB_NOTICE("init"); + event_login = afb_daemon_make_event("login"); + event_logout = afb_daemon_make_event("logout"); + if (afb_event_is_valid(event_login) && afb_event_is_valid(event_logout)) + return 0; + AFB_ERROR("Can't create events"); + return -1; +} + +const afb_verb_v2 verbs[] = { + { .verb="login", .callback=login }, + { .verb="action", .callback=action, .session=AFB_SESSION_LOA_1 }, + { .verb="logout", .callback=logout, .session=AFB_SESSION_LOA_1 }, + { .verb=NULL } +}; + +const afb_binding_v2 afbBindingV2 = { + .api = "tuto-2", + .specification = NULL, + .verbs = verbs, + .preinit = preinit, + .init = init, + .noconcurrency = 0 +}; \ No newline at end of file diff --git a/doc/FAQ.md b/doc/FAQ.md deleted file mode 100644 index 5063ed74..00000000 --- a/doc/FAQ.md +++ /dev/null @@ -1,5 +0,0 @@ -Frequently Asked Question about AFB-DAEMON -========================================== - - - diff --git a/doc/afb-application-writing.md b/doc/afb-application-writing.md deleted file mode 100644 index cc513b48..00000000 --- a/doc/afb-application-writing.md +++ /dev/null @@ -1,301 +0,0 @@ -How to write an application on top of AGL FRAMEWORK -==================================================== - -Programming Languages for Applications ------------------------------------------ - -### Writing an HTML5 application - -Developers of HTML5 applications (client side) can easily create -applications for AGL framework using their preferred -HTML5 framework. - -Developers may also take advantage of powerful server side bindings to improve -application behavior. Server side bindings return an application/json mine-type -and can be accessed though either HTTP or Websockets. - -In a near future, JSON-RPC protocol should be added to complete the current -x-afb-json1 protocol. - -Two examples of HTML5 applications are given: - -- [afb-client](https://gerrit.automotivelinux.org/gerrit/gitweb?p=src/app-framework-demo.git;a=tree;f=afb-client) a simple "hello world" application template - -- [afm-client](https://gerrit.automotivelinux.org/gerrit/gitweb?p=src/app-framework-demo.git;a=tree;f=afm-client) a simple "Home screen" application template - -### Writing a Qt application - -Writing Qt applications is also supported. Qt offers standard API to send -request through HTTP or WebSockets. - -It is also possible to write QML applications. A sample QML application -[token-websock] is available: - -- [token-websock](https://gerrit.automotivelinux.org/gerrit/gitweb?p=src/app-framework-binder.git;a=blob;f=test/token-websock.qml) -a simple "hello world" application in QML - -### Writing a "C" application - -C applications can use afb-daemon binder through a websocket connection. - -The library **libafbwsc** is provided for C clients that need -to connect with an afb-daemon binder. - -The program **afb-client-demo** is the C example that use -**libafbwsc** library. -Source code is available here -[src/afb-client-demo.c](https://gerrit.automotivelinux.org/gerrit/gitweb?p=src/app-framework-binder.git;a=blob;f=src/afb-client-demo.c). - -Current implementation relies on libsystemd and file descriptors. -This model might be review in the future to support secure sockets -and get rid of libsystemd dependency. - -Handling sessions within applications -------------------------------------- - -Applications should understand sessions and token management when interacting -with afb-daemon binder. - -Applications communicate with their private binder(afb-daemon) using -a network connection or potentially any other connection channel. While the -current version does not yet implement Unix socket, this feature might be added -in the near future. Developers need to be warn that HTTP protocol is a none -connected protocol and that using HTTP socket connection to authenticate -clients is not supported. - -For this reason, the binder should authenticate the application -by using a shared secret. The secret is named "token" and the identification -of client is named "session". - -The examples **token-websock.qml** and **afb-client** are demonstrating -how authentication and sessions are managed. - -### Handling sessions - -Bindings and other binder features need to keep track of client -instances. This is especially important for bindings running as services -as they may typically have to keep each client's data separated. - -For HTML5 applications, the web runtime handles the cookie of session -that the binder afb-daemon automatically sets. - -Session identifier can be set using the parameter **uuid** or **x-afb-uuid** in -URI requests. Within current version of the framework session UUID is supported -by both HTTP requests and websocket negotiation. - -### Exchanging tokens - -At application start, AGL framework communicates a shared secret to both binder -and client application. This initial secret is called the "**initial token**". - -For each of its client application, the binder manages a current active -token for session management. This authentication token can be use to restrict -the access to some binding's methods. - -The token must be included in URI request on HTTP or during websockets -connection using parameter **token** or **x-afb-token**. - -To ensure security, tokens must be refreshed periodically. - -### Example of session management - -In following examples, we suppose that **afb-daemon** is launched with something -equivalent to: - - $ afb-daemon --port=1234 --token=123456 [...] - -making the expectation that **AuthLogin** binding is requested as default. - -#### Using curl - -First, connects with the initial token, 123456: - - $ curl http://localhost:1234/api/auth/connect?token=123456 - { - "jtype": "afb-reply", - "request": { - "status": "success", - "token": "0aef6841-2ddd-436d-b961-ae78da3b5c5f", - "uuid": "850c4594-1be1-4e9b-9fcc-38cc3e6ff015" - }, - "response": {"token": "A New Token and Session Context Was Created"} - } - -It returns an answer containing session UUID, 850c4594-1be1-4e9b-9fcc-38cc3e6ff015, -and a refreshed token, 850c4594-1be1-4e9b-9fcc-38cc3e6ff015. - -Check if session and token is valid: - - $ curl http://localhost:1234/api/auth/check?token=0aef6841-2ddd-436d-b961-ae78da3b5c5f\&uuid=850c4594-1be1-4e9b-9fcc-38cc3e6ff015 - { - "jtype": "afb-reply", - "request": {"status":"success"}, - "response": {"isvalid":true} - } - -Refresh the token: - - $ curl http://localhost:1234/api/auth/refresh?token=0aef6841-2ddd-436d-b961-ae78da3b5c5f\&uuid=850c4594-1be1-4e9b-9fcc-38cc3e6ff015 - { - "jtype": "afb-reply", - "request": { - "status":"success", - "token":"b8ec3ec3-6ffe-448c-9a6c-efda69ad7bd9" - }, - "response": {"token":"Token was refreshed"} - } - -Close the session: - - curl http://localhost:1234/api/auth/logout?token=b8ec3ec3-6ffe-448c-9a6c-efda69ad7bd9\&uuid=850c4594-1be1-4e9b-9fcc-38cc3e6ff015 - { - "jtype": "afb-reply", - "request": {"status": "success"}, - "response": {"info":"Token and all resources are released"} - } - -Checking on closed session for uuid should be refused: - - curl http://localhost:1234/api/auth/check?token=b8ec3ec3-6ffe-448c-9a6c-efda69ad7bd9\&uuid=850c4594-1be1-4e9b-9fcc-38cc3e6ff015 - { - "jtype": "afb-reply", - "request": { - "status": "failed", - "info": "invalid token's identity" - } - } - -#### Using afb-client-demo - -> The program is packaged within AGL in the rpm **libafbwsc-dev** - -Here is an example of exchange using **afb-client-demo**: - - $ afb-client-demo ws://localhost:1234/api?token=123456 - auth connect - ON-REPLY 1:auth/connect: {"jtype":"afb-reply","request":{"status":"success", - "token":"63f71a29-8b52-4f9b-829f-b3028ba46b68","uuid":"5fcc3f3d-4b84-4fc7-ba66-2d8bd34ae7d1"}, - "response":{"token":"A New Token and Session Context Was Created"}} - auth check - ON-REPLY 2:auth/check: {"jtype":"afb-reply","request":{"status":"success"},"response":{"isvalid":true}} - auth refresh - ON-REPLY 4:auth/refresh: {"jtype":"afb-reply","request":{"status":"success", - "token":"8b8ba8f4-1b0c-48fa-962d-4a00a8c9157e"},"response":{"token":"Token was refreshed"}} - auth check - ON-REPLY 5:auth/check: {"jtype":"afb-reply","request":{"status":"success"},"response":{"isvalid":true}} - auth refresh - ON-REPLY 6:auth/refresh: {"jtype":"afb-reply","request":{"status":"success", - "token":"e83b36f8-d945-463d-b983-5d8ed73ba529"},"response":{"token":"Token was refreshed"}} - -After closing connection, reconnect as here after: - - $ afb-client-demo ws://localhost:1234/api?token=e83b36f8-d945-463d-b983-5d8ed73ba529\&uuid=5fcc3f3d-4b84-4fc7-ba66-2d8bd34ae7d1 auth check - ON-REPLY 1:auth/check: {"jtype":"afb-reply","request":{"status":"success"},"response":{"isvalid":true}} - -Same connection check using **curl**: - - $ curl http://localhost:1234/api/auth/check?token=e83b36f8-d945-463d-b983-5d8ed73ba529\&uuid=5fcc3f3d-4b84-4fc7-ba66-2d8bd34ae7d1 - {"jtype":"afb-reply","request":{"status":"success"},"response":{"isvalid":true}} - -Format of replies ------------------ - -Replies use javascript object returned as serialized JSON. - -This object contains at least 2 mandatory fields of name **jtype** and -**request** and one optional field of name **response**. - -### Template - -This is a template of replies: - -```json -{ - "jtype": "afb-reply", - "request": { - "status": "success", - "info": "informationnal text", - "token": "e83b36f8-d945-463d-b983-5d8ed73ba52", - "uuid": "5fcc3f3d-4b84-4fc7-ba66-2d8bd34ae7d1", - "reqid": "application-generated-id-23456" - }, - "response": ....any response object.... -} -``` - -### Field jtype - -The field **jtype** must have a value of type string equal to **"afb-reply"**. - -### Field request - -The field **request** must have a value of type object. This request object -has at least one field named **status** and four optional fields named -**info**, **token**, **uuid**, **reqid**. - -#### Subfield request.status - -**status** must have a value of type string. This string is equal to **"success"** -only in case of success. - -#### Subfield request.info - -**info** is of type string and represent optional information added to the reply. - -#### Subfield request.token - -**token** is of type string. It is sent either at session creation -or when the token is refreshed. - -#### Subfield request.uuid - -**uuid** is of type string. It is sent at session creation. - -#### Subfield request.reqid - -**reqid** is of type string. It is sent in response to HTTP requests -that added a parameter of name **reqid** or **x-afb-reqid** at request time. -Value returns in the reply has the exact same value as the one received in the -request. - -### Field response - -This field response optionally contains an object returned when request -succeeded. - -Format of events ----------------- - -Events are javascript object serialized as JSON. - -This object contains at least 2 mandatory fields of name **jtype** and **event** -and one optional field of name **data**. - -### Template - -Here is a template of event: - -```json -{ - "jtype": "afb-event", - "event": "sample_api_name/sample_event_name", - "data": ...any event data... -} -``` - -### Field jtype - -The field **jtype** must have a value of type string equal to **"afb-event"**. - -### Field event - -The field **event** carries the event's name. - -The name of the event is made of two parts separated by a slash: -the name of the name of the API that generated the event -and the name of event within the API. - -### Field data - -This field data if present holds the data carried by the event. - diff --git a/doc/afb-bindings-overview.md b/doc/afb-bindings-overview.md deleted file mode 100644 index c79e17f4..00000000 --- a/doc/afb-bindings-overview.md +++ /dev/null @@ -1,180 +0,0 @@ - -Overview of bindings shipped with AFB-Daemon -=========================================== - -List of bindings ---------------- - -Here are the bindings shipped in the source tree: - -* Hello World -* Authentication -* Tic Tac Toe -* Audio _(2 backends: ALSA/PulseAudio)_ -* Radio _(1 backend: RTLSDR RTL2832U)_ -* Media _(1 backend: Rygel UPnP)_ - -All bindings may not be built, depending on the development libraries present on -the system at build time. - - -Detail of bindings ------------------ - -### Hello World - -A sample Hello World binding for demonstration and learning purposes. - -This binding provides a few unauthenticated requests, all beginning with -"ping", to demonstrate basic binder capabilities. - -**Verbs**: - -* _ping:_ returns a success response -* _pingfail:_ returns a failure response -* _pingnull:_ returns a success response, with an empty JSON response field -* _pingbug:_ does a memory violation (intercepted by the binder) -* _pingJson:_ returns a success response, with a complex JSON response field -* _pingevent:_ broadcasts a global event - -
- - -### Authentication - -A sample Authentication binding for demonstration purposes. - -This binding provides a few requests to demonstrate the binder's token-based -security mechanism. - -Calling "_connect_" with a security token will initiate a session, calling -"_refresh_" will issue a new token and invalidate the previous one, calling -"_logout_" will invalidate all tokens and close the session. - -**Verbs**: - -* _ping:_ returns a success response -* _connect:_ creates a session and returns a new token -* _refresh:_ returns a new token -* _check:_ verifies the passed token is valid -* _logout:_ closes the session - -
- - -### Tic Tac Toe - -A sample Tic Tac Toe game binding. - -This binding provides an interactive Tic Tac Toe game where the binder returns -the grid as a JSON response. - -**Verbs**: - -* _new:_ starts a new game -* _play:_ asks the server to play -* _move:_ gives a client move -* _board:_ gets the current board state, as a JSON structure -* _level_: sets the server level -* _join_: joins an existing board -* _undo_: undo the last move -* _wait_: wait for a move - -
- - -### Audio - -A sample Audio binding with 2 backends: - -* ALSA (mandatory) -* PulseAudio (optional) - -This binding is able to initialize a specific soundcard, define volume levels, -channels (mono/stereo...), mute sound, and play a 22,050 Hz PCM stream. - -**Verbs**: - -* _ping:_ returns a success response -* _init:_ initializes backend, on the "default" sound card -* _volume:_ gets or sets volume, in % (0-100) -* _channels:_ gets or sets channels count (1-8) -* _mute:_ gets or sets the mute status (on-off) -* _play_: gets or sets the playing status (on-off) - -_(if PulseAudio development libraries are not found at build time, only ALSA -will be available)_ - -_(if a PulseAudio server is not found at runtime, the binding will dynamically -fall back to ALSA)_ - -_(a specifc backend can be forced by using this syntax before running afb-daemon -: **$ export AFB_AUDIO_OUTPUT=Alsa**)_ - -
- - -### Radio - -A sample AM/FM Radio binding with 1 backend: - -* RTLSDR - Realtek RTL2832U dongles (mandatory) - -This binding is able to initialize specific RTL2832U dongles, switch between -AM/FM modes, define frequency, mute sound, and play sound (if combining with -the **audio** binding). - -**Verbs**: - -* _ping:_ returns a success response -* _init:_ initializes backend, looking for plugged-in devices -* _power:_ sets device power status (on-off) -* _mode:_ sets device reception mode (AM-FM) -* _freq:_ sets device frequency (in Hz) -* _mute_: sets device mute status (on-off) -* _play_: sets device playing status (on-off) - -_(if rtlsdr development libraries are not found at build time, this binding will -not be built)_ - -
- -### Media - -A sample Media Server binding with 1 backend: - - * Rygel - -This binding is able to detect a local Rygel UPnP media server, list audio -files, select an audio file for playback, play/pause/seek in this file, upload -an audio file to the server. - -**Verbs**: - -* _ping:_ returns a success response -* _init:_ initializes backend, looking for an active local UPnP server -* _list:_ returns list of audio files, as a JSON structure -* _select:_ select an audio files, by index number (001-...) -* _play:_ plays the currently selected audio file -* _stop:_ stops the currently selected audio file -* _pause:_ pauses the currently selected audio file -* _seek:_ seeks in the currently selected audio file, in seconds -* _upload:_ uploads an audio file, with a POST request - -_(if GUPnP/GSSDP development libraries are not found at build time, this binding -will not be built)_ - -
- - ---- -
- -Sample command-line applications: _afb-client-demo_ (built by default) - -Sample HTML5 applications: -**test/*.html**, -**[afb-client](https://gerrit.automotivelinux.org/gerrit/gitweb?p=src/app-framework-demo.git;a=tree)**, -**[afb-radio](https://github.com/iotbzh/afb-radio)** - -Sample Qt/QML applications: *test/token-websock.qml* diff --git a/doc/afb-bindings-writing.md b/doc/afb-bindings-writing.md deleted file mode 100644 index d8335086..00000000 --- a/doc/afb-bindings-writing.md +++ /dev/null @@ -1,1407 +0,0 @@ - -How to write a binding for AFB-DAEMON -=================================== - -Summary -------- - -The afb-daemon binders serve files through HTTP protocol and offers to -developers the capability to offer application API methods through HTTP or -WebSocket protocol. - -The bindings are used to add API to ***afb-daemon***. -This part describes how to write a binding for***afb-daemon***. - -Excepting this summary, this document target developers. - -Before moving further through an example, here after -a short overview of binder bindings fundamentals. - -### Nature of a binding - -A binding is an independent piece of software. A binding is self contain and -exposes application logic as sharable library. A binding is intended to be -dynamically loaded by ***afb-daemon*** to expose application API. - -Technically, a binder binding does not reference and is not linked with any -***afb-daemon*** library. - -### Class of bindings - -Application binder supports two kinds of bindings: application bindings and -service bindings. Technically both class of binding are equivalent and use the -same coding convention. Only sharing mode and security context diverge. - -#### Application-bindings - -Application-bindings implements the glue in between application's UI and -services. Every AGL application has a corresponding binder that typically -activates one or many bindings to interface the application logic with lower -platform services. When an application is started by the AGL application -framework, a dedicate binder is started that loads/activates application -binding(s). API expose by application-binding are executed within corresponding -application security context. - -Application bindings generally handle a unique context for a unique client. As -the application framework start a dedicated instance of afb_daemon for each AGL -application, if a given binding is used within multiple application each of -those application get a new and private instance of eventually "shared" binding. - -#### Service-bindings - -Service-bindings enable API activation within corresponding service security -context and not within calling application context. Service-bindings are -intended to run as a unique instance. Service-bindings can be shared in between -multiple clients. - -Service-bindings can either be stateless or manage client context. When managing -context each client get a private context. Sharing may either be global to the -platform (ie: GPS service) or dedicated to a given user (ie: user preferences) - -### Live cycle of bindings within ***afb-daemon*** - -Application and service bindings are loaded and activated each time a new -***afb-daemon*** is started. - -At launch time, every loaded binding initialise itself. If a single binding -initialisation fails the corresponding instance of ***afb-daemon*** aborts. - -Conversely, when a binding initialisation succeeds, it should register its -unique name as well as the list of verbs (methods name from binder point of -view) attached to the methods it exposes. - -When initialised, on request from application clients to the right API/verbs -binding methods are activated by the ***afb-daemon*** attached to the -application or service. - -At exit time, no special action is enforced by ***afb-daemon***. When a specific -actions is required at afb-daemon stop, developers should use 'atexit/on_exit' -during binding initialisation sequence to register a custom exit function. - -### Binding Content - -Afb-daemon's bindings register two classes of objects: names and functions. - -Bindings declare categories of names: - - A unique binding name to access all API exposed by this binding, - - One name for each methods/verbs provided by this binding. - -Bindings declare two categories of functions: - - function used for initialisation - - functions implementing the exposed API methods - -Afb-daemon parses URI requests to extract the API(binding name) and the -VERB(method to activate). As an example, URI **foo/bar** translates to binding -named **foo** and method named **bar**. To serve such a request, -***afb-daemon*** looks for an active binding named **foo** and then within this -binding for a method named **bar**. When found ***afb-daemon*** calls -the corresponding method with an attached parameter if any. - -Afb-daemon is case-insensitive when parsing URI. Thus **TicTacToe/Board** and -**tictactoe/board** are equivalent. - -#### The name of the binding - -The name of a given binding is also known as the name -of the API prefix that defines the binding. - -The name of a binding SHOULD be unique within a given ***afb-daemon*** instance. - -For example, when a client of ***afb-daemon*** calls a URI named **foo/bar**. -Afb-daemon extracts the prefix **foo** and the suffix **bar**. **foo** must -match a binding name and **bar** has to match a VERB attached to some method. - -#### Names of methods - -Each binding exposes a set of methods that can be called by the clients of a -given ***afb-daemon***. - -VERB's name attached to a given binding (API) MUST be unique within a binding. - -Bindings static declaration link VERBS to the corresponding methods. -When clients emit requests on a given API/VERB corresponding method is called -by ***afb-daemon***. - -#### Initialisation function - -Binding's initialisation function serves several purposes. - -1. It allows ***afb-daemon*** to control the binding version depending on -the initialisation of function name. As today, the only supported initialisation -function is **afbBindingV1Register**. This identifies version "one" of bindings. - -2. It allows bindings to initialise itself. - -3. It enables names declarations: descriptions, requirements and implementations -of exposed API/VERB. - -#### Functions instantiation of API/VERBs - -When an API/VERB is called, ***afb-daemon*** constructs a request object. Then -it passes this request object to the implementation function corresponding to -requested method, this within attached API binding. - -An implementation function receives a request object used to: get -the arguments of the request, send an answer, store session data. - -A binding MUST set an answer to every received requests. - -Nevertheless, there are two implementations, *synchronous* and *asynchronous*. -API/VERB implementation that set an answer before returning are called -*synchronous implementations*. Those that do not systematically set an answer -before returning are called *asynchronous implementations*. - -Asynchronous implementations typically launch asynchronous actions. They record -some context at request time and provide an answer to the request only at -completion of asynchronous actions. - -The Tic-Tac-Toe example ------------------------ - -This part explains how to write an afb-binding. For the sake of being practical -it uses many examples based on tic-tac-toe. This binding example is in -*bindings/samples/tic-tac-toe.c*. - -This binding is named ***tictactoe***. - -Dependencies when compiling ---------------------------- - -Afb-daemon provides a configuration file for *pkg-config*. -Typing the command - - pkg-config --cflags afb-daemon - -Print flags use for compilation: - - $ pkg-config --cflags afb-daemon - -I/opt/local/include -I/usr/include/json-c - -For linking, you should use - - $ pkg-config --libs afb-daemon - -ljson-c - -Afb-daemon automatically includes the dependency to json-c. -This is activated through **Requires** keyword in pkg-config. -While almost every binding replies on **json-c** this is not a must have -dependency. - -Internally, ***afb-daemon*** relies on **libsystemd** for its event loop, as -well as for its binding to D-Bus. Bindings developers are encouraged to leverage -**libsystemd** when possible. Nevertheless there is no hard dependency to -**libsystemd** if you do not want to use it, feel free to do so. - -> Afb-daemon bindings are fully self contained. They do not enforce dependency -on any libraries from the application framework. -> Afb-daemon dependencies requirer to run AGL bindings are given at runtime -through pointers leveraging read-only -> memory feature. - -Header files to include ------------------------ - -Binding *tictactoe* has following includes: - -```C -#define _GNU_SOURCE -#include -#include -#include -#include -``` - -Header *afb/afb-binding.h* is the only hard dependency, it includes all features -that a binding MUST HAVE. Outside of includes used to support application logic, -common external headers used within bindings are: - -- *json-c/json.h*: should be include to handle json objects; -- *systemd/sd-event.h*: should be include to access event main loop; -- *systemd/sd-bus.h*: should be include for dbus connections. - -The *tictactoe* binding does not leverage systemd features, also only json.h -is used on top of mandatory afb/afb-binding.h. - -When including *afb/afb-binding.h*, the macro **_GNU_SOURCE** MUST be -defined. - -Choosing names --------------- - -Designers of bindings should define a unique name for every API binding -as well as for methods VERBs. They should also define names for request -arguments passed as name/value pair in URI. - -While forging names, designers should respect few rules to -ensure that created names are valid and easy to use across platforms. - -All names and strings are UTF-8 encoded. - -### Names for API (binding) - -Binding API name are checked. -All characters are authorised except: - -- the control characters (\u0000 .. \u001f) -- the characters of the set { ' ', '"', '#', '%', '&', - '\'', '/', '?', '`', '\x7f' } - -In other words the set of forbidden characters is -{ \u0000..\u0020, \u0022, \u0023, \u0025..\u0027, - \u002f, \u003f, \u0060, \u007f }. - -Afb-daemon makes no distinction between lower case -and upper case when searching for API/VERB. - -### Names for methods - -The names of methods VERBs are totally free and not checked. - -However, the validity rules for method's VERB name are the -same as for Binding API name except that the dot(.) character -is forbidden. - -Afb-daemon makes no case distinction when searching for an API by name. - -### Names for arguments - -Argument's name are not restricted and can be everything you wish. - -> Warning arguments search is case sensitive and "index" and "Index" -> are not two different arguments. - -### Forging names widely available - -The key names of javascript object can be almost -anything using the arrayed notation: - - object[key] = value - -Nevertheless this is not the case with javascript dot notation: - - object.key = value - -Using the dot notation, the key must be a valid javascript -identifier and dash(-) as well as few other reserved characters cannot be used. - -For this reason, we advise developers to chose name compatible with both -javascript and HTML notation. - -It is a good practice, even for arguments not to rely on case sensitivity. -This may reduce headache strength at debug time, especially with interpreted -language like javascript that may not warn you that a variable was not defined. - -Declaration of methods and initialisation of the bindings ---------------------------------------------------------- - -### Declaration of methods - -To be active, binding's methods should be declared to -***afb-daemon***. Furthermore, the binding itself must be recorded. - -The registration mechanism is very basic: when ***afb-daemon*** starts, -it loads all bindings listed in: command line or configuration file. - -Loading a binding follows the following steps: - -1. Afb-daemon loads the binding with *dlopen*. - -2. Afb-daemon searches for a symbol named **afbBindingV1Register** using *dlsym*. -This symbol is assumed to be the exported initialisation function of the binding. - -3. Afb-daemon builds an interface object for the binding. - -4. Afb-daemon calls the found function **afbBindingV1Register** with interface pointer -as parameter. - -5. Function **afbBindingV1Register** setups the binding and initialises it. - -6. Function **afbBindingV1Register** returns the pointer to a structure -describing the binding: version, name (prefix or API name), and list of methods. - -7. Afb-daemon checks that the returned version and name can be managed. -If so, binding and its methods are register to become usable as soon as -***afb-daemon*** initialisation is finished. - -### Initialisation of bindings - -The bindings initialisation is the final step made at the end of declaration of -methods. This will initialize the binding and make its ***afb-daemon***'s -interface fully functional. - -So, afb-daemon binder call **afbBindingV1ServiceInit** as final step to a -binding. This will allows the binding to call features in its name and as saw in -[Binder events guide](afb-events-guide.md) you can create an event only at this -moment and not before. Before that it will fail because afb-daemon doesn't know -the binding name. - -**afbBindingV1ServiceInit** is defined as below: - -```C -/* - * When a binding have an exported implementation of the - * function 'afbBindingV1ServiceInit', defined below, - * the framework calls it for initialising the service after - * registration of all bindings. - * - * The object 'service' should be recorded. It has functions that - * allows the binding to call features with its own personality. - * - * The function should return 0 in case of success or, else, should return - * a negative value. - */ -extern int afbBindingV1ServiceInit(struct afb_service service); -``` - -### Application binding example: tic-tac-toe - -If we continue our tic-tac-toe example, here after the code used for -**afbBindingV1Register** implementation from binding *tic-tac-toe*: - -```C -/* - * activation function for registering the binding called by afb-daemon - */ -const struct afb_binding *afbBindingV1Register(const struct afb_binding_interface *itf) -{ - afbitf = itf; // records the interface for accessing afb-daemon - return &binding_description; // returns the description of the binding -} -``` - -It is a very minimal initialisation function because *tic-tac-toe* binding doesn't -have any application related initialisation step. It merely record daemon's interface -and returns its description. - -The variable **afbitf** is a binding global variable. It keeps the -interface to ***afb-daemon*** that should be used for logging and pushing events. -Here is its declaration: - -```C -/* - * the interface to afb-daemon - */ -const struct afb_binding_interface *afbitf; -``` - -The description of the binding is defined here after. - -```C -/* - * array of the methods exported to afb-daemon - */ -static const struct afb_verb_desc_v1 binding_methods[] = { - /* VERB'S NAME SESSION MANAGEMENT FUNCTION TO CALL SHORT DESCRIPTION */ - { .name= "new", .session= AFB_SESSION_NONE, .callback= new, .info= "Starts a new game" }, - { .name= "play", .session= AFB_SESSION_NONE, .callback= play, .info= "Asks the server to play" }, - { .name= "move", .session= AFB_SESSION_NONE, .callback= move, .info= "Tells the client move" }, - { .name= "board", .session= AFB_SESSION_NONE, .callback= board, .info= "Get the current board" }, - { .name= "level", .session= AFB_SESSION_NONE, .callback= level, .info= "Set the server level" }, - { .name= "join", .session= AFB_SESSION_CHECK,.callback= join, .info= "Join a board" }, - { .name= "undo", .session= AFB_SESSION_NONE, .callback= undo, .info= "Undo the last move" }, - { .name= "wait", .session= AFB_SESSION_NONE, .callback= wait, .info= "Wait for a change" }, - { .name= NULL } /* marker for end of the array */ -}; - -/* - * description of the binding for afb-daemon - */ -static const struct afb_binding binding_description = -{ - /* description conforms to VERSION 1 */ - .type= AFB_BINDING_VERSION_1, - .v1= { /* fills the v1 field of the union when AFB_BINDING_VERSION_1 */ - .prefix= "tictactoe", /* the API name (or binding name or prefix) */ - .info= "Sample tac-tac-toe game", /* short description of of the binding */ - .methods = binding_methods /* the array describing the methods of the API */ - } -}; -``` - -The structure **binding_description** describes the binding. -It declares the type and version of the binding, its name, a short description -and its methods list. - -The list of methods is an array of structures describing the methods and terminated by a NULL marker. - -In version one of afb-damon binding, a method description contains 4 fields: - -- the name of the method, - -- the session management flags, - -- the implementation function to be call for the method, - -- a short description. - -The structure describing methods is defined as follows: - -```C -/* - * Description of one method of the API provided by the binding - * This enumeration is valid for bindings of type 1 - */ -struct afb_verb_desc_v1 -{ - const char *name; /* name of the method */ - enum AFB_session_v1 session; /* authorisation and session requirements of the method */ - void (*callback)(struct afb_req req); /* callback function implementing the method */ - const char *info; /* textual description of the method */ -}; -``` - -For technical reasons, the enumeration **enum AFB_session_v1** is not exactly an -enumeration but the wrapper of constant definitions that can be mixed using bitwise or -(the C operator |). - -The constants that can bit mixed are: - -Constant name | Meaning --------------------------|------------------------------------------------------------- -**AFB_SESSION_CREATE** | Equals to AFB_SESSION_LOA_EQ_0|AFB_SESSION_RENEW -**AFB_SESSION_CLOSE** | Closes the session after the reply and set the LOA to 0 -**AFB_SESSION_RENEW** | Refreshes the token of authentification -**AFB_SESSION_CHECK** | Just requires the token authentification -**AFB_SESSION_LOA_LE_0** | Requires the current LOA to be lesser then or equal to 0 -**AFB_SESSION_LOA_LE_1** | Requires the current LOA to be lesser then or equal to 1 -**AFB_SESSION_LOA_LE_2** | Requires the current LOA to be lesser then or equal to 2 -**AFB_SESSION_LOA_LE_3** | Requires the current LOA to be lesser then or equal to 3 -**AFB_SESSION_LOA_GE_0** | Requires the current LOA to be greater then or equal to 0 -**AFB_SESSION_LOA_GE_1** | Requires the current LOA to be greater then or equal to 1 -**AFB_SESSION_LOA_GE_2** | Requires the current LOA to be greater then or equal to 2 -**AFB_SESSION_LOA_GE_3** | Requires the current LOA to be greater then or equal to 3 -**AFB_SESSION_LOA_EQ_0** | Requires the current LOA to be equal to 0 -**AFB_SESSION_LOA_EQ_1** | Requires the current LOA to be equal to 1 -**AFB_SESSION_LOA_EQ_2** | Requires the current LOA to be equal to 2 -**AFB_SESSION_LOA_EQ_3** | Requires the current LOA to be equal to 3 - -If any of this flag is set, ***afb-daemon*** requires an authentication token -as if **AFB_SESSION_CHECK** flag was also set. - -The special value **AFB_SESSION_NONE** is zero and can be used to bypass token check. - -> Note that **AFB_SESSION_CREATE** and **AFB_SESSION_CLOSE** might be removed in later versions. - -Sending messages to the log system ----------------------------------- - -Afb-daemon provides 4 levels of verbosity and 5 methods for logging messages. - -The verbosity is managed. Options allow the change the verbosity of ***afb-daemon*** -and the verbosity of the bindings can be set binding by binding. - -The methods for logging messages are defined as macros that test the -verbosity level and that call the real logging function only if the -message must be output. This avoid evaluation of arguments of the -formatting messages if the message must not be output. - -### Verbs for logging messages - -The 5 logging methods are: - -Macro | Verbosity | Meaning | syslog level ---------|:---------:|-----------------------------------|:-----------: -ERROR | 0 | Error conditions | 3 -WARNING | 1 | Warning conditions | 4 -NOTICE | 1 | Normal but significant condition | 5 -INFO | 2 | Informational | 6 -DEBUG | 3 | Debug-level messages | 7 - -You can note that the 2 methods **WARNING** and **NOTICE** have the same level -of verbosity. But they don't have the same *syslog level*. It means that -they are output with a different level on the logging system. - -All of these methods have the same signature: - -```C -void ERROR(const struct afb_binding_interface *afbitf, const char *message, ...); -``` - -The first argument **afbitf** is the interface to afb daemon that the -binding received at initialisation time when **afbBindingV1Register** is called. - -The second argument **message** is a formatting string compatible with printf/sprintf. - -The remaining arguments are arguments of the formating message like with printf. - -### Managing verbosity - -Depending on the level of verbosity, the messages are output or not. -The following table explains what messages will be output depending -ont the verbosity level. - -Level of verbosity | Outputed macro -:-----------------:|-------------------------- -0 | ERROR -1 | ERROR + WARNING + NOTICE -2 | ERROR + WARNING + NOTICE + INFO -3 | ERROR + WARNING + NOTICE + INFO + DEBUG - -### Output format and destination - -The syslog level is used for forging a prefix to the message. -The prefixes are: - -syslog level | prefix -:-----------:|--------------- -0 | <0> EMERGENCY -1 | <1> ALERT -2 | <2> CRITICAL -3 | <3> ERROR -4 | <4> WARNING -5 | <5> NOTICE -6 | <6> INFO -7 | <7> DEBUG - - -The message is pushed to standard error. -The final destination of the message depends on how systemd service -was configured through its variable **StandardError**. It can be -journal, syslog or kmsg. (See man sd-daemon). - -Sending events --------------- - -Specific documentation exists about [sending events](afb-events-guide.md). - -The binding *tic-tac-toe* broadcasts events when the board changes. This is done -in the function ***changed***: - -```C -/* - * signals a change of the board - */ -static void changed(struct board *board, const char *reason) -{ - ... - struct json_object *description; - - /* get the description */ - description = describe(board); - - ... - - afb_daemon_broadcast_event(afbitf->daemon, reason, description); -} -``` - -The description of the changed board is pushed via the daemon interface. - -Within binding *tic-tac-toe*, *reason* indicates the origin of -the change. In function **afb_daemon_broadcast_event** the second -parameter is the name of broadcasted event. The third argument is the -object that is transmitted with the event. - -Function **afb_daemon_broadcast_event** is defined here after: - -```C -/* - * Broadcasts widely the event of 'name' with the data 'object'. - * 'object' can be NULL. - * 'daemon' MUST be the daemon given in interface when activating the binding. - * - * For convenience, the function calls 'json_object_put' for 'object'. - * Thus, in the case where 'object' should remain available after - * the function returns, the function 'json_object_get' shall be used. - */ -void afb_daemon_broadcast_event(struct afb_daemon daemon, const char *name, struct json_object *object); -``` - -> Be aware, as with reply functions **object** is automatically released using -> **json_object_put** when using this function. Call **json_object_get** before -> calling **afb_daemon_broadcast_event** to keep **object** available -> after function returns. - -Event name received by listeners is prefixed with binding name. -So when a change occurs after a move, the reason is **move** and every clients -receive an event **tictactoe/move**. - -> Note that nothing is said about case sensitivity of event names. -> However, the event is always prefixed with the name that the binding -> declared, with the same case, followed with a slash /. -> Thus it is safe to compare event using a case sensitive comparison. - -Writing a synchronous method implementation ------------------------------------------ - -The method **tictactoe/board** is a synchronous implementation. -Here is its listing: - -```C -/* - * get the board - */ -static void board(struct afb_req req) -{ - struct board *board; - struct json_object *description; - - /* retrieves the context for the session */ - board = board_of_req(req); - INFO(afbitf, "method 'board' called for boardid %d", board->id); - - /* describe the board */ - description = describe(board); - - /* send the board's description */ - afb_req_success(req, description, NULL); -} -``` - -This example shows many aspects of a synchronous -method implementation. Let summarise it: - -1. The function **board_of_req** retrieves the context stored -for the binding: the board. - -2. The macro **INFO** sends a message of kind *INFO* -to the logging system. The global variable named **afbitf** -used represents the interface to ***afb-daemon***. - -3. The function **describe** creates a json_object representing -the board. - -4. The function **afb_req_success** sends the reply, attaching to -it the object *description*. - -### The incoming request - -For any implementation, the request is received by a structure of type -**struct afb_req**. - -> Note that this is a PLAIN structure, not a pointer to a structure. - -The definition of **struct afb_req** is: - -```C -/* - * Describes the request by bindings from afb-daemon - */ -struct afb_req { - const struct afb_req_itf *itf; /* the interfacing functions */ - void *closure; /* the closure for functions */ -}; -``` - -It contains two pointers: first one *itf*, points to functions used -to handle internal request. Second one *closure* point onto function closure. - -> The structure must never be used directly. -> Instead developer should use the intended functions provided -> by ***afb-daemon*** as described here after. - -*req* is used to get arguments of the request, to send -answer, to store session data. - -This object and its interface is defined and documented -in the file names *afb/afb-req-itf.h* - -The above example uses twice *req* object request. - -The first time, to retrieve the board attached to the session of the request. - -The second time, to send the reply: an object that describes the current board. - -### Associating a client context to a session - -When *tic-tac-toe* binding receives a request, it musts get -the board describing the game associated to the session. - -For a binding, having data associated to a session is common. -This data is called "binding context" for the session. -Within *tic-tac-toe* binding the context is the board. - -Requests *afb_req* offer four functions for storing and retrieving session -associated context. - -These functions are: - -- **afb_req_context_get**: - retrieves context data stored for current binding. - -- **afb_req_context_set**: - store context data of current binding. - -- **afb_req_context**: - if exist retrieves context data of current binding. - if context does not yet exist, creates a new context and store it. - -- **afb_req_context_clear**: - reset the stored context data. - -The binding *tictactoe* use a convenient function to retrieve -its context: the board. This function is *board_of_req*: - -```C -/* - * retrieves the board of the request - */ -static inline struct board *board_of_req(struct afb_req req) -{ - return afb_req_context(req, (void*)get_new_board, (void*)release_board); -} -``` - -The function **afb_req_context** ensures an existing context -for the session of the request. -Its two last arguments are functions to allocate and free context. -Note function type casts to avoid compilation warnings. - -Here is the definition of the function **afb_req_context** - -```C -/* - * Gets the pointer stored by the binding for the session of 'req'. - * If the stored pointer is NULL, indicating that no pointer was - * already stored, afb_req_context creates a new context by calling - * the function 'create_context' and stores it with the freeing function - * 'free_context'. - */ -static inline void *afb_req_context(struct afb_req req, void *(*create_context)(), void (*free_context)(void*)) -{ - void *result = afb_req_context_get(req); - if (result == NULL) { - result = create_context(); - afb_req_context_set(req, result, free_context); - } - return result; -} -``` - -The second argument if the function that creates the context. -For binding *tic-tac-toe* (function **get_new_board**). -The function **get_new_board** creates a new board and set usage its count to 1. -The boards are checking usage count to free resources when not used. - -The third argument is a function that frees context resources. -For binding *tic-tac-toe* (function **release_board**). -The function **release_board** decrease usage count of the board passed in -argument. When usage count falls to zero, data board are freed. - -Definition of other functions dealing with contexts: - -```C -/* - * Gets the pointer stored by the binding for the session of 'req'. - * When the binding has not yet recorded a pointer, NULL is returned. - */ -void *afb_req_context_get(struct afb_req req); - -/* - * Stores for the binding the pointer 'context' to the session of 'req'. - * The function 'free_context' will be called when the session is closed - * or if binding stores an other pointer. - */ -void afb_req_context_set(struct afb_req req, void *context, void (*free_context)(void*)); - -/* - * Frees the pointer stored by the binding for the session of 'req' - * and sets it to NULL. - * - * Shortcut for: afb_req_context_set(req, NULL, NULL) - */ -static inline void afb_req_context_clear(struct afb_req req) -{ - afb_req_context_set(req, NULL, NULL); -} -``` - -### Sending reply to a request - -Two kinds of replies: successful or failure. - -> Sending a reply to a request MUST be done once and only once. - -It exists two functions for "success" replies: **afb_req_success** and **afb_req_success_f**. - -```C -/* - * Sends a reply of kind success to the request 'req'. - * The status of the reply is automatically set to "success". - * Its send the object 'obj' (can be NULL) with an - * informationnal comment 'info (can also be NULL). - * - * For convenience, the function calls 'json_object_put' for 'obj'. - * Thus, in the case where 'obj' should remain available after - * the function returns, the function 'json_object_get' shall be used. - */ -void afb_req_success(struct afb_req req, struct json_object *obj, const char *info); - -/* - * Same as 'afb_req_success' but the 'info' is a formatting - * string followed by arguments. - * - * For convenience, the function calls 'json_object_put' for 'obj'. - * Thus, in the case where 'obj' should remain available after - * the function returns, the function 'json_object_get' shall be used. - */ -void afb_req_success_f(struct afb_req req, struct json_object *obj, const char *info, ...); -``` - -It exists two functions for "failure" replies: **afb_req_fail** and **afb_req_fail_f**. - -```C -/* - * Sends a reply of kind failure to the request 'req'. - * The status of the reply is set to 'status' and an - * informational comment 'info' (can also be NULL) can be added. - * - * Note that calling afb_req_fail("success", info) is equivalent - * to call afb_req_success(NULL, info). Thus even if possible it - * is strongly recommended to NEVER use "success" for status. - * - * For convenience, the function calls 'json_object_put' for 'obj'. - * Thus, in the case where 'obj' should remain available after - * the function returns, the function 'json_object_get' shall be used. - */ -void afb_req_fail(struct afb_req req, const char *status, const char *info); - -/* - * Same as 'afb_req_fail' but the 'info' is a formatting - * string followed by arguments. - * - * For convenience, the function calls 'json_object_put' for 'obj'. - * Thus, in the case where 'obj' should remain available after - * the function returns, the function 'json_object_get' shall be used. - */ -void afb_req_fail_f(struct afb_req req, const char *status, const char *info, ...); -``` - -> For convenience, these functions automatically call **json_object_put** to release **obj**. -> Because **obj** usage count is null after being passed to a reply function, it SHOULD not be used anymore. -> If exceptionally **obj** needs to remain usable after reply function then using **json_object_get** on **obj** -> to increase usage count and cancels the effect the **json_object_put** is possible. - -Getting argument of invocation ------------------------------- - -Many methods expect arguments. Afb-daemon's bindings -retrieve arguments by name and not by position. - -Arguments are passed by requests through either HTTP -or WebSockets. - -For example, the method **join** of binding **tic-tac-toe** -expects one argument: the *boardid* to join. Here is an extract: - -```C -/* - * Join a board - */ -static void join(struct afb_req req) -{ - struct board *board, *new_board; - const char *id; - - /* retrieves the context for the session */ - board = board_of_req(req); - INFO(afbitf, "method 'join' called for boardid %d", board->id); - - /* retrieves the argument */ - id = afb_req_value(req, "boardid"); - if (id == NULL) - goto bad_request; - ... -``` - -The function **afb_req_value** searches in the request *req* -for argument name passed in the second argument. When argument name -is not passed, **afb_req_value** returns NULL. - -> The search is case sensitive and *boardid* is not equivalent to *BoardId*. -> Nevertheless having argument names that only differ by name case is not a good idea. - -### Basic functions for querying arguments - -The function **afb_req_value** is defined here after: - -```C -/* - * Gets from the request 'req' the string value of the argument of 'name'. - * Returns NULL if when there is no argument of 'name'. - * Returns the value of the argument of 'name' otherwise. - * - * Shortcut for: afb_req_get(req, name).value - */ -static inline const char *afb_req_value(struct afb_req req, const char *name) -{ - return afb_req_get(req, name).value; -} -``` - -It is defined as a shortcut to call the function **afb_req_get**. -That function is defined here after: - -```C -/* - * Gets from the request 'req' the argument of 'name'. - * Returns a PLAIN structure of type 'struct afb_arg'. - * When the argument of 'name' is not found, all fields of result are set to NULL. - * When the argument of 'name' is found, the fields are filled, - * in particular, the field 'result.name' is set to 'name'. - * - * There is a special name value: the empty string. - * The argument of name "" is defined only if the request was made using - * an HTTP POST of Content-Type "application/json". In that case, the - * argument of name "" receives the value of the body of the HTTP request. - */ -struct afb_arg afb_req_get(struct afb_req req, const char *name); -``` - -That function takes 2 parameters: the request and the name -of the argument to retrieve. It returns a PLAIN structure of -type **struct afb_arg**. - -There is a special name that is defined when the request is -of type HTTP/POST with a Content-Type being application/json. -This name is **""** (the empty string). In that case, the value -of this argument of empty name is the string received as a body -of the post and is supposed to be a JSON string. - -The definition of **struct afb_arg** is: - -```C -/* - * Describes an argument (or parameter) of a request - */ -struct afb_arg { - const char *name; /* name of the argument or NULL if invalid */ - const char *value; /* string representation of the value of the argument */ - /* original filename of the argument if path != NULL */ - const char *path; /* if not NULL, path of the received file for the argument */ - /* when the request is finalized this file is removed */ -}; -``` - -The structure returns the data arguments that are known for the -request. This data include a field named **path**. This **path** -can be accessed using the function **afb_req_path** defined here after: - -```C -/* - * Gets from the request 'req' the path for file attached to the argument of 'name'. - * Returns NULL if when there is no argument of 'name' or when there is no file. - * Returns the path of the argument of 'name' otherwise. - * - * Shortcut for: afb_req_get(req, name).path - */ -static inline const char *afb_req_path(struct afb_req req, const char *name) -{ - return afb_req_get(req, name).path; -} -``` - -The path is only defined for HTTP/POST requests that send file. - -### Arguments for received files - -As it is explained above, clients can send files using HTTP/POST requests. - -Received files are attached to "file" argument name. For example, the -following HTTP fragment (from test/sample-post.html) -will send an HTTP/POST request to the method -**post/upload-image** with 2 arguments named *file* and -*hidden*. - -```html -

Sample Post File

-
- - -
- -
-``` - -Argument named **file** should have both its value and path defined. - -The value is the name of the file as it was set by the HTTP client. -Generally it is the filename on client side. - -The path is the effective path of saved file on the temporary local storage -area of the application. This is a randomly generated and unique filename. -It is not linked with the original filename as used on client side. - -After success the binding can use the uploaded file directly from local storage path with no restriction: -read, write, remove, copy, rename... -Nevertheless when request reply is set and query terminated, the uploaded temporary file at -path is destroyed. - -### Arguments as a JSON object - -Bindings may also request every arguments of a given call as one single object. -This feature is provided by the function **afb_req_json** defined here after: - -```C -/* - * Gets from the request 'req' the json object hashing the arguments. - * The returned object must not be released using 'json_object_put'. - */ -struct json_object *afb_req_json(struct afb_req req); -``` - -It returns a json object. This object depends on how the request was built: - -- For HTTP requests, this json object uses key names mapped on argument name. -Values are either string for common arguments or object ie: { "file": "...", "path": "..." } - -- For WebSockets requests, returned directly the object as provided by the client. - -> In fact, for Websockets requests, the function **afb_req_value** -> can be seen as a shortcut to -> ***json_object_get_string(json_object_object_get(afb_req_json(req), name))*** - - -Writing an asynchronous method implementation -------------------------------------------- - -The *tic-tac-toe* example allows two clients or more to share the same board. -This is implemented by the method **join** that illustrated partly how to -retrieve arguments. - -When two or more clients are sharing a same board, one of them can wait -until the state of the board changes, but this could also be implemented using -events because an event is generated each time the board changes. - -In this case, the reply to the wait is sent only when the board changes. -See the diagram below: - -![tic-tac-toe_diagram][tic-tac-toe_diagram] - -Here, this is an invocation of the binding by an other client that -unblock the suspended *wait* call. -Nevertheless in most case this should be a timer, a hardware event, a sync with -a concurrent process or thread, ... - -Common case of an asynchronous implementation. - -Here is the listing of the function **wait**: - -```C -static void wait(struct afb_req req) -{ - struct board *board; - struct waiter *waiter; - - /* retrieves the context for the session */ - board = board_of_req(req); - INFO(afbitf, "method 'wait' called for boardid %d", board->id); - - /* creates the waiter and enqueues it */ - waiter = calloc(1, sizeof *waiter); - waiter->req = req; - waiter->next = board->waiters; - afb_req_addref(req); - board->waiters = waiter; -} -``` - -After retrieving the board, the function adds a new waiter to -waiters list and returns without setting a reply. - -Before returning, it increases **req** request's reference count using **afb_req_addref** function. - -> When a method returns without setting a reply, -> it **MUST** increment request's reference count -> using **afb_req_addref**. If unpredictable behaviour may pop up. - -Later, when a board changes, it calls *tic-tac-toe* **changed** function -with reason of change in parameter. - -Here is the full listing of the function **changed**: - -```C -/* - * signals a change of the board - */ -static void changed(struct board *board, const char *reason) -{ - struct waiter *waiter, *next; - struct json_object *description; - - /* get the description */ - description = describe(board); - - waiter = board->waiters; - board->waiters = NULL; - while (waiter != NULL) { - next = waiter->next; - afb_req_success(waiter->req, json_object_get(description), reason); - afb_req_unref(waiter->req); - free(waiter); - waiter = next; - } - - afb_event_sender_push(afb_daemon_get_event_sender(afbitf->daemon), reason, description); -} -``` - -The list of waiters is walked and a reply is sent to each waiter. -After sending the reply, the reference count of the request -is decremented using **afb_req_unref** to allow resources to be freed. - -> The reference count **MUST** be decremented using **afb_req_unref** to free -> resources and avoid memory leaks. -> This usage count decrement should happen **AFTER** setting reply or -> bad things may happen. - -Sending messages to the log system ----------------------------------- - -Afb-daemon provides 4 levels of verbosity and 5 methods for logging messages. - -The verbosity is managed. Options allow the change the verbosity of ***afb-daemon*** -and the verbosity of the bindings can be set binding by binding. - -The methods for logging messages are defined as macros that test the -verbosity level and that call the real logging function only if the -message must be output. This avoid evaluation of arguments of the -formatting messages if the message must not be output. - -### Verbs for logging messages - -The 5 logging methods are: - -Macro | Verbosity | Meaning | syslog level ---------|:---------:|-----------------------------------|:-----------: -ERROR | 0 | Error conditions | 3 -WARNING | 1 | Warning conditions | 4 -NOTICE | 1 | Normal but significant condition | 5 -INFO | 2 | Informational | 6 -DEBUG | 3 | Debug-level messages | 7 - -You can note that the 2 methods **WARNING** and **NOTICE** have the same level -of verbosity. But they don't have the same *syslog level*. It means that -they are output with a different level on the logging system. - -All of these methods have the same signature: - -```C -void ERROR(const struct afb_binding_interface *afbitf, const char *message, ...); -``` - -The first argument **afbitf** is the interface to afb daemon that the -binding received at initialisation time when **afbBindingV1Register** is called. - -The second argument **message** is a formatting string compatible with printf/sprintf. - -The remaining arguments are arguments of the formating message like with printf. - -### Managing verbosity - -Depending on the level of verbosity, the messages are output or not. -The following table explains what messages will be output depending -ont the verbosity level. - -Level of verbosity | Outputed macro -:-----------------:|-------------------------- -0 | ERROR -1 | ERROR + WARNING + NOTICE -2 | ERROR + WARNING + NOTICE + INFO -3 | ERROR + WARNING + NOTICE + INFO + DEBUG - -### Output format and destination - -The syslog level is used for forging a prefix to the message. -The prefixes are: - -syslog level | prefix -:-----------:|--------------- -0 | <0> EMERGENCY -1 | <1> ALERT -2 | <2> CRITICAL -3 | <3> ERROR -4 | <4> WARNING -5 | <5> NOTICE -6 | <6> INFO -7 | <7> DEBUG - - -The message is pushed to standard error. -The final destination of the message depends on how systemd service -was configured through its variable **StandardError**. It can be -journal, syslog or kmsg. (See man sd-daemon). - -Sending events --------------- - -Since version 0.5, bindings can broadcast events to any potential listener. -As today only unattended events are supported. Targeted events are expected for -next coming version. - -The binding *tic-tac-toe* broadcasts events when the board changes. -This is done in the function **changed**: - -```C -/* - * signals a change of the board - */ -static void changed(struct board *board, const char *reason) -{ - ... - struct json_object *description; - - /* get the description */ - description = describe(board); - - ... - - afb_daemon_broadcast_event(afbitf->daemon, reason, description); -} -``` - -The description of the changed board is pushed via the daemon interface. - -Within binding *tic-tac-toe*, *reason* indicates the origin of -the change. In function **afb_daemon_broadcast_event** the second -parameter is the name of broadcasted event. The third argument is the -object that is transmitted with the event. - -Function **afb_daemon_broadcast_event** is defined here after: - -```C -/* - * Broadcasts widely the event of 'name' with the data 'object'. - * 'object' can be NULL. - * 'daemon' MUST be the daemon given in interface when activating the binding. - * - * For convenience, the function calls 'json_object_put' for 'object'. - * Thus, in the case where 'object' should remain available after - * the function returns, the function 'json_object_get' shall be used. - */ -void afb_daemon_broadcast_event(struct afb_daemon daemon, const char *name, struct json_object *object); -``` - -> Be aware, as with reply functions **object** is automatically released using -> **json_object_put** when using this function. Call **json_object_get** before -> calling **afb_daemon_broadcast_event** to keep **object** available -> after function returns. - -Event name received by listeners is prefixed with binding name. -So when a change occurs after a move, the reason is **move** and every clients -receive an event **tictactoe/move**. - -> Note that nothing is said about case sensitivity of event names. -> However, the event is always prefixed with the name that the binding -> declared, with the same case, followed with a slash /. -> Thus it is safe to compare event using a case sensitive comparison. - -How to build a binding ---------------------- - -Afb-daemon provides a *pkg-config* configuration file that can be -queried by providing ***afb-daemon*** in command line arguments. -This configuration file provides data that should be used -for bindings compilation. Examples: - -```bash -$ pkg-config --cflags afb-daemon -$ pkg-config --libs afb-daemon -``` - -### Example for cmake meta build system - -This example is the extract for building the binding *afm-main* using *CMAKE*. - -```cmake -pkg_check_modules(afb afb-daemon) -if(afb_FOUND) - message(STATUS "Creation afm-main-binding for AFB-DAEMON") - add_library(afm-main-binding MODULE afm-main-binding.c) - target_compile_options(afm-main-binding PRIVATE ${afb_CFLAGS}) - target_include_directories(afm-main-binding PRIVATE ${afb_INCLUDE_DIRS}) - target_link_libraries(afm-main-binding utils ${afb_LIBRARIES}) - set_target_properties(afm-main-binding PROPERTIES - PREFIX "" - LINK_FLAGS "-Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/afm-main-binding.export-map" - ) - install(TARGETS afm-main-binding LIBRARY DESTINATION ${binding_dir}) -else() - message(STATUS "Not creating the binding for AFB-DAEMON") -endif() -``` - -Let now describe some of these lines. - -```cmake -pkg_check_modules(afb afb-daemon) -``` - -This first lines searches to the *pkg-config* configuration file for -**afb-daemon**. Resulting data are stored in the following variables: - -Variable | Meaning -------------------|------------------------------------------------ -afb_FOUND | Set to 1 if afb-daemon binding development files exist -afb_LIBRARIES | Only the libraries (w/o the '-l') for compiling afb-daemon bindings -afb_LIBRARY_DIRS | The paths of the libraries (w/o the '-L') for compiling afb-daemon bindings -afb_LDFLAGS | All required linker flags for compiling afb-daemon bindings -afb_INCLUDE_DIRS | The '-I' preprocessor flags (w/o the '-I') for compiling afb-daemon bindings -afb_CFLAGS | All required cflags for compiling afb-daemon bindings - -If development files are found, the binding can be added to the set of -target to build. - -```cmake -add_library(afm-main-binding MODULE afm-main-binding.c) -``` - -This line asks to create a shared library having a single -source file named afm-main-binding.c to be compiled. -The default name of the created shared object is -**libafm-main-binding.so**. - -```cmake -set_target_properties(afm-main-binding PROPERTIES - PREFIX "" - LINK_FLAGS "-Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/afm-main-binding.export-map" -) -``` - -This lines are doing two things: - -1. It renames the built library from **libafm-main-binding.so** to **afm-main-binding.so** -by removing the implicitly added prefix *lib*. This step is not mandatory -because afb-daemon doesn't check names of files at load time. -The only filename convention used by afb-daemon relates to **.so** termination. -*.so pattern is used when afb-daemon automatically discovers binding from a directory hierarchy. - -2. It applies a version script at link time to only export the reserved name -**afbBindingV1Register** for registration entry point. By default, when building -a shared library linker exports all the public symbols (C functions that are not **static**). - -Next line are: - -```cmake -target_include_directories(afm-main-binding PRIVATE ${afb_INCLUDE_DIRS}) -target_link_libraries(afm-main-binding utils ${afb_LIBRARIES}) -``` - -As you can see it uses the variables computed by ***pkg_check_modules(afb afb-daemon)*** -to configure the compiler and the linker. - -### Exporting the function afbBindingV1Register - -The function **afbBindingV1Register** MUST be exported. This can be achieved -using a version script at link time. Here after is a version script used for -*tic-tac-toe* (bindings/samples/export.map). - - { global: afbBindingV1Register; local: *; }; - -This sample [version script](https://sourceware.org/binutils/docs-2.26/ld/VERSION.html#VERSION) -exports as global the symbol *afbBindingV1Register* and hides any -other symbols. - -This version script is added to the link options using the -option **--version-script=export.map** is given directly to the -linker or using the option **-Wl,--version-script=export.map** -when the option is given to the C compiler. - -### Building within yocto - -Adding a dependency to afb-daemon is enough. See below: - - DEPENDS += " afb-daemon " - - -[tic-tac-toe_diagram]: pictures/tic-tac-toe.svg diff --git a/doc/afb-daemon-vocabulary.md b/doc/afb-daemon-vocabulary.md deleted file mode 100644 index c3b7c1ea..00000000 --- a/doc/afb-daemon-vocabulary.md +++ /dev/null @@ -1,98 +0,0 @@ - -Vocabulary for AFB-DAEMON -========================= - -## Binding - -A shared library object intended to add a functionality to an afb-daemon -instance. It implements an API and may provide a service. - -Binding made for services can have specific entry point called after -initialisation and before serving. - -## Event - -Message with data propagated from the services to the client and not expecting -any reply. - -The current implementation allows to widely broadcast events to all clients. - -## Level of assurance (LOA) - -This level that can be from 0 to 3 represent the level of -assurance that the services can expect from the session. - -The exact definition of the meaning of these levels and how to use it remains to -be achieved. - -## Plugin - -Old name for binding, see binding. - -## Request - -A request is an invocation by a client to a binding method using a message -transferred through some protocol: HTTP, WebSocket, DBUS... and served by -***afb-daemon*** - -## Reply/Response - -This is a message sent to client as the result of the request. - -## Service - -Service are made of bindings running by their side on their binder. -It can serve many client. Each one attached to one session. - -The framework establishes connection between the services and -the clients. Using DBus currently but other protocols are considered. - -## Session - -A session is meant to be the unique instance context of a client, -which identify that instance across requests. - -Each session has an identifier. Session identifier generated by afb-daemon are -UUIDs. - -Internally, afb-daemon offers a mechanism to attach data to sessions. -When the session is closed or disappears, the data attached to that session -are freed. - -## Token - -The token is an identifier that the client must give to be authenticated. - -At start, afb-daemon get an initial token. This initial token must be presented -by incoming client to be authenticated. - -A token is valid only for a period. - -The token must be renewed periodically. When the token is renewed, afb-daemon -sends the new token to the client. - -Tokens generated by afb-daemon are UUIDs. - -## UUID - -It stand for Universal Unique IDentifier. - -It is designed to create identifier in a way that avoid has much as possible -conflicts. It means that if two different instances create an UUID, the -probability that they create the same UUID is very low, near to zero. - -## x-afb-reqid - -Argument name that can be used with HTTP request. When this argument is given, -it is automatically added to the "request" object of the answer. - -## x-afb-token - -Argument name meant to give the token without ambiguity. -You can also use the name **token** but it may conflicts with others arguments. - -## x-afb-uuid - -Argument name for giving explicitly the session identifier without ambiguity. -You can also use the name **uuid** but it may conflicts with others arguments. - diff --git a/doc/afb-events-guide.md b/doc/afb-events-guide.md deleted file mode 100644 index aaa09be1..00000000 --- a/doc/afb-events-guide.md +++ /dev/null @@ -1,475 +0,0 @@ -Guide for developing with events -================================ - -Signaling agents are services that send events to any clients that -subscribed for receiving it. The sent events carry any data. - -To have a good understanding of how to write a signaling agent, the -actions of subscribing, unsubscribing, producing, sending and receiving -events must be described and explained. - -Overview of events ------------------- - -The basis of a signaling agent is shown in the following figure: - -![scenario of using events](pictures/signaling-basis.svg) - -This figure shows the main role of the signaling framework for the events -propagation. - -For people not familiar with the framework, a signaling agent and -a “binding” are similar. - -### Subscribing and unsubscribing - -Subscribing is the action that makes a client able to receive data from a -signaling agent. Subscription must create resources for generating the data, and -for delivering the data to the client. These two aspects are not handled by the -same piece of software. Generating the data is the responsibility of the -developer of the signaling agent while delivering the data is handled by the -framework. - -When a client subscribes for data, the agent must: - -1. check that the subscription request is correct; -2. establish the computation chain of the required data, if not already - done; -3. create a named event for the computed data, if not already done; -4. ask the framework to establish the subscription to the event for the - request; -5. optionally give indications about the event in the reply to - the client. - -The first two steps are not involving the framework. They are linked to -the business logic of the binding. The request can be any description of -the requested data and the computing stream can be of any nature, this -is specific to the binding. - -As said before, the framework uses and integrates **libsystemd** and its event -loop. Within the framework, **libsystemd** is the standard API/library for -bindings expecting to setup and handle I/O, timer or signal events. - -Steps 3 and 4 are bound to the framework. - -The agent must create an object for handling the propagation of produced -data to its clients. That object is called “event” in the framework. An -event has a name that allows clients to distinguish it from other -events. - -Events are created using the ***afb\_daemon\_make\_event*** function -that takes the name of the event. Example: - -```C - event = afb_daemon_make_event(afb_daemon, name); -``` - -Once created, the event can be used either to push data to its -subscribers or to broadcast data to any listener. - -The event must be used to establish the subscription for the requesting -client. This is done using the ***afb\_req\_subscribe*** function -that takes the current request object and event and associates them -together. Example: - -```C - rc = afb_req_subscribe(afb_req, event); -``` - -When successful, this function make the connection between the event and -the client that emitted the request. The client becomes a subscriber of -the event until it unsubscribes or disconnects. The -***afb\_req\_subscribe*** function will fail if the client -connection is weak: if the request comes from a HTTP link. To receive -signals, the client must be connected. The AGL framework allows connections -using WebSocket. - -The name of the event is either a well known name or an ad hoc name -forged for the use case. - -Let's see a basic example: client A expects to receive the speed in km/h -every second while client B expects the speed in mph twice a second. In -that case, there are two different events because it is not the same -unit and it is not the same frequency. Having two different events -allows to associate clients to the correct event. But this doesn't tell -any word about the name of these events. The designer of the signaling -agent has two options for naming: - -1. names can be the same (“speed” for example) with sent data - self describing itself or having a specific tag (requiring from - clients awareness about requesting both kinds of speed isn't safe). -2. names of the event include the variations (by example: - “speed-km/h-1Hz” and “speed-mph-2Hz”) and, in that case, sent data - can self describe itself or not. - -In both cases, the signaling agent might have to send the name of the -event and/or an associated tag to its client in the reply of the -subscription. This is part of the step 5 above. - -The framework only uses the event (not its name) for subscription, -unsubscription and pushing. - -When the requested data is already generated and the event used for -pushing it already exists, the signaling agent must not instantiate a -new processing chain and must not create a new event object for pushing -data. The signaling agent must reuse the existing chain and event. - -Unsubscribing is made by the signaling agent on a request of its client. -The ***afb\_req\_unsubscribe*** function tells the framework to -remove the requesting client from the event's list of subscribers. -Example: - -```C - afb_req_unsubscribe(afb_req, event); -``` - -Subscription count does not matter to the framework: subscribing the -same client several times has the same effect that subscribing only one -time. Thus, when unsubscribing is invoked, it becomes immediately -effective. - -#### More on naming events - -Within the AGL framework, a signaling agent is a binding that has an API -prefix. This prefix is meant to be unique and to identify the binding -API. The names of the events that this signaling agent creates are -automatically prefixed by the framework, using the API prefix of the -binding. - -Thus, if a signaling agent of API prefix ***api*** creates an event -of name ***event*** and pushes data to that event, the subscribers -will receive an event of name ***api/event***. - -### Generating and pushing signals and data - -This of the responsibility of the designer of the signaling agent to -establish the processing chain for generating events. In many cases, -this can be achieved using I/O or timer or signal events inserted in the -main loop. For this case, the AGL framework uses **libsystemd** and -provide a way to integrates to the main loop of this library using -afb\_daemon\_get\_event\_loop. Example: - -```C - sdev = afb_daemon_get_event_loop(af_daemon); - rc = sd_event_add_io(sdev, &source, fd, EPOLLIN, myfunction, NULL); -``` - -In some other cases, the events are coming from D-Bus. In that case, the -framework also uses **libsystemd** internally to access D-Bus. It provides -two methods to get the available D-Bus objects, already existing and -bound to the main**libsystemd**event loop. Use either -***afb\_daemon\_get\_system\_bus*** or -***afb\_daemon\_get\_user\_bus*** to get the required instance. Then -use functions of **libsystemd** to handle D-Bus. - -In some rare cases, the generation of the data requires to start a new -thread. - -When a data is generated and ready to be pushed, the signaling agent -should call the function ***afb\_event\_push***. Example: - -```C - rc = afb_event_push(event, JSON); - if (rc == 0) { - stop_generating(event); - afb_event_drop(event); - } -``` - -The function ***afb\_event\_push*** pushes json data to all the -subscribers. It then returns the count of subscribers. When the count is -zero, there is no subscriber listening for the event. The example above -shows that in that case, the signaling agent stops to generate data for -the event and delete the event using afb\_event\_drop. This is one -possible option. Other valuable options are: do nothing and continue to -generate and push the event or just stop to generate and push the data -but keep the event existing. - -### Receiving the signals - -Understanding what a client expects when it receives signals, events or -data shall be the most important topic of the designer of a signaling -agent. The good point here is that because JSON[^1] is the exchange -format, structured data can be sent in a flexible way. - -The good design is to allow as much as possible the client to describe -what is needed with the goal to optimize the processing to the -requirements only. - -### The exceptional case of wide broadcast - -Some data or events have so much importance that they can be widely -broadcasted to alert any listening client. Examples of such an alert -are: - -- system is entering/leaving “power safe” mode -- system is shutting down -- the car starts/stops moving -- ... - -An event can be broadcasted using one of the two following methods: -***afb\_daemon\_broadcast\_event*** or -***afb\_event\_broadcast***. - -Example 1: - -```C - afb_daemon_broadcast_event(afb_daemon, name, json); -``` - -Example 2: - -```C - event = afb_daemon_make_event(afb_daemon, name); - . . . . - afb_event_broadcast(event, json); -``` - -As for other events, the name of events broadcasted using -***afb\_daemon\_broadcast\_event*** are automatically prefixed by -the framework with API prefix of the binding (signaling agent). - -Reference of functions ----------------------- - -### Function afb\_event afb\_daemon\_make\_event - -The function ***afb\_daemon\_make\_event*** that is defined as below: - -```C -/* - * Creates an event of 'name' and returns it. - * 'daemon' MUST be the daemon given in interface when activating the binding. - */ -struct afb_event afb_daemon_make_event(struct afb_daemon daemon, const char *name); -``` - -The daemon is the handler to the application framework binder daemon -received during initialisation steps of the binding. - -Calling the function ***afb\_daemon\_make\_event*** within the initialisation -function ***afbBindingV1Register*** will _fail_ because the binding -name is not known at this time. - -The correct way to create the event at initialisation is to call the function -***afb\_daemon\_make\_event*** within the initialisation -function ***afbBindingV1ServiceInit***. - -### Function afb\_event\_push - -The function ***afb\_event\_push*** is defined as below: - -```C -/* - * Pushes the 'event' with the data 'object' to its observers. - * 'object' can be NULL. - * - * For convenience, the function calls 'json_object_put' for object'. - * Thus, in the case where 'object' should remain available after - * the function returns, the function 'json_object_get' shall be used. - * - * Returns the count of clients that received the event. - */ -int afb_event_push(struct afb_event event, struct json_object *object); -``` - -As the function ***afb\_event\_push*** returns 0 when there is no -more subscriber, a binding can remove such unexpected event using the -function ***afb\_event\_drop***. - -### Function afb\_event\_drop - -The function ***afb\_event\_drop*** is defined as below: - -```C -/* - * Drops the data associated to the event - * After calling this function, the event - * MUST NOT BE USED ANYMORE. - */ -void afb_event_drop(struct afb_event event); -``` - -### Function afb\_req\_subscribe - -The function ***afb\_req\_subscribe*** is defined as below: - -```C -/* - * Establishes for the client link identified by 'req' a subscription - * to the 'event'. - * Returns 0 in case of successful subscription or -1 in case of error. - */ -int afb_req_subscribe(struct afb_req req, struct afb_event event); -``` - -The subscription adds the client of the request to the list of subscribers -to the event. - -### Function afb\_req\_unsubscribe - -The function ***afb\_req\_unsubscribe*** is defined as -below: - -```C -/* - * Revokes the subscription established to the 'event' for the client - * link identified by 'req'. - * Returns 0 in case of successful unsubscription or -1 in case of error. - */ -int afb_req_unsubscribe(struct afb_req req, struct afb_event event); -``` - -The unsubscription removes the client of the request of the list of subscribers -to the event. -When the list of subscribers to the event becomes empty, -the function ***afb\_event\_push*** will return zero. - -### Function afb\_event\_broadcast - -The function ***afb\_event\_broadcast*** is defined as below: - -```C -/* - * Broadcasts widely the 'event' with the data 'object'. - * 'object' can be NULL. - * - * For convenience, the function calls 'json_object_put' for 'object'. - * Thus, in the case where 'object' should remain available after - * the function returns, the function 'json_object_get' shall be used. - * - * Returns the count of clients that received the event. - */ -int afb_event_broadcast(struct afb_event event, struct json_object *object); -``` - -This uses an existing event (created with ***afb\_daemon\_make\_event***) -for broadcasting an event having its name. - - -### Function afb\_daemon\_broadcast\_event - -The function ***afb\_daemon\_broadcast\_event*** is defined as below: - -```C -/* - * Broadcasts widely the event of 'name' with the data 'object'. - * 'object' can be NULL. - * 'daemon' MUST be the daemon given in interface when activating the binding. - * - * For convenience, the function calls 'json_object_put' for 'object'. - * Thus, in the case where 'object' should remain available after - * the function returns, the function 'json_object_get' shall be used. - * - * Returns the count of clients that received the event. - */ -int afb_daemon_broadcast_event(struct afb_daemon daemon, const char *name, struct json_object *object); -``` - -The name is given here explicitly. The name is automatically prefixed -with the name of the binding. For example, a binding of prefix "xxx" -would broadcat the event "xxx/name". - -### Function afbBindingV1ServiceEvent - -Binding can implement function **afbBindingV1ServiceEvent** which will -be called when an event is broadcasted or if service subscribed to an event. -That allow a service to react to an event and do what it is to do if this is -relevant for it (ie: car back camera detects imminent collision and broadcast -it, then appropriate service enable parking brake.). Here is the -**afbBindingV1ServiceEvent** definition: - -```C -/* - * When a binding have an implementation of the function 'afbBindingV1ServiceEvent', - * defined below, the framework calls that function for any broadcasted event or for - * events that the service subscribed to in its name. - * - * It receive the 'event' name and its related data in 'object' (be aware that 'object' - * might be NULL). - */ -extern void afbBindingV1ServiceEvent(const char *event, struct json_object *object); - -The binding *tic-tac-toe* broadcasts events when the board changes. -This is done in the function **changed**: -``` - -Architectural digressions -------------------------- - -Based on their dependencies to hardware, signaling agents can be split -into 2 categories: low-level signaling agents and high-level signaling -agents. - -Low-level signaling agents are bound to the hardware and focused on -interfacing and driving. - -High-level signaling agent are independent of the hardware and focused on -providing service. - -This separation (that may in the corner look artificial) aim to help in -the systems design. The main idea here is that high-level signaling -agents are providing “business logic”, also known as “application -logic”, that is proper to the car industry and that can be reused and -that can evolve as a foundation for the future of the industry. - -The implementation of this decomposition may follow 2 paths: strict -separation or soft composition. - -### Strict separation - -The strict separation implements the modularity composition of signaling -agent through the framework. The high-level signaling agent subscribes -to the low level signaling agent using the standard client API. - -Advantages: - -- Modularity -- Separation of responsibilities -- Possible aggregation of multiple sources -- Soft binding of agent good for maintenance - -Drawbacks: - -- Cost of propagation of data (might serialize) -- Difficulties to abstract low-level signaling agent or to find a - trade-off between abstracting and specializing - -The key is modularity versus cost of propagation. It can be partly -solved when logical group of signaling agent are launched together in -the same binder process. In that particular case, the cost of -propagation of data between agents is reduced[^2] because there is no -serialization. - -This reduction of the propagation cost (and of the resources used) -precludes implementation of strong security between the agents because -they share the same memory. - -### Soft composition - -The soft composition implements the business logic of high-level -signaling agents as libraries that can then be used directly by the low -level signaling agents. - -Advantages: - -- No propagation: same memory, sharing of native structures - -Drawbacks: - -- Cannot be used for aggregation of several sources -- Difficulties to abstract low-level signaling agent or to find a - trade-off between abstracting and specializing -- Source code binding not good for maintenance - -[^1]: There are two aspect in using JSON: the first is the flexible data - structure that mixes common types (booleans, numbers, strings, - arrays, dictionaries, nulls), the second, is the streaming - specification. Streaming is often seen as the bottleneck of using - JSON (see http://bjson.org). When the agent share the same process, - there is no streaming at all. - -[^2]: Within the same process, there is not serialization, the - propagation has the cost of wrapping a json data and calling - callbacks with the benefit of having a powerful callback manager: - the event mechanism of the framework. diff --git a/doc/afb-overview.md b/doc/afb-overview.md deleted file mode 100644 index eec3f91a..00000000 --- a/doc/afb-overview.md +++ /dev/null @@ -1,315 +0,0 @@ - -Overview of AFB-DAEMON -====================== - -Roles of afb-daemon -------------------- - -The name **afb-daemon** stands for *Application -Framework Binder Daemon*. That is why afb-daemon -is also named ***the binder***. - -**Afb-daemon** is in charge to bind one instance of -an application to the AGL framework and AGL system. - -On the following figure, you can use a typical use -of afb-daemon: - -

Figure: binder afb-daemon, basis

- -![binder-basis][binder-basis] - -The application and its companion binder run in secured and isolated -environment set for them. Applications are intended to access to AGL -system through the binder. - -The binder afb-daemon serves multiple purposes: - -1. It acts as a gateway for the application to access the system; - -2. It acts as an HTTP server for serving files to HTML5 applications; - -3. It allows HTML5 applications to have native extensions subject -to security enforcement for accessing hardware resources or -for speeding parts of algorithm. - -Use cases of the binder afb-daemon ----------------------------------- - -This section tries to give a better understanding of the binder -usage through several use cases. - -### Remotely running application - -One of the most interesting aspect of using the binder afb-daemon -is the ability to run applications remotely. This feature is -possible because the binder afb-daemon implements native web -protocols. - -So the [figure binder, basis](#binder-fig-basis) would become -when the application is run remotely: - -

Figure: binder afb-daemon and remotely running application

- - -### Adding native features to HTML5/QML applications - -Applications can provide with their packaged delivery a binding. -That binding will be instantiated for each application instance. -The methods of the binding will be accessible by applications and -will be executed within the security context. - -### Offering services to the system - -It is possible to run the binder afb-daemon as a daemon that provides the -API of its bindings. - -This will be used for: - -1. offering common APIs - -2. provide application's services (services provided as application) - -In that case, the figure showing the whole aspects is - -

Figure: binder afb-daemon for services

- -![afb-for-services][afb-for-services] - -For this case, the binder afb-daemon takes care to attribute one single session -context to each client instance. It allows bindings to store and retrieve data -associated to each of its client. - -The bindings of the binder afb-daemon ------------------------------------- - -The binder can instantiate bindings. The primary use of bindings -is to add native methods that can be accessed by applications -written with any language through web technologies ala JSON RPC. - -This simple idea is declined to serves multiple purposes: - -1. add native feature to applications - -2. add common API available by any applications - -3. provide customers services - -A specific document explains how to write an afb-daemon binder binding: -[HOWTO WRITE a BINDING for AFB-DAEMON](afb-bindings-writing.html) - - -Launching the binder afb-daemon -------------------------------- - -The launch options for binder **afb-daemon** are: - - --help - - Prints help with available options - - --version - - Display version and copyright - - --verbose - - Increases the verbosity, can be repeated - - --quiet - - Decreases the verbosity, can be repeated - - --port=xxxx - - HTTP listening TCP port [default 1234] - - --workdir=xxxx - - Directory where the daemon must run [default: $PWD if defined - or the current working directory] - - --uploaddir=xxxx - - Directory where uploaded files are temporarily stored [default: workdir] - - --rootdir=xxxx - - Root directory of the application to serve [default: workdir] - - --roothttp=xxxx - - Directory of HTTP served files. If not set, files are not served - but apis are still accessibles. - - --rootbase=xxxx - - Angular Base Root URL [default /opa] - - This is used for any application of kind OPA (one page application). - When set, any missing document whose url has the form /opa/zzz - is translated to /opa/#!zzz - - --rootapi=xxxx - - HTML Root API URL [default /api] - - The bindings are available within that url. - - --alias=xxxx - - Maps a path located anywhere in the file system to the - a subdirectory. The syntax for mapping a PATH to the - subdirectory NAME is: --alias=/NAME:PATH. - - Example: --alias=/icons:/usr/share/icons maps the - content of /usr/share/icons within the subpath /icons. - - This option can be repeated. - - --no-httpd - - Tells to not start the HTTP server. - - --apitimeout=xxxx - - binding API timeout in seconds [default 20] - - Defines how many seconds maximum a method is allowed to run. - 0 means no limit. - - --cntxtimeout=xxxx - - Client Session Timeout in seconds [default 3600] - - --cache-eol=xxxx - - Client cache end of live [default 100000 that is 27,7 hours] - - --session-max=xxxx - - Maximum count of simultaneous sessions [default 10] - - --ldpaths=xxxx - - Load bindings from given paths separated by colons - as for dir1:dir2:binding1.so:... [default = $libdir/afb] - - You can mix path to directories and to bindings. - The sub-directories of the given directories are searched - recursively. - - The bindings are the files terminated by '.so' (the extension - so denotes shared object) that contain the public entry symbol. - - --binding=xxxx - - Load the binding of given path. - - --token=xxxx - - Initial Secret token to authenticate. - - If not set, no client can authenticate. - - If set to the empty string, then any initial token is accepted. - - --random-token - - Generate a random starting token. See option --exec. - - --mode=xxxx - - Set the mode: either local, remote or global. - - The mode indicate if the application is run locally on the host - or remotely through network. - - --readyfd=xxxx - - Set the #fd to signal when ready - - If set, the binder afb-daemon will write "READY=1\n" on the file - descriptor whose number if given (/proc/self/fd/xxx). - - --dbus-client=xxxx - - Transparent binding to a binder afb-daemon service through dbus. - - It creates an API of name xxxx that is implemented remotely - and queried via DBUS. - - --dbus-server=xxxx - - Provides a binder afb-daemon service through dbus. - - The name xxxx must be the name of an API defined by a binding. - This API is exported through DBUS. - - --ws-client=xxxx - - Transparent binding to a binder afb-daemon service through a WebSocket. - - The value of xxxx is either a unix naming socket, of the form "unix:path/api", - or an internet socket, of the form "host:port/api". - - --ws-server=xxxx - - Provides a binder afb-daemon service through WebSocket. - - The value of xxxx is either a unix naming socket, of the form "unix:path/api", - or an internet socket, of the form "host:port/api". - - --foreground - - Get all in foreground mode (default) - - --daemon - - Get all in background mode - - --no-httpd - - Forbids HTTP serve - - --exec - - Must be the last option for afb-daemon. The remaining - arguments define a command that afb-daemon will launch. - The sequences @p, @t and @@ of the arguments are replaced - with the port, the token and @. - - --tracereq=xxxx - - Trace the processing of requests in the log file. - - Valid values are 'no' (default), 'common', 'extra' or 'all'. - - - - - -Future development of afb-daemon --------------------------------- - -- The binder afb-daemon would launch the applications directly. - -- The current setting of mode (local/remote/global) might be reworked to a -mechanism for querying configuration variables. - -- Implements "one-shot" initial token. It means that after its first -authenticated use, the initial token is removed and no client can connect -anymore. - -- Creates some intrinsic APIs. - -- Make the service connection using WebSocket not DBUS. - -- Management of targeted events. - -- Securing LOA. - -- Integration of the protocol JSON-RPC for the websockets. - -[binder-basis]: pictures/AFB_overview.svg -[afb-for-services]: pictures/AFB_for_services.svg diff --git a/doc/afb-tests-overview.md b/doc/afb-tests-overview.md deleted file mode 100644 index d6f619fe..00000000 --- a/doc/afb-tests-overview.md +++ /dev/null @@ -1,89 +0,0 @@ - -Overview of tests shipped with AFB-Daemon -========================================= - -List of tests -------------- - -Here are the tests shipped in the source tree: - -* **afb-client-demo** (command-line WebSockets) - -* **token-websock.qml** (Qt/QML WebSockets) - -* ***.html** (HTML5/JS HTTP-REST & WebSockets) - - -Detail of tests ---------------- - -### afb-client-demo (command-line WebSockets) - -This clients interactively calls bindings APIs from the command line, using the binder -[WebSockets](https://en.wikipedia.org/wiki/WebSocket) facility. - -If _afb-daemon_ has been launched with the following parameters: - - - $ afb-daemon --port=1234 --token=123456 [...] - - -Then run the client with : - - afb-client-demo ws://localhost:1234/api?token=123456 [ []] - -For instance, to initialize the Audio binding from the command line : - - afb-client-demo ws://localhost:1234/api?token=123456 - -The command doesn't return. You should type requests of type []. -So, try: - - auth connect - hello pingjson true - -
- - - -### token-websock.qml (Qt/QML WebSockets) - -If _afb-daemon_ has been launched with the following parameters: - - $ afb-daemon --port=1234 --token=123456 [...] - -and Qt5 is installed. - -For installing Qt5 on **Ubuntu 16.04**: - - $ apt-get install qmlscene qml-module-qtwebsockets qml-module-qtquick-controls - -For installing Qt5 on **Fedora >= 22** : - - $ dnf install qt5-qtdeclarative-devel qt5-qtwebsockets-devel qt5-qtquickcontrols - - -Then run the client with : - - qmlscene test/token-websock.qml - -and interactively press the buttons, "Connect", "Refresh", "Logout". - -
- - -### *.html (HTML5/JS HTTP-REST & WebSockets) - -If _afb-daemon_ has been launched with the following parameters: - - $ afb-daemon --port=1234 --rootdir=$PWD/test [...] - -_("$PWD/test_" being the "test" subdirectory of the source tree)_ - - -Then open your preferred Web browser, connect to the following URL: - - http://localhost:1234 - -and interactively run the various tests. - diff --git a/doc/index.md b/doc/index.md deleted file mode 120000 index 81c25433..00000000 --- a/doc/index.md +++ /dev/null @@ -1 +0,0 @@ -afb-overview.md \ No newline at end of file diff --git a/doc/pictures/AFB_for_services.svg b/doc/pictures/AFB_for_services.svg deleted file mode 100644 index 6e536c50..00000000 --- a/doc/pictures/AFB_for_services.svg +++ /dev/null @@ -1,238 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Isolated security context - - - - - - - - APPLICATION - - - - - - - - binderAFB-DAEMON - - - - - - - - BINDINGS - - - - - - - - - - - - - - - - - - - - - D-Bus & CYNARA - - - - - - - - Isolated security context A - - - - - - - - binderAFB-DAEMON - - - - - - - - serviceBINDINGS A - - - - - - - - - - - - - - - Isolated security context B - - - - - - - - binderAFB-DAEMON - - - - - - - - serviceBINDINGS B - - - - - - - - - - - - - - - Isolated security context C - - - - - - - - binderAFB-DAEMON - - - - - - - - serviceBINDINGS C - - - - - - - - - - - - - - \ No newline at end of file diff --git a/doc/pictures/AFB_overview.svg b/doc/pictures/AFB_overview.svg deleted file mode 100644 index 240dae89..00000000 --- a/doc/pictures/AFB_overview.svg +++ /dev/null @@ -1,140 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Isolated security context - - - - - - - - APPLICATION - - - - - - - - binderAFB-DAEMON - - - - - - - - BINDINGS - - - - - - AGL SYSTEM - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/doc/pictures/signaling-basis.svg b/doc/pictures/signaling-basis.svg deleted file mode 100644 index b13fcf17..00000000 --- a/doc/pictures/signaling-basis.svg +++ /dev/null @@ -1,145 +0,0 @@ - - - - - - - request-data - - - - - - client 1 - - - - - - - - client 2 - - - - - - - - : framework - - - - - - - - signaling agent - - - - - - - - - - - request-data - - - - - - afb_daemon_make_event - - - - - - - - - afb_req_subscribe - - - - - reply of request-data - - - - - - afb_req_subscribe - - - - - reply of request-data - - - - - - device - - - - - - - - - setup - - - - - << wake up >> - - - - - afb_event_push - - - - - << event >> - - - - - << event >> - - - - - reply of afb_event_push - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/doc/pictures/tic-tac-toe.svg b/doc/pictures/tic-tac-toe.svg deleted file mode 100644 index 7a5fb84e..00000000 --- a/doc/pictures/tic-tac-toe.svg +++ /dev/null @@ -1,289 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Client A - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Client B - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Tic-Tac-Toe - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - move - - - - - - wait - - - - - - success of move - - - - - - success of wait - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/doc/pictures/triskel_iot_bzh.svg b/doc/pictures/triskel_iot_bzh.svg deleted file mode 100644 index 096f4244..00000000 --- a/doc/pictures/triskel_iot_bzh.svg +++ /dev/null @@ -1,110 +0,0 @@ - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - diff --git a/docs/FAQ.md b/docs/FAQ.md new file mode 100644 index 00000000..5063ed74 --- /dev/null +++ b/docs/FAQ.md @@ -0,0 +1,5 @@ +Frequently Asked Question about AFB-DAEMON +========================================== + + + diff --git a/docs/afb-application-writing.md b/docs/afb-application-writing.md new file mode 100644 index 00000000..cc513b48 --- /dev/null +++ b/docs/afb-application-writing.md @@ -0,0 +1,301 @@ +How to write an application on top of AGL FRAMEWORK +==================================================== + +Programming Languages for Applications +----------------------------------------- + +### Writing an HTML5 application + +Developers of HTML5 applications (client side) can easily create +applications for AGL framework using their preferred +HTML5 framework. + +Developers may also take advantage of powerful server side bindings to improve +application behavior. Server side bindings return an application/json mine-type +and can be accessed though either HTTP or Websockets. + +In a near future, JSON-RPC protocol should be added to complete the current +x-afb-json1 protocol. + +Two examples of HTML5 applications are given: + +- [afb-client](https://gerrit.automotivelinux.org/gerrit/gitweb?p=src/app-framework-demo.git;a=tree;f=afb-client) a simple "hello world" application template + +- [afm-client](https://gerrit.automotivelinux.org/gerrit/gitweb?p=src/app-framework-demo.git;a=tree;f=afm-client) a simple "Home screen" application template + +### Writing a Qt application + +Writing Qt applications is also supported. Qt offers standard API to send +request through HTTP or WebSockets. + +It is also possible to write QML applications. A sample QML application +[token-websock] is available: + +- [token-websock](https://gerrit.automotivelinux.org/gerrit/gitweb?p=src/app-framework-binder.git;a=blob;f=test/token-websock.qml) +a simple "hello world" application in QML + +### Writing a "C" application + +C applications can use afb-daemon binder through a websocket connection. + +The library **libafbwsc** is provided for C clients that need +to connect with an afb-daemon binder. + +The program **afb-client-demo** is the C example that use +**libafbwsc** library. +Source code is available here +[src/afb-client-demo.c](https://gerrit.automotivelinux.org/gerrit/gitweb?p=src/app-framework-binder.git;a=blob;f=src/afb-client-demo.c). + +Current implementation relies on libsystemd and file descriptors. +This model might be review in the future to support secure sockets +and get rid of libsystemd dependency. + +Handling sessions within applications +------------------------------------- + +Applications should understand sessions and token management when interacting +with afb-daemon binder. + +Applications communicate with their private binder(afb-daemon) using +a network connection or potentially any other connection channel. While the +current version does not yet implement Unix socket, this feature might be added +in the near future. Developers need to be warn that HTTP protocol is a none +connected protocol and that using HTTP socket connection to authenticate +clients is not supported. + +For this reason, the binder should authenticate the application +by using a shared secret. The secret is named "token" and the identification +of client is named "session". + +The examples **token-websock.qml** and **afb-client** are demonstrating +how authentication and sessions are managed. + +### Handling sessions + +Bindings and other binder features need to keep track of client +instances. This is especially important for bindings running as services +as they may typically have to keep each client's data separated. + +For HTML5 applications, the web runtime handles the cookie of session +that the binder afb-daemon automatically sets. + +Session identifier can be set using the parameter **uuid** or **x-afb-uuid** in +URI requests. Within current version of the framework session UUID is supported +by both HTTP requests and websocket negotiation. + +### Exchanging tokens + +At application start, AGL framework communicates a shared secret to both binder +and client application. This initial secret is called the "**initial token**". + +For each of its client application, the binder manages a current active +token for session management. This authentication token can be use to restrict +the access to some binding's methods. + +The token must be included in URI request on HTTP or during websockets +connection using parameter **token** or **x-afb-token**. + +To ensure security, tokens must be refreshed periodically. + +### Example of session management + +In following examples, we suppose that **afb-daemon** is launched with something +equivalent to: + + $ afb-daemon --port=1234 --token=123456 [...] + +making the expectation that **AuthLogin** binding is requested as default. + +#### Using curl + +First, connects with the initial token, 123456: + + $ curl http://localhost:1234/api/auth/connect?token=123456 + { + "jtype": "afb-reply", + "request": { + "status": "success", + "token": "0aef6841-2ddd-436d-b961-ae78da3b5c5f", + "uuid": "850c4594-1be1-4e9b-9fcc-38cc3e6ff015" + }, + "response": {"token": "A New Token and Session Context Was Created"} + } + +It returns an answer containing session UUID, 850c4594-1be1-4e9b-9fcc-38cc3e6ff015, +and a refreshed token, 850c4594-1be1-4e9b-9fcc-38cc3e6ff015. + +Check if session and token is valid: + + $ curl http://localhost:1234/api/auth/check?token=0aef6841-2ddd-436d-b961-ae78da3b5c5f\&uuid=850c4594-1be1-4e9b-9fcc-38cc3e6ff015 + { + "jtype": "afb-reply", + "request": {"status":"success"}, + "response": {"isvalid":true} + } + +Refresh the token: + + $ curl http://localhost:1234/api/auth/refresh?token=0aef6841-2ddd-436d-b961-ae78da3b5c5f\&uuid=850c4594-1be1-4e9b-9fcc-38cc3e6ff015 + { + "jtype": "afb-reply", + "request": { + "status":"success", + "token":"b8ec3ec3-6ffe-448c-9a6c-efda69ad7bd9" + }, + "response": {"token":"Token was refreshed"} + } + +Close the session: + + curl http://localhost:1234/api/auth/logout?token=b8ec3ec3-6ffe-448c-9a6c-efda69ad7bd9\&uuid=850c4594-1be1-4e9b-9fcc-38cc3e6ff015 + { + "jtype": "afb-reply", + "request": {"status": "success"}, + "response": {"info":"Token and all resources are released"} + } + +Checking on closed session for uuid should be refused: + + curl http://localhost:1234/api/auth/check?token=b8ec3ec3-6ffe-448c-9a6c-efda69ad7bd9\&uuid=850c4594-1be1-4e9b-9fcc-38cc3e6ff015 + { + "jtype": "afb-reply", + "request": { + "status": "failed", + "info": "invalid token's identity" + } + } + +#### Using afb-client-demo + +> The program is packaged within AGL in the rpm **libafbwsc-dev** + +Here is an example of exchange using **afb-client-demo**: + + $ afb-client-demo ws://localhost:1234/api?token=123456 + auth connect + ON-REPLY 1:auth/connect: {"jtype":"afb-reply","request":{"status":"success", + "token":"63f71a29-8b52-4f9b-829f-b3028ba46b68","uuid":"5fcc3f3d-4b84-4fc7-ba66-2d8bd34ae7d1"}, + "response":{"token":"A New Token and Session Context Was Created"}} + auth check + ON-REPLY 2:auth/check: {"jtype":"afb-reply","request":{"status":"success"},"response":{"isvalid":true}} + auth refresh + ON-REPLY 4:auth/refresh: {"jtype":"afb-reply","request":{"status":"success", + "token":"8b8ba8f4-1b0c-48fa-962d-4a00a8c9157e"},"response":{"token":"Token was refreshed"}} + auth check + ON-REPLY 5:auth/check: {"jtype":"afb-reply","request":{"status":"success"},"response":{"isvalid":true}} + auth refresh + ON-REPLY 6:auth/refresh: {"jtype":"afb-reply","request":{"status":"success", + "token":"e83b36f8-d945-463d-b983-5d8ed73ba529"},"response":{"token":"Token was refreshed"}} + +After closing connection, reconnect as here after: + + $ afb-client-demo ws://localhost:1234/api?token=e83b36f8-d945-463d-b983-5d8ed73ba529\&uuid=5fcc3f3d-4b84-4fc7-ba66-2d8bd34ae7d1 auth check + ON-REPLY 1:auth/check: {"jtype":"afb-reply","request":{"status":"success"},"response":{"isvalid":true}} + +Same connection check using **curl**: + + $ curl http://localhost:1234/api/auth/check?token=e83b36f8-d945-463d-b983-5d8ed73ba529\&uuid=5fcc3f3d-4b84-4fc7-ba66-2d8bd34ae7d1 + {"jtype":"afb-reply","request":{"status":"success"},"response":{"isvalid":true}} + +Format of replies +----------------- + +Replies use javascript object returned as serialized JSON. + +This object contains at least 2 mandatory fields of name **jtype** and +**request** and one optional field of name **response**. + +### Template + +This is a template of replies: + +```json +{ + "jtype": "afb-reply", + "request": { + "status": "success", + "info": "informationnal text", + "token": "e83b36f8-d945-463d-b983-5d8ed73ba52", + "uuid": "5fcc3f3d-4b84-4fc7-ba66-2d8bd34ae7d1", + "reqid": "application-generated-id-23456" + }, + "response": ....any response object.... +} +``` + +### Field jtype + +The field **jtype** must have a value of type string equal to **"afb-reply"**. + +### Field request + +The field **request** must have a value of type object. This request object +has at least one field named **status** and four optional fields named +**info**, **token**, **uuid**, **reqid**. + +#### Subfield request.status + +**status** must have a value of type string. This string is equal to **"success"** +only in case of success. + +#### Subfield request.info + +**info** is of type string and represent optional information added to the reply. + +#### Subfield request.token + +**token** is of type string. It is sent either at session creation +or when the token is refreshed. + +#### Subfield request.uuid + +**uuid** is of type string. It is sent at session creation. + +#### Subfield request.reqid + +**reqid** is of type string. It is sent in response to HTTP requests +that added a parameter of name **reqid** or **x-afb-reqid** at request time. +Value returns in the reply has the exact same value as the one received in the +request. + +### Field response + +This field response optionally contains an object returned when request +succeeded. + +Format of events +---------------- + +Events are javascript object serialized as JSON. + +This object contains at least 2 mandatory fields of name **jtype** and **event** +and one optional field of name **data**. + +### Template + +Here is a template of event: + +```json +{ + "jtype": "afb-event", + "event": "sample_api_name/sample_event_name", + "data": ...any event data... +} +``` + +### Field jtype + +The field **jtype** must have a value of type string equal to **"afb-event"**. + +### Field event + +The field **event** carries the event's name. + +The name of the event is made of two parts separated by a slash: +the name of the name of the API that generated the event +and the name of event within the API. + +### Field data + +This field data if present holds the data carried by the event. + diff --git a/docs/afb-binding-references.md b/docs/afb-binding-references.md new file mode 100644 index 00000000..7d6cdd1d --- /dev/null +++ b/docs/afb-binding-references.md @@ -0,0 +1,672 @@ +Binding Reference +================= + +Structure for declaring binding +------------------------------- + +### struct afb_binding_v2 + +The main structure, of type **afb_binding_v2**, for describing the binding +must be exported under the name **afbBindingV2**. + +This structure is defined as below. + +```C +/* + * Description of the bindings of type version 2 + */ +struct afb_binding_v2 +{ + const char *api; /* api name for the binding */ + const char *specification; /* textual openAPIv3 specification of the binding */ + const struct afb_verb_v2 *verbs; /* array of descriptions of verbs terminated by a NULL name */ + int (*preinit)(); /* callback at load of the binding */ + int (*init)(); /* callback for starting the service */ + void (*onevent)(const char *event, struct json_object *object); /* callback for handling events */ + unsigned noconcurrency: 1; /* avoids concurrent requests to verbs */ +}; +``` + +### struct afb_verb_v2 + +Each verb is described with a structure of type **afb_verb_v2** +defined below: + +```C +/* + * Description of one verb of the API provided by the binding + * This enumeration is valid for bindings of type version 2 + */ +struct afb_verb_v2 +{ + const char *verb; /* name of the verb */ + void (*callback)(struct afb_req req); /* callback function implementing the verb */ + const struct afb_auth *auth; /* required authorisation */ + uint32_t session; /* authorisation and session requirements of the verb */ +}; +``` + +The session flags is an or of the constant defined below: + + - AFB_SESSION_NONE : no flag, synonym to 0 + - AFB_SESSION_LOA_0 : Requires the LOA to be 0 or more, synonym to 0 or AFB_SESSION_NONE + - AFB_SESSION_LOA_1 : Requires the LOA to be 1 or more + - AFB_SESSION_LOA_2 : Requires the LOA to be 2 or more + - AFB_SESSION_LOA_3 : Requires the LOA to be 3 or more + - AFB_SESSION_CHECK : Requires the token to be set and valid + - AFB_SESSION_REFRESH : Implies a token refresh + - AFB_SESSION_CLOSE : Implies cloing the session + +The LOA is set binding by binding using the function **afb_req_session_set_LOA**. + +### struct afb_auth and enum afb_auth_type + +The structure **afb_auth** is used within verb description to +set security requirements. The interpretation of the structure +depends on the value of the field **type**. + +```C +struct afb_auth +{ + const enum afb_auth_type type; + union { + const char *text; + const unsigned loa; + const struct afb_auth *first; + }; + const struct afb_auth *next; +}; +``` +The possible values for **type** is defined here: + +```C +/* + * Enum for Session/Token/Assurance middleware. + */ +enum afb_auth_type +{ + afb_auth_No = 0, /** never authorized, no data */ + afb_auth_Token, /** authorized if token valid, no data */ + afb_auth_LOA, /** authorized if LOA greater than data 'loa' */ + afb_auth_Permission, /** authorized if permission 'text' is granted */ + afb_auth_Or, /** authorized if 'first' or 'next' is authorized */ + afb_auth_And, /** authorized if 'first' and 'next' are authorized */ + afb_auth_Not, /** authorized if 'first' is not authorized */ + afb_auth_Yes /** always authorized, no data */ +}; +``` + +Example: + +```C +static const struct afb_auth _afb_auths_v2_monitor[] = { + { .type = afb_auth_Permission, .text = "urn:AGL:permission:monitor:public:set" }, + { .type = afb_auth_Permission, .text = "urn:AGL:permission:monitor:public:get" }, + { .type = afb_auth_Or, .first = &_afb_auths_v2_monitor[1], .next = &_afb_auths_v2_monitor[0] } +}; +``` + +Functions of class afb_daemon... +------------------------- + +The 3 following functions are linked to libsystemd. +They allow use of **sd_event** features and access +to **sd_bus** features. + +```C +/* + * Retrieves the common systemd's event loop of AFB + */ +struct sd_event *afb_daemon_get_event_loop(); + +/* + * Retrieves the common systemd's user/session d-bus of AFB + */ +struct sd_bus *afb_daemon_get_user_bus(); + +/* + * Retrieves the common systemd's system d-bus of AFB + */ +struct sd_bus *afb_daemon_get_system_bus(); +``` + +The 2 following functions are linked to event management. +Broadcasting an event send it to any possible listener. + +```C +/* + * Broadcasts widely the event of 'name' with the data 'object'. + * 'object' can be NULL. + * + * For convenience, the function calls 'json_object_put' for 'object'. + * Thus, in the case where 'object' should remain available after + * the function returns, the function 'json_object_get' shall be used. + * + * Returns the count of clients that received the event. + */ +int afb_daemon_broadcast_event(const char *name, struct json_object *object); + +/* + * Creates an event of 'name' and returns it. + * + * See afb_event_is_valid to check if there is an error. + */ +struct afb_event afb_daemon_make_event(const char *name); +``` + +The following function is used by logging macros and should normally +not be used. Instead, you should use the macros +**AFB\_ERROR**, **AFB\_WARNING**, **AFB\_NOTICE**, +**AFB\_INFO**, **AFB\_DEBUG** + +```C +/* + * Send a message described by 'fmt' and following parameters + * to the journal for the verbosity 'level'. + * + * 'file', 'line' and 'func' are indicators of position of the code in source files + * (see macros __FILE__, __LINE__ and __func__). + * + * 'level' is defined by syslog standard: + * EMERGENCY 0 System is unusable + * ALERT 1 Action must be taken immediately + * CRITICAL 2 Critical conditions + * ERROR 3 Error conditions + * WARNING 4 Warning conditions + * NOTICE 5 Normal but significant condition + * INFO 6 Informational + * DEBUG 7 Debug-level messages + */ +void afb_daemon_verbose(int level, const char *file, int line, const char * func, const char *fmt, ...); +``` + +The 2 following functions MUST be used to access data of the bindings. + +```C +/* + * Get the root directory file descriptor. This file descriptor can + * be used with functions 'openat', 'fstatat', ... + */ +int afb_daemon_rootdir_get_fd(); + +/* + * Opens 'filename' within the root directory with 'flags' (see function openat) + * using the 'locale' definition (example: "jp,en-US") that can be NULL. + * Returns the file descriptor or -1 in case of error. + */ +int afb_daemon_rootdir_open_locale(const char *filename, int flags, const char *locale); +``` + +The following function is used to queue jobs. + +```C +/* + * Queue the job defined by 'callback' and 'argument' for being executed asynchronously + * in this thread (later) or in an other thread. + * If 'group' is not NUL, the jobs queued with a same value (as the pointer value 'group') + * are executed in sequence in the order of there submission. + * If 'timeout' is not 0, it represent the maximum execution time for the job in seconds. + * At first, the job is called with 0 as signum and the given argument. + * The job is executed with the monitoring of its time and some signals like SIGSEGV and + * SIGFPE. When a such signal is catched, the job is terminated and reexecuted but with + * signum being the signal number (SIGALRM when timeout expired). + * + * Returns 0 in case of success or -1 in case of error. + */ +int afb_daemon_queue_job(void (*callback)(int signum, void *arg), void *argument, void *group, int timeout) +``` + +The following function must be used when a binding depends on other +bindings at its initialisation. + +```C +/* + * Tells that it requires the API of "name" to exist + * and if 'initialized' is not null to be initialized. + * Returns 0 in case of success or -1 in case of error. + */ +int afb_daemon_require_api(const char *name, int initialized) +``` + +Functions of class afb_service... +------------------------- + +The following functions allow services to call verbs of other +bindings for themselves. + +```C +/** + * Calls the 'verb' of the 'api' with the arguments 'args' and 'verb' in the name of the binding. + * The result of the call is delivered to the 'callback' function with the 'callback_closure'. + * + * For convenience, the function calls 'json_object_put' for 'args'. + * Thus, in the case where 'args' should remain available after + * the function returns, the function 'json_object_get' shall be used. + * + * The 'callback' receives 3 arguments: + * 1. 'closure' the user defined closure pointer 'callback_closure', + * 2. 'iserror' a boolean status being true (not null) when an error occured, + * 2. 'result' the resulting data as a JSON object. + * + * @param api The api name of the method to call + * @param verb The verb name of the method to call + * @param args The arguments to pass to the method + * @param callback The to call on completion + * @param callback_closure The closure to pass to the callback + * + * @see also 'afb_req_subcall' + */ +void afb_service_call( + const char *api, + const char *verb, + struct json_object *args, + void (*callback)(void*closure, int iserror, struct json_object *result), + void *callback_closure); + +/** + * Calls the 'verb' of the 'api' with the arguments 'args' and 'verb' in the name of the binding. + * 'result' will receive the response. + * + * For convenience, the function calls 'json_object_put' for 'args'. + * Thus, in the case where 'args' should remain available after + * the function returns, the function 'json_object_get' shall be used. + * + * @param api The api name of the method to call + * @param verb The verb name of the method to call + * @param args The arguments to pass to the method + * @param result Where to store the result - should call json_object_put on it - + * + * @returns 1 in case of success or 0 in case of error. + * + * @see also 'afb_req_subcall' + */ +int afb_service_call_sync( + const char *api, + const char *verb, + struct json_object *args, + struct json_object **result); +``` + +Functions of class afb_event... +------------------------- + +This function checks whether the event is valid. It must be used +when creating events. + +```C +/* + * Checks wether the 'event' is valid or not. + * + * Returns 0 if not valid or 1 if valid. + */ +int afb_event_is_valid(struct afb_event event); +``` + +The two following functions are used to broadcast or push +event with its data. + +```C +/* + * Broadcasts widely the 'event' with the data 'object'. + * 'object' can be NULL. + * + * For convenience, the function calls 'json_object_put' for 'object'. + * Thus, in the case where 'object' should remain available after + * the function returns, the function 'json_object_get' shall be used. + * + * Returns the count of clients that received the event. + */ +int afb_event_broadcast(struct afb_event event, struct json_object *object); + +/* + * Pushes the 'event' with the data 'object' to its observers. + * 'object' can be NULL. + * + * For convenience, the function calls 'json_object_put' for 'object'. + * Thus, in the case where 'object' should remain available after + * the function returns, the function 'json_object_get' shall be used. + * + * Returns the count of clients that received the event. + */ +int afb_event_push(struct afb_event event, struct json_object *object); +``` + +The following function destiys the event. + +```C +/* + * Drops the data associated to the 'event' + * After calling this function, the event + * MUST NOT BE USED ANYMORE. + */ +void afb_event_drop(struct afb_event event); +``` + +This function allows to retrieve the exact name of the event. + +```C +/* + * Gets the name associated to the 'event'. + */ +const char *afb_event_name(struct afb_event event); +``` + +Functions of class afb_req... +------------------------- + +This function checks the validity of the **req**. + +```C +/* + * Checks wether the request 'req' is valid or not. + * + * Returns 0 if not valid or 1 if valid. + */ +int afb_req_is_valid(struct afb_req req); +``` + +The following functions retrieves parameters of the request. + +```C +/* + * Gets from the request 'req' the argument of 'name'. + * Returns a PLAIN structure of type 'struct afb_arg'. + * When the argument of 'name' is not found, all fields of result are set to NULL. + * When the argument of 'name' is found, the fields are filled, + * in particular, the field 'result.name' is set to 'name'. + * + * There is a special name value: the empty string. + * The argument of name "" is defined only if the request was made using + * an HTTP POST of Content-Type "application/json". In that case, the + * argument of name "" receives the value of the body of the HTTP request. + */ +struct afb_arg afb_req_get(struct afb_req req, const char *name); + +/* + * Gets from the request 'req' the string value of the argument of 'name'. + * Returns NULL if when there is no argument of 'name'. + * Returns the value of the argument of 'name' otherwise. + * + * Shortcut for: afb_req_get(req, name).value + */ +const char *afb_req_value(struct afb_req req, const char *name); + +/* + * Gets from the request 'req' the path for file attached to the argument of 'name'. + * Returns NULL if when there is no argument of 'name' or when there is no file. + * Returns the path of the argument of 'name' otherwise. + * + * Shortcut for: afb_req_get(req, name).path + */ +const char *afb_req_path(struct afb_req req, const char *name); + +/* + * Gets from the request 'req' the json object hashing the arguments. + * The returned object must not be released using 'json_object_put'. + */ +struct json_object *afb_req_json(struct afb_req req); +``` + +The following functions emit the reply to the request. + +```C +/* + * Sends a reply of kind success to the request 'req'. + * The status of the reply is automatically set to "success". + * Its send the object 'obj' (can be NULL) with an + * informationnal comment 'info (can also be NULL). + * + * For convenience, the function calls 'json_object_put' for 'obj'. + * Thus, in the case where 'obj' should remain available after + * the function returns, the function 'json_object_get' shall be used. + */ +void afb_req_success(struct afb_req req, struct json_object *obj, const char *info); + +/* + * Same as 'afb_req_success' but the 'info' is a formatting + * string followed by arguments. + * + * For convenience, the function calls 'json_object_put' for 'obj'. + * Thus, in the case where 'obj' should remain available after + * the function returns, the function 'json_object_get' shall be used. + */ +void afb_req_success_f(struct afb_req req, struct json_object *obj, const char *info, ...); + +/* + * Same as 'afb_req_success_f' but the arguments to the format 'info' + * are given as a variable argument list instance. + * + * For convenience, the function calls 'json_object_put' for 'obj'. + * Thus, in the case where 'obj' should remain available after + * the function returns, the function 'json_object_get' shall be used. + */ +void afb_req_success_v(struct afb_req req, struct json_object *obj, const char *info, va_list args); + +/* + * Sends a reply of kind failure to the request 'req'. + * The status of the reply is set to 'status' and an + * informationnal comment 'info' (can also be NULL) can be added. + * + * Note that calling afb_req_fail("success", info) is equivalent + * to call afb_req_success(NULL, info). Thus even if possible it + * is strongly recommanded to NEVER use "success" for status. + */ +void afb_req_fail(struct afb_req req, const char *status, const char *info); + +/* + * Same as 'afb_req_fail' but the 'info' is a formatting + * string followed by arguments. + */ +void afb_req_fail_f(struct afb_req req, const char *status, const char *info, ...); + +/* + * Same as 'afb_req_fail_f' but the arguments to the format 'info' + * are given as a variable argument list instance. + */ +void afb_req_fail_v(struct afb_req req, const char *status, const char *info, va_list args); +``` + +The following functions handle the session data. + +```C +/* + * Gets the pointer stored by the binding for the session of 'req'. + * When the binding has not yet recorded a pointer, NULL is returned. + */ +void *afb_req_context_get(struct afb_req req); + +/* + * Stores for the binding the pointer 'context' to the session of 'req'. + * The function 'free_context' will be called when the session is closed + * or if binding stores an other pointer. + */ +void afb_req_context_set(struct afb_req req, void *context, void (*free_context)(void*)); + +/* + * Gets the pointer stored by the binding for the session of 'req'. + * If the stored pointer is NULL, indicating that no pointer was + * already stored, afb_req_context creates a new context by calling + * the function 'create_context' and stores it with the freeing function + * 'free_context'. + */ +void *afb_req_context(struct afb_req req, void *(*create_context)(), void (*free_context)(void*)); + +/* + * Frees the pointer stored by the binding for the session of 'req' + * and sets it to NULL. + * + * Shortcut for: afb_req_context_set(req, NULL, NULL) + */ +void afb_req_context_clear(struct afb_req req); + +/* + * Closes the session associated with 'req' + * and delete all associated contexts. + */ +void afb_req_session_close(struct afb_req req); + +/* + * Sets the level of assurance of the session of 'req' + * to 'level'. The effect of this function is subject of + * security policies. + * Returns 1 on success or 0 if failed. + */ +int afb_req_session_set_LOA(struct afb_req req, unsigned level); +``` + + +The 4 following functions must be used for asynchronous handling requests. + +```C +/* + * Adds one to the count of references of 'req'. + * This function MUST be called by asynchronous implementations + * of verbs if no reply was sent before returning. + */ +void afb_req_addref(struct afb_req req); + +/* + * Substracts one to the count of references of 'req'. + * This function MUST be called by asynchronous implementations + * of verbs after sending the asynchronous reply. + */ +void afb_req_unref(struct afb_req req); + +/* + * Stores 'req' on heap for asynchrnous use. + * Returns a handler to the stored 'req' or NULL on memory depletion. + * The count of reference to 'req' is incremented on success + * (see afb_req_addref). + */ +struct afb_stored_req *afb_req_store(struct afb_req req); + +/* + * Retrieves the afb_req stored at 'sreq'. + * Returns the stored request. + * The count of reference is UNCHANGED, thus, the + * function 'afb_req_unref' should be called on the result + * after that the asynchronous reply if sent. + */ +struct afb_req afb_req_unstore(struct afb_stored_req *sreq); +``` + +The two following functions are used to associate client with events +(subscription). + +```C +/* + * Establishes for the client link identified by 'req' a subscription + * to the 'event'. + * Returns 0 in case of successful subscription or -1 in case of error. + */ +int afb_req_subscribe(struct afb_req req, struct afb_event event); + +/* + * Revokes the subscription established to the 'event' for the client + * link identified by 'req'. + * Returns 0 in case of successful subscription or -1 in case of error. + */ +int afb_req_unsubscribe(struct afb_req req, struct afb_event event); +``` + +The following functions must be used to make request in the name of the +client (with its permissions). + +```C +/* + * Makes a call to the method of name 'api' / 'verb' with the object 'args'. + * This call is made in the context of the request 'req'. + * On completion, the function 'callback' is invoked with the + * 'closure' given at call and two other parameters: 'iserror' and 'result'. + * 'iserror' is a boolean that indicates if the reply is an error reply. + * 'result' is the json object of the reply, you must not call json_object_put + * on the result. + * + * For convenience, the function calls 'json_object_put' for 'args'. + * Thus, in the case where 'args' should remain available after + * the function returns, the function 'json_object_get' shall be used. + */ +void afb_req_subcall( + struct afb_req req, + const char *api, + const char *verb, + struct json_object *args, + void (*callback)(void *closure, int iserror, struct json_object *result), + void *closure); + +/* + * Makes a call to the method of name 'api' / 'verb' with the object 'args'. + * This call is made in the context of the request 'req'. + * This call is synchronous, it waits untill completion of the request. + * It returns 0 on an error answer and returns 1 when no error was detected. + * The object pointed by 'result' is filled and must be released by the caller + * after its use by calling 'json_object_put'. + * + * For convenience, the function calls 'json_object_put' for 'args'. + * Thus, in the case where 'args' should remain available after + * the function returns, the function 'json_object_get' shall be used. + */ +int afb_req_subcall_sync( + struct afb_req req, + const char *api, + const char *verb, + struct json_object *args, + struct json_object **result); +``` + +The following function is used by logging macros and should normally +not be used. Instead, you should use the macros +**AFB_REQ_ERROR**, **AFB_REQ_WARNING**, **AFB_REQ_NOTICE**, +**AFB_REQ_INFO**, **AFB_REQ_DEBUG** + +```C +/* + * Send associated to 'req' a message described by 'fmt' and following parameters + * to the journal for the verbosity 'level'. + * + * 'file', 'line' and 'func' are indicators of position of the code in source files + * (see macros __FILE__, __LINE__ and __func__). + * + * 'level' is defined by syslog standard: + * EMERGENCY 0 System is unusable + * ALERT 1 Action must be taken immediately + * CRITICAL 2 Critical conditions + * ERROR 3 Error conditions + * WARNING 4 Warning conditions + * NOTICE 5 Normal but significant condition + * INFO 6 Informational + * DEBUG 7 Debug-level messages + */ +void afb_req_verbose(struct afb_req req, int level, const char *file, int line, const char * func, const char *fmt, ...); +``` + +Logging macros +-------------- + +The following macros must be used for logging: + +```C +AFB_ERROR(fmt,...) +AFB_WARNING(fmt,...) +AFB_NOTICE(fmt,...) +AFB_INFO(fmt,...) +AFB_DEBUG(fmt,...) +``` + +The following macros can be used for logging in the context +of a request **req** of type **afb_req**: + +```C +AFB_REQ_ERROR(req,fmt,...) +AFB_REQ_WARNING(req,fmt,...) +AFB_REQ_NOTICE(req,fmt,...) +AFB_REQ_INFO(req,fmt,...) +AFB_REQ_DEBUG(req,fmt,...) +``` + +By default, the logging macros add file, line and function +indication. + +Some definitions can be used to + + diff --git a/docs/afb-binding-writing.md b/docs/afb-binding-writing.md new file mode 100644 index 00000000..dd9a8b56 --- /dev/null +++ b/docs/afb-binding-writing.md @@ -0,0 +1,465 @@ + +Overview of the bindings +======================== + +The ***binder*** serves files through HTTP protocol and offers to +developers the capability to offer application API methods through HTTP or +WebSocket protocol. + +The ***bindings*** are used to add **API** to ***binders***. +This part describes how to write a ***binding*** for ***binder*** +or in other words how to add a new **API** to the system. + +Excepting this summary, this section target developers. + +This section shortly explain how to write a binding +using the C programming language. + +It is convenient to install the ***binder*** on the +desktop used for writing the binding. It allows easy +debug and test. + +Nature of a binding +------------------- + +A ***binding*** is an independent piece of software compiled as a shared +library and dynamically loaded by a ***binder***. + +It is intended to provide one **API** (**A**pplication **P**rogramming +**I**nterface). + +The **API** is designated and accessed through its name. +It contains several **verbs** that implement the ***binding*** +functionnalities. Each of these **verbs** is a **method** that +processes requests of applications and sends result. + +The ***binding***'s methods is invoqued by HTTP or websocket +requests. + +The **methods** of the ***bindings*** are noted **api/verb** +where **api** is the **API** name of the binding and **verb** is +the **method**'s name within the **API**. +This notation comes from HTTP invocations that rely on URL path terminated +with **api/verb**. + +The name of an **API** can be made of any characters except: + + - the control characters (\u0000 .. \u001f) + - the characters of the set { ' ', '"', '#', '%', '&', + '\'', '/', '?', '`', '\x7f' } + +The names if the **verbs** can be any character. + +The binder mkes no distinctions between upper case and lower case +latin letters. So **API/VERB** matches **Api/Verb** or **api/verb**. + +Actually it exists 2 ways of writing ***bindings***. +You can either write: + + - a binding version 1 (not recommanded); + - a binding version 2 (RECOMMANDED). + +A ***binder*** loads and runs any of these version +in any combination. + +This document explain how to write bindings version 2. + + +Sample binding: tuto-1 +====================== + +This is the code of the binding **tuto-1.c**: + +```C + 1 #define AFB_BINDING_VERSION 2 + 2 #include + 3 + 4 void hello(afb_req req) + 5 { + 6 AFB_REQ_DEBUG(req, "hello world"); + 7 afb_req_success(req, NULL, "hello world"); + 8 } + 9 + 10 const afb_verb_v2 verbs[] = { + 11 { .verb="hello", .callback=hello }, + 12 { .verb=NULL } + 13 }; + 14 + 15 const afb_binding_v2 afbBindingV2 = { + 16 .api = "tuto-1", + 17 .verbs = verbs + 18 }; +``` + +Compiling: + +```bash +$ gcc -fPIC -shared tuto-1.c -o tuto-1.so $(pkg-config --cflags-only-I afb-daemon) +``` + +Running: + +```bash +$ afb-daemon --binding tuto-1.so --port 3333 --token '' +``` + +Testing using **curl**: + +```bash +$ curl http://localhost:3333/api/tuto-1/hello +{"jtype":"afb-reply","request":{"status":"success","info":"hello world","uuid":"1e587b54-900b-49ab-9940-46141bc2e1d6"}} +``` + +Testing using **afb-client-demo** (with option -H for +getting a human readable output): + +```bash +$ afb-client-demo -H ws://localhost:3333/api?token=x tuto-1 hello +ON-REPLY 1:tuto-1/hello: OK +{ + "jtype":"afb-reply", + "request":{ + "status":"success", + "info":"hello world", + "uuid":"03a84ad1-458a-4ace-af74-b1da917391b9" + } +} +``` + +This shows basic things: + + - The include to get for creating a binding + - How to declare the API offered by the binding + - How to handle request made to the binding + + +### Getting declarations for the binding + +The lines 1 and 2 show how to get the include file **afb-binding.h**. + +```C + 1 #define AFB_BINDING_VERSION 2 + 2 #include +``` + +You must define the version of ***binding*** that you are using. +This is done line 1 where we define that this is the version 2. + +If you don't define it, a warning message is prompted by the compiler +and the version is switched to version 1. This behaviour is +temporarily and enables to continue to use previously written +***binding*** without change but it will change in some future when +***bindings*** V1 will become obsoletes. + +To include **afb-binding.h** successfuly, the include search path +should be set correctly if needed (not needed only if installed in +/usr/include/afb directory that is the default). + +Setting the include path is easy using **pkg-config**: + +```bash +$ pkg-config --cflags-only-I afb-daemon +``` + +Note for **C++** developers: The ***binder*** currently expose +only **C** language **API**. The file **afb/afb-binding.h** +isn't **C++** ready. You should use the construct **extern "C"** +as below: + +```C + #define AFB_BINDING_VERSION 2 + extern "C" { + #include + } +``` + +Future version of the ***binder*** will include a **C++** +interface. Until it is available, please, use the above +construct. + +### Declaring the API of the binding + +Lines 10 to 18 show the declaration of the ***binding***. + +The ***binder*** knows that this is a ***binding*** version 2 because +it finds the exported symbol **afbBindingV2** that is expected to be +a structure of type **afb_binding_v2**. + +```C + 10 const afb_verb_v2 verbs[] = { + 11 { .verb="hello", .callback=hello }, + 12 { .verb=NULL } + 13 }; + 14 + 15 const afb_binding_v2 afbBindingV2 = { + 16 .api = "tuto-1", + 17 .verbs = verbs + 18 }; +``` + +The structure **afbBindingV2** actually tells that: + + - the exported **API** name is **tuto-1** (line 16) + - the array of verbs is the above defined one + +The exported list of verb is specified by an array of structures, +each describing a verb, ended with a verb NULL (line 12). + +The only defined verb here (line 11) is named **hello** (field **.verb**) +and the function that handle the related request is **hello** +(field **.callback**). + +Note that you can explicitely mark the fact that these are +struct by typing the **struct** as below: + +```C + 10 const struct afb_verb_v2 verbs[] = { + 11 { .verb="hello", .callback=hello }, + 12 { .verb=NULL } + 13 }; + 14 + 15 const struct afb_binding_v2 afbBindingV2 = { + 16 .api = "tuto-1", + 17 .verbs = verbs + 18 }; +``` + +### Handling binder's requests + +As shown above this is by default the common include directory where +the AGL stuff is installed. + +```C + 4 void hello(afb_req req) + 5 { + 6 AFB_REQ_DEBUG(req, "hello world"); + 7 afb_req_success(req, NULL, "hello world"); + 8 } +``` + +When the ***binder*** receives a request for the verb **hello** of +of the api **tuto-1**, it invoque the callback **hello** of the **binding** +with the argument **req** that handles the client request. + +The callback has to treat synchronously or asynchronously the request and +should at the end emit a reply for the request. + +Here, the callback for **tuto-1/hello** replies a successful answer +(ligne 7) to the request **req**. The second parameter (here NULL) +is a json object that is sent to the client with the reply. +The third parameter is also sent with the reply and is a string +called info that can be used as some meta data. + +Here again, you can explicitely mark the fact that +**afb_req** is a structure by declaring **hello** as below: + +```C + 4 void hello(struct afb_req req) +``` + +Sample binding: tuto-2 +====================== + +The second tutorial shows many important feature that can +commonly be used when writting a ***binding***: initialisation, +getting arguments, sending replies, pushing events. + +This is the code of the binding **tuto-2.c**: + +```C + 1 #include + 2 #include + 3 + 4 #define AFB_BINDING_VERSION 2 + 5 #include + 6 + 7 afb_event event_login, event_logout; + 8 + 9 void login(afb_req req) + 10 { + 11 json_object *args, *user, *passwd; + 12 char *usr; + 13 + 14 args = afb_req_json(req); + 15 if (!json_object_object_get_ex(args, "user", &user) + 16 || !json_object_object_get_ex(args, "password", &passwd)) { + 17 AFB_REQ_ERROR(req, "login, bad request: %s", json_object_get_string(args)); + 18 afb_req_fail(req, "bad-request", NULL); + 19 } else if (afb_req_context_get(req)) { + 20 AFB_REQ_ERROR(req, "login, bad state, logout first"); + 21 afb_req_fail(req, "bad-state", NULL); + 22 } else if (strcmp(json_object_get_string(passwd), "please")) { + 23 AFB_REQ_ERROR(req, "login, unauthorized: %s", json_object_get_string(args)); + 24 afb_req_fail(req, "unauthorized", NULL); + 25 } else { + 26 usr = strdup(json_object_get_string(user)); + 27 AFB_REQ_NOTICE(req, "login user: %s", usr); + 28 afb_req_session_set_LOA(req, 1); + 29 afb_req_context_set(req, usr, free); + 30 afb_req_success(req, NULL, NULL); + 31 afb_event_push(event_login, json_object_new_string(usr)); + 32 } + 33 } + 34 + 35 void action(afb_req req) + 36 { + 37 json_object *args, *val; + 38 char *usr; + 39 + 40 args = afb_req_json(req); + 41 usr = afb_req_context_get(req); + 42 AFB_REQ_NOTICE(req, "action for user %s: %s", usr, json_object_get_string(args)); + 43 if (json_object_object_get_ex(args, "subscribe", &val)) { + 44 if (json_object_get_boolean(val)) { + 45 AFB_REQ_NOTICE(req, "user %s subscribes to events", usr); + 46 afb_req_subscribe(req, event_login); + 47 afb_req_subscribe(req, event_logout); + 48 } else { + 49 AFB_REQ_NOTICE(req, "user %s unsubscribes to events", usr); + 50 afb_req_unsubscribe(req, event_login); + 51 afb_req_unsubscribe(req, event_logout); + 52 } + 53 } + 54 afb_req_success(req, json_object_get(args), NULL); + 55 } + 56 + 57 void logout(afb_req req) + 58 { + 59 char *usr; + 60 + 61 usr = afb_req_context_get(req); + 62 AFB_REQ_NOTICE(req, "login user %s out", usr); + 63 afb_event_push(event_logout, json_object_new_string(usr)); + 64 afb_req_session_set_LOA(req, 0); + 65 afb_req_context_clear(req); + 66 afb_req_success(req, NULL, NULL); + 67 } + 68 + 69 int preinit() + 70 { + 71 AFB_NOTICE("preinit"); + 72 return 0; + 73 } + 74 + 75 int init() + 76 { + 77 AFB_NOTICE("init"); + 78 event_login = afb_daemon_make_event("login"); + 79 event_logout = afb_daemon_make_event("logout"); + 80 if (afb_event_is_valid(event_login) && afb_event_is_valid(event_logout)) + 81 return 0; + 82 AFB_ERROR("Can't create events"); + 83 return -1; + 84 } + 85 + 86 const afb_verb_v2 verbs[] = { + 87 { .verb="login", .callback=login }, + 88 { .verb="action", .callback=action, .session=AFB_SESSION_LOA_1 }, + 89 { .verb="logout", .callback=logout, .session=AFB_SESSION_LOA_1 }, + 90 { .verb=NULL } + 91 }; + 92 + 93 const afb_binding_v2 afbBindingV2 = { + 94 .api = "tuto-2", + 95 .specification = NULL, + 96 .verbs = verbs, + 97 .preinit = preinit, + 98 .init = init, + 99 .noconcurrency = 0 + 100 }; +``` + +Compiling: + +```bash +$ gcc -fPIC -shared tuto-2.c -o tuto-2.so $(pkg-config --cflags --libs afb-daemon) +``` + +Running: + +```bash +$ afb-daemon --binding tuto-2.so --port 3333 --token '' +``` + +Testing: + +```bash +$ afb-client-demo -H localhost:3333/api?token=toto +tuto-2 login {"help":true} +ON-REPLY 1:tuto-2/login: ERROR +{ + "jtype":"afb-reply", + "request":{ + "status":"bad-request", + "uuid":"e2b24a13-fc43-487e-a5f4-9266dd1e60a9" + } +} +tuto-2 login {"user":"jose","password":"please"} +ON-REPLY 2:tuto-2/login: OK +{ + "jtype":"afb-reply", + "request":{ + "status":"success" + } +} +tuto-2 login {"user":"jobol","password":"please"} +ON-REPLY 3:tuto-2/login: ERROR +{ + "jtype":"afb-reply", + "request":{ + "status":"bad-state" + } +} +tuto-2 action {"subscribe":true} +ON-REPLY 4:tuto-2/action: OK +{ + "response":{ + "subscribe":true + }, + "jtype":"afb-reply", + "request":{ + "status":"success" + } +} +``` + +In an other terminal: + +```bash +$ afb-client-demo -H localhost:3333/api?token=toto +tuto-2 login {"user":"jobol","password":"please"} +ON-REPLY 1:tuto-2/login: OK +{ + "jtype":"afb-reply", + "request":{ + "status":"success", + "uuid":"a09f55ff-0e89-4f4e-8415-c6e0e7f439be" + } +} +tuto-2 logout true +ON-REPLY 2:tuto-2/logout: OK +{ + "jtype":"afb-reply", + "request":{ + "status":"success" + } +} +``` + +It produced in the first terminal: + +```bash +ON-EVENT tuto-2/login: +{ + "event":"tuto-2\/login", + "data":"jobol", + "jtype":"afb-event" +} +ON-EVENT tuto-2/logout: +{ + "event":"tuto-2\/logout", + "data":"jobol", + "jtype":"afb-event" +} +``` + diff --git a/docs/afb-daemon-options.md b/docs/afb-daemon-options.md new file mode 100644 index 00000000..39cf0bdb --- /dev/null +++ b/docs/afb-daemon-options.md @@ -0,0 +1,200 @@ + +Launching options of afb-daemon +--------------------- + +The launch options for binder **afb-daemon** are: + + --help + + Prints help with available options + + --version + + Display version and copyright + + --verbose + + Increases the verbosity, can be repeated + + --quiet + + Decreases the verbosity, can be repeated + + --port=xxxx + + HTTP listening TCP port [default 1234] + + --workdir=xxxx + + Directory where the daemon must run [default: $PWD if defined + or the current working directory] + + --uploaddir=xxxx + + Directory where uploaded files are temporarily stored [default: workdir] + + --rootdir=xxxx + + Root directory of the application to serve [default: workdir] + + --roothttp=xxxx + + Directory of HTTP served files. If not set, files are not served + but apis are still accessibles. + + --rootbase=xxxx + + Angular Base Root URL [default /opa] + + This is used for any application of kind OPA (one page application). + When set, any missing document whose url has the form /opa/zzz + is translated to /opa/#!zzz + + --rootapi=xxxx + + HTML Root API URL [default /api] + + The bindings are available within that url. + + --alias=xxxx + + Maps a path located anywhere in the file system to the + a subdirectory. The syntax for mapping a PATH to the + subdirectory NAME is: --alias=/NAME:PATH. + + Example: --alias=/icons:/usr/share/icons maps the + content of /usr/share/icons within the subpath /icons. + + This option can be repeated. + + --apitimeout=xxxx + + binding API timeout in seconds [default 20] + + Defines how many seconds maximum a method is allowed to run. + 0 means no limit. + + --cntxtimeout=xxxx + + Client Session Timeout in seconds [default 3600] + + --cache-eol=xxxx + + Client cache end of live [default 100000 that is 27,7 hours] + + --session-max=xxxx + + Maximum count of simultaneous sessions [default 10] + + --ldpaths=xxxx + + Load bindings from given paths separated by colons + as for dir1:dir2:binding1.so:... [default = $libdir/afb] + + You can mix path to directories and to bindings. + The sub-directories of the given directories are searched + recursively. + + The bindings are the files terminated by '.so' (the extension + so denotes shared object) that contain the public entry symbol. + + --binding=xxxx + + Load the binding of given path. + + --token=xxxx + + Initial Secret token to authenticate. + + If not set, no client can authenticate. + + If set to the empty string, then any initial token is accepted. + + --random-token + + Generate a random starting token. See option --exec. + + --mode=xxxx + + Set the mode: either local, remote or global. + + The mode indicate if the application is run locally on the host + or remotely through network. + + --dbus-client=xxxx + + Transparent binding to a binder afb-daemon service through dbus. + + It creates an API of name xxxx that is implemented remotely + and queried via DBUS. + + --dbus-server=xxxx + + Provides a binder afb-daemon service through dbus. + + The name xxxx must be the name of an API defined by a binding. + This API is exported through DBUS. + + --ws-client=xxxx + + Transparent binding to a binder afb-daemon service through a WebSocket. + + The value of xxxx is either a unix naming socket, of the form "unix:path/api", + or an internet socket, of the form "host:port/api". + + --ws-server=xxxx + + Provides a binder afb-daemon service through WebSocket. + + The value of xxxx is either a unix naming socket, of the form "unix:path/api", + or an internet socket, of the form "host:port/api". + + --foreground + + Get all in foreground mode (default) + + --daemon + + Get all in background mode + + --no-httpd + + Forbids HTTP serve + + --exec + + Must be the last option for afb-daemon. The remaining + arguments define a command that afb-daemon will launch. + The sequences @p, @t and @@ of the arguments are replaced + with the port, the token and @. + + --tracereq=xxxx + + Trace the processing of requests in the log file. + + Valid values are 'no' (default), 'common', 'extra' or 'all'. + + --traceditf=xxxx + + Trace the accesses to functions of class daemon. + + Valid values are 'no' (default), 'common', 'extra' or 'all'. + + --tracesvc=xxxx + + Trace the accesses to functions of class service. + + Valid values are 'no' (default) or 'all'. + + --traceevt=xxxx + + Trace the accesses to functions of class event. + + Valid values are 'no' (default), 'common', 'extra' or 'all'. + + --call=xxx + + Call a binding at start (can be be repeated). + The values are given in the form API/VERB:json-args. + + Example: --call 'monitor/set:{"verbosity":{"api":"debug"}}' diff --git a/docs/afb-daemon-vocabulary.md b/docs/afb-daemon-vocabulary.md new file mode 100644 index 00000000..c3b7c1ea --- /dev/null +++ b/docs/afb-daemon-vocabulary.md @@ -0,0 +1,98 @@ + +Vocabulary for AFB-DAEMON +========================= + +## Binding + +A shared library object intended to add a functionality to an afb-daemon +instance. It implements an API and may provide a service. + +Binding made for services can have specific entry point called after +initialisation and before serving. + +## Event + +Message with data propagated from the services to the client and not expecting +any reply. + +The current implementation allows to widely broadcast events to all clients. + +## Level of assurance (LOA) + +This level that can be from 0 to 3 represent the level of +assurance that the services can expect from the session. + +The exact definition of the meaning of these levels and how to use it remains to +be achieved. + +## Plugin + +Old name for binding, see binding. + +## Request + +A request is an invocation by a client to a binding method using a message +transferred through some protocol: HTTP, WebSocket, DBUS... and served by +***afb-daemon*** + +## Reply/Response + +This is a message sent to client as the result of the request. + +## Service + +Service are made of bindings running by their side on their binder. +It can serve many client. Each one attached to one session. + +The framework establishes connection between the services and +the clients. Using DBus currently but other protocols are considered. + +## Session + +A session is meant to be the unique instance context of a client, +which identify that instance across requests. + +Each session has an identifier. Session identifier generated by afb-daemon are +UUIDs. + +Internally, afb-daemon offers a mechanism to attach data to sessions. +When the session is closed or disappears, the data attached to that session +are freed. + +## Token + +The token is an identifier that the client must give to be authenticated. + +At start, afb-daemon get an initial token. This initial token must be presented +by incoming client to be authenticated. + +A token is valid only for a period. + +The token must be renewed periodically. When the token is renewed, afb-daemon +sends the new token to the client. + +Tokens generated by afb-daemon are UUIDs. + +## UUID + +It stand for Universal Unique IDentifier. + +It is designed to create identifier in a way that avoid has much as possible +conflicts. It means that if two different instances create an UUID, the +probability that they create the same UUID is very low, near to zero. + +## x-afb-reqid + +Argument name that can be used with HTTP request. When this argument is given, +it is automatically added to the "request" object of the answer. + +## x-afb-token + +Argument name meant to give the token without ambiguity. +You can also use the name **token** but it may conflicts with others arguments. + +## x-afb-uuid + +Argument name for giving explicitly the session identifier without ambiguity. +You can also use the name **uuid** but it may conflicts with others arguments. + diff --git a/docs/afb-desktop-package.md b/docs/afb-desktop-package.md new file mode 100644 index 00000000..775f4ee5 --- /dev/null +++ b/docs/afb-desktop-package.md @@ -0,0 +1,40 @@ + +Desktop packages for binder developement +======================================== + +It exists packages of the ***binder*** (afb-daemon) +for common desktop linux distributions. + + - Fedora + - Ubuntu + - Debian + - Suse + +Installing the developement package of the ***binder*** +allows to write ***bindings*** that runs on the destop +computer of the developper. + +It is very convenient to quickly write and debug a binding. + +Retriving compiling option with pkg-config +========================================== + +The ***binder*** afb-daemon provides a configuration +file for **pkg-config**. +Typing the command + + pkg-config --cflags afb-daemon + +Print flags use for compilation: + + $ pkg-config --cflags afb-daemon + -I/opt/local/include -I/usr/include/json-c + +For linking, you should use + + $ pkg-config --libs afb-daemon + -ljson-c + +It automatically includes the dependency to json-c. +This is activated through **Requires** keyword in pkg-config. + diff --git a/docs/afb-events-guide.md b/docs/afb-events-guide.md new file mode 100644 index 00000000..b6e4557c --- /dev/null +++ b/docs/afb-events-guide.md @@ -0,0 +1,371 @@ +Guide for developing with events +================================ + +Signaling agents are services that send events to any clients that +subscribed for receiving it. The sent events carry any data. + +To have a good understanding of how to write a signaling agent, the +actions of subscribing, unsubscribing, producing, sending and receiving +events must be described and explained. + +Overview of events +------------------ + +The basis of a signaling agent is shown in the following figure: + +![scenario of using events](pictures/signaling-basis.svg) + +This figure shows the main role of the signaling framework for the events +propagation. + +For people not familiar with the framework, a signaling agent and +a “binding” are similar. + +### Subscribing and unsubscribing + +Subscribing is the action that makes a client able to receive data from a +signaling agent. Subscription must create resources for generating the data, and +for delivering the data to the client. These two aspects are not handled by the +same piece of software. Generating the data is the responsibility of the +developer of the signaling agent while delivering the data is handled by the +framework. + +When a client subscribes for data, the agent must: + +1. check that the subscription request is correct; +2. establish the computation chain of the required data, if not already + done; +3. create a named event for the computed data, if not already done; +4. ask the framework to establish the subscription to the event for the + request; +5. optionally give indications about the event in the reply to + the client. + +The first two steps are not involving the framework. They are linked to +the business logic of the binding. The request can be any description of +the requested data and the computing stream can be of any nature, this +is specific to the binding. + +As said before, the framework uses and integrates **libsystemd** and its event +loop. Within the framework, **libsystemd** is the standard API/library for +bindings expecting to setup and handle I/O, timer or signal events. + +Steps 3 and 4 are bound to the framework. + +The agent must create an object for handling the propagation of produced +data to its clients. That object is called “event” in the framework. An +event has a name that allows clients to distinguish it from other +events. + +Events are created using the ***afb\_daemon\_make\_event*** function +that takes the name of the event. Example: + +```C + event = afb_daemon_make_event(name); +``` + +Once created, the event can be used either to push data to its +subscribers or to broadcast data to any listener. + +The event must be used to establish the subscription for the requesting +client. This is done using the ***afb\_req\_subscribe*** function +that takes the current request object and event and associates them +together. Example: + +```C + rc = afb_req_subscribe(req, event); +``` + +When successful, this function make the connection between the event and +the client that emitted the request. The client becomes a subscriber of +the event until it unsubscribes or disconnects. The +***afb\_req\_subscribe*** function will fail if the client +connection is weak: if the request comes from a HTTP link. To receive +signals, the client must be connected. The AGL framework allows connections +using WebSocket. + +The name of the event is either a well known name or an ad hoc name +forged for the use case. + +Let's see a basic example: client A expects to receive the speed in km/h +every second while client B expects the speed in mph twice a second. In +that case, there are two different events because it is not the same +unit and it is not the same frequency. Having two different events +allows to associate clients to the correct event. But this doesn't tell +any word about the name of these events. The designer of the signaling +agent has two options for naming: + +1. names can be the same (“speed” for example) with sent data + self describing itself or having a specific tag (requiring from + clients awareness about requesting both kinds of speed isn't safe). +2. names of the event include the variations (by example: + “speed-km/h-1Hz” and “speed-mph-2Hz”) and, in that case, sent data + can self describe itself or not. + +In both cases, the signaling agent might have to send the name of the +event and/or an associated tag to its client in the reply of the +subscription. This is part of the step 5 above. + +The framework only uses the event (not its name) for subscription, +unsubscription and pushing. + +When the requested data is already generated and the event used for +pushing it already exists, the signaling agent must not instantiate a +new processing chain and must not create a new event object for pushing +data. The signaling agent must reuse the existing chain and event. + +Unsubscribing is made by the signaling agent on a request of its client. +The ***afb\_req\_unsubscribe*** function tells the framework to +remove the requesting client from the event's list of subscribers. +Example: + +```C + afb_req_unsubscribe(req, event); +``` + +Subscription count does not matter to the framework: subscribing the +same client several times has the same effect that subscribing only one +time. Thus, when unsubscribing is invoked, it becomes immediately +effective. + +#### More on naming events + +Within the AGL framework, a signaling agent is a binding that has an API +prefix. This prefix is meant to be unique and to identify the binding +API. The names of the events that this signaling agent creates are +automatically prefixed by the framework, using the API prefix of the +binding. + +Thus, if a signaling agent of API prefix ***api*** creates an event +of name ***event*** and pushes data to that event, the subscribers +will receive an event of name ***api/event***. + +### Generating and pushing signals and data + +This of the responsibility of the designer of the signaling agent to +establish the processing chain for generating events. In many cases, +this can be achieved using I/O or timer or signal events inserted in the +main loop. For this case, the AGL framework uses **libsystemd** and +provide a way to integrates to the main loop of this library using +afb\_daemon\_get\_event\_loop. Example: + +```C + sdev = afb_daemon_get_event_loop(); + rc = sd_event_add_io(sdev, &source, fd, EPOLLIN, myfunction, NULL); +``` + +In some other cases, the events are coming from D-Bus. In that case, the +framework also uses **libsystemd** internally to access D-Bus. It provides +two methods to get the available D-Bus objects, already existing and +bound to the main**libsystemd**event loop. Use either +***afb\_daemon\_get\_system\_bus*** or +***afb\_daemon\_get\_user\_bus*** to get the required instance. Then +use functions of **libsystemd** to handle D-Bus. + +In some rare cases, the generation of the data requires to start a new +thread. + +When a data is generated and ready to be pushed, the signaling agent +should call the function ***afb\_event\_push***. Example: + +```C + rc = afb_event_push(event, JSON); + if (rc == 0) { + stop_generating(event); + afb_event_drop(event); + } +``` + +The function ***afb\_event\_push*** pushes json data to all the +subscribers. It then returns the count of subscribers. When the count is +zero, there is no subscriber listening for the event. The example above +shows that in that case, the signaling agent stops to generate data for +the event and delete the event using afb\_event\_drop. This is one +possible option. Other valuable options are: do nothing and continue to +generate and push the event or just stop to generate and push the data +but keep the event existing. + +### Receiving the signals + +Understanding what a client expects when it receives signals, events or +data shall be the most important topic of the designer of a signaling +agent. The good point here is that because JSON[^1] is the exchange +format, structured data can be sent in a flexible way. + +The good design is to allow as much as possible the client to describe +what is needed with the goal to optimize the processing to the +requirements only. + +### The exceptional case of wide broadcast + +Some data or events have so much importance that they can be widely +broadcasted to alert any listening client. Examples of such an alert +are: + +- system is entering/leaving “power safe” mode +- system is shutting down +- the car starts/stops moving +- ... + +An event can be broadcasted using one of the two following methods: +***afb\_daemon\_broadcast\_event*** or +***afb\_event\_broadcast***. + +Example 1: + +```C + afb_daemon_broadcast_event(name, json); +``` + +Example 2: + +```C + event = afb_daemon_make_event(name); + . . . . + afb_event_broadcast(event, json); +``` + +As for other events, the name of events broadcasted using +***afb\_daemon\_broadcast\_event*** are automatically prefixed by +the framework with API prefix of the binding (signaling agent). + +Reference of functions +---------------------- + +### Function afb\_event afb\_daemon\_make\_event + +The function ***afb\_daemon\_make\_event*** that is defined as below: + +```C +/* + * Creates an event of 'name' and returns it. + */ +struct afb_event afb_daemon_make_event(const char *name); +``` + +The correct way to create the event at initialisation is to call the function +***afb\_daemon\_make\_event*** within the initialisation +function referenced by the field ***init*** of the structure ***afbBindingV2***. + +### Function afb\_event\_push + +The function ***afb\_event\_push*** is defined as below: + +```C +/* + * Pushes the 'event' with the data 'object' to its observers. + * 'object' can be NULL. + * + * For convenience, the function calls 'json_object_put' for object'. + * Thus, in the case where 'object' should remain available after + * the function returns, the function 'json_object_get' shall be used. + * + * Returns the count of clients that received the event. + */ +int afb_event_push(struct afb_event event, struct json_object *object); +``` + +As the function ***afb\_event\_push*** returns 0 when there is no +more subscriber, a binding can remove such unexpected event using the +function ***afb\_event\_drop***. + +### Function afb\_event\_drop + +The function ***afb\_event\_drop*** is defined as below: + +```C +/* + * Drops the data associated to the event + * After calling this function, the event + * MUST NOT BE USED ANYMORE. + */ +void afb_event_drop(struct afb_event event); +``` + +### Function afb\_req\_subscribe + +The function ***afb\_req\_subscribe*** is defined as below: + +```C +/* + * Establishes for the client link identified by 'req' a subscription + * to the 'event'. + * Returns 0 in case of successful subscription or -1 in case of error. + */ +int afb_req_subscribe(struct afb_req req, struct afb_event event); +``` + +The subscription adds the client of the request to the list of subscribers +to the event. + +### Function afb\_req\_unsubscribe + +The function ***afb\_req\_unsubscribe*** is defined as +below: + +```C +/* + * Revokes the subscription established to the 'event' for the client + * link identified by 'req'. + * Returns 0 in case of successful unsubscription or -1 in case of error. + */ +int afb_req_unsubscribe(struct afb_req req, struct afb_event event); +``` + +The unsubscription removes the client of the request of the list of subscribers +to the event. +When the list of subscribers to the event becomes empty, +the function ***afb\_event\_push*** will return zero. + +### Function afb\_event\_broadcast + +The function ***afb\_event\_broadcast*** is defined as below: + +```C +/* + * Broadcasts widely the 'event' with the data 'object'. + * 'object' can be NULL. + * + * For convenience, the function calls 'json_object_put' for 'object'. + * Thus, in the case where 'object' should remain available after + * the function returns, the function 'json_object_get' shall be used. + * + * Returns the count of clients that received the event. + */ +int afb_event_broadcast(struct afb_event event, struct json_object *object); +``` + +This uses an existing event (created with ***afb\_daemon\_make\_event***) +for broadcasting an event having its name. + + +### Function afb\_daemon\_broadcast\_event + +The function ***afb\_daemon\_broadcast\_event*** is defined as below: + +```C +/* + * Broadcasts widely the event of 'name' with the data 'object'. + * 'object' can be NULL. + * + * For convenience, the function calls 'json_object_put' for 'object'. + * Thus, in the case where 'object' should remain available after + * the function returns, the function 'json_object_get' shall be used. + * + * Returns the count of clients that received the event. + */ +int afb_daemon_broadcast_event(const char *name, struct json_object *object); +``` + +The name is given here explicitly. The name is automatically prefixed +with the name of the binding. For example, a binding of prefix "xxx" +would broadcat the event "xxx/name". + +### Function onevent (field of afbBindingV2) + +Binding can designate an event handling function using the field **onevent** +of the structure **afbBindingV2**. This function is called when an event is +broadcasted or when an event the binding subscribed to is pushed. +That allow a service to react to an event and do what it is to do if this is +relevant for it (ie: car back camera detects imminent collision and broadcast +it, then appropriate service enable parking brake.). diff --git a/docs/afb-migration-v1-to-v2.md b/docs/afb-migration-v1-to-v2.md new file mode 100644 index 00000000..487c259d --- /dev/null +++ b/docs/afb-migration-v1-to-v2.md @@ -0,0 +1,611 @@ +Migration from binding V1 to binding V2 +======================================= + +The ***binding*** interface evolved from version 1 to version 2 +for the following reasons: + + - integration of the security requirements within the bindings + - simplification of the API (after developer feedbacks) + - removal of obscur features, cleanup + +The ***binder*** can run ***bindings*** v1 and/or v2 in any combination. +Thus moving from v1 to v2 is not enforced. There is no real need. + +More, it is possible to write a dual ***binding***: a ***binding*** that +implements the version 1 AND the version 2. + +However, IT IS HIGHLY RECOMMANDED TO SWITCH TO ONLY VERSION 2: + + - any new developement SHOULD start using ***binding*** V2 + - existing ***bindings*** SHOULD migrate to the version 2 + +This guide covers the migration of bindings from version 1 to version 2. + +It also explains some of the rationale taken when migrating from version 1 to version 2. + +In the future, if ***binding*** api evolves to fresh versions (3, 4, ...) +it might be necessarily to write bindings implementing more than +just one version. For example, a ***binding*** being v2 AND v3 will resolve +the issue of running on older AND newer version of AGL. This should always +be possible even if more complicated. + +Important things to known when migrating +---------------------------------------- + +One of the most important change when migrating from v1 to v2 is +that many functions use an hidden *common* variable. +This affects the functions of the following classes: + + - functions of class **daemon**: + * functions starting with **afb_daemon_...** + * functions for logging: **ERROR**, **WARNING**, **NOTICE**, **INFO**, **DEBUG** + - functions of class **service**: + * functions starting with **afb_service_...** + - callback functions: + * the register function (that is removed) + * the service init function + * the onevent function + +For these functions, the first parameter is now implicit. + +Let takes an example. For ***binding*** v1 you had to write: + +```C + afb_daemon_broadcast_event(afbitf->daemon, reason, description); +``` + +For ***binding*** v2, you simply write: + +```C + afb_daemon_broadcast_event(reason, description); +``` + +This simplification is possible because the header files included for the bindings +now provide a common variable for storing the **daemon** and **service** data. + +As a programmer, you shouldn't care much about that hidden variable. +It simplifies the job, that's all and that is the reason of the change. + +An other important difference is between the version 1 and the version 2 is +on how the ***binding***'s **API** is documented. The version 2 enphasis the +**OpenAPI v3** description of the **API**. For this reason, to avoid +duplication of descriptions, only one description is expected: the **OpenAPI** one. + +Task list for the migration +--------------------------- + +This task list is: + + 1. Enforce use of binding v2 by setting **AFB_BINDING_VERSION** + 2. Rewrite the main structure and the list of exported verbs + 3. Adapt the init and callback functions + 4. Removes the first parameter of functions of classes **daemon** + and **service** + 5. Consider where to emit logs for requests + 6. Take care of store/unstore changes + 7. Consider use of synchronous (sub)call requests + 8. Optionnaly, removes explicit struct + +The remaining chapters explain these task with more details. + +Enforce use of binding v2 by setting AFB_BINDING_VERSION +-------------------------------------------------------- + +By defining **AFB_BINDING_VERSION** to **2** you switch to version 2. +This is done as below. + +```C +#define AFB_BINDING_VERSION 2 +#include +``` + +After that you will get many errors when compiling. + +Rewrite the main structures and the list of exported verbs +--------------------------------------------------------- + +The structures describing the ***binding** changed from version 1 to version 2. + +The structure for describing verbs changed to include security +requirements. In version 1 it was: + +```C +struct afb_verb_desc_v1 +{ + const char *name; /* name of the verb */ + enum afb_session_flags_v1 session; /* authorisation and session requirements of the verb */ + void (*callback)(struct afb_req req); /* callback function implementing the verb */ + const char *info; /* textual description of the verb */ +}; +``` + +In version 2 it becomes: + +```C +struct afb_verb_v2 +{ + const char *verb; /* name of the verb */ + void (*callback)(struct afb_req req); /* callback function implementing the verb */ + const struct afb_auth *auth; /* required authorisation */ + uint32_t session; /* authorisation and session requirements of the verb */ +}; + +``` + +The migration of instances of that structure requires the following actions: + + - rename field **name** to **verb** + - remove field **info** + - adapt field **session** if needed + - set field **auth** to NULL + +Example: + +```C + { .name= "new", .session= AFB_SESSION_NONE, .callback= new, .info= "Starts a new game" } +``` + +Becomes + +```C + { .verb = "new", .session = AFB_SESSION_NONE, .callback = new, .auth = NULL } +``` + +The field **auth** can be set to a value describing the requested +authorisation. + +The main describing structure also changed. In version 1 it was: + +```C +struct afb_binding_desc_v1 +{ + const char *info; /* textual information about the binding */ + const char *prefix; /* required prefix name for the binding */ + const struct afb_verb_desc_v1 *verbs; /* array of descriptions of verbs terminated by a NULL name */ +}; +``` + +In version 2 it becomes: + +```C +struct afb_binding_v2 +{ + const char *api; /* api name for the binding */ + const char *specification; /* textual specification of the binding */ + const struct afb_verb_v2 *verbs; /* array of descriptions of verbs terminated by a NULL name */ + int (*preinit)(); /* callback at load of the binding */ + int (*init)(); /* callback for starting the service */ + void (*onevent)(const char *event, struct json_object *object); /* callback for handling events */ + unsigned noconcurrency: 1; /* avoids concurrent requests to verbs */ +}; +``` + +The migration of instances of that structure requires the following actions: + + - declare, expore, name the structure as ```const struct afb_binding_v2 afbBindingV2``` + - rename the field **prefix** to **api** + - remove the field **info** + - setup the fields **preinit**, **init**, **onevent** accordling to the next section + - set the field **noconcurrency** to the right value: + * to 1 if you want to avoid concurrent calls to verbs. + * to 0 if you allow concurrent calls to verbs. + +Example: + +```C +static const struct afb_binding plugin_desc = { + .type = AFB_BINDING_VERSION_1, + .v1 = { + .info = "Minimal Hello World Sample", + .prefix = "hello", + .verbs = verbs + } +``` +Becomes: + +```C +const struct afb_binding_v2 afbBindingV2 = { + .api = "hello", + .specification = NULL, + .verbs = verbs, + .preinit = preinit, + .init = init +}; +``` + +The **binder** now relies only on the exported names +to deduce the type of the binding. This make the main +structure more simple. + +Adapt the init and callback functions +------------------------------------- + +The ***bindings*** version 1 defined 3 exported functions: + + - **afbBindingV1Register** + - **afbBindingV1ServiceInit** + - **afbBindingV1ServiceEvent** + +These function should not be exported any more and there definition changed. + +The function **afbBindingV1Register** is no more used to describe the binding. +When a binding has to take actions when it is loaded, it must set the field +**preinit** of the structure **afbBindingV2**. This field, this preinit, might +be used to check features at load. When it returns a negative number, the +***binder*** stops before initializing any ***binding***. + +The function **afbBindingV1ServiceInit** is replaced by the field **init** +of the structure **afbBindingV2**. The init function should return 0 in case +of success or a negative error code in case of problem. It is called during +initialisation of services. + +The function **afbBindingV1ServiceEvent**is replaced by the field **onevent** +of the structure **afbBindingV2**. + +The two functions **afbBindingV1Register** and **afbBindingV1ServiceInit**, +were taking as parameter the ***binder*** interface and the service interface respectively. +These interfaces are now managed hiddenly for the **binding** by the **binder**. +So the variable that ***bindings*** version used to store the ***binder*** interface +and the service interface are no more needed and can be removed. + +Example: + +```C +const struct afb_binding_interface *interface; +struct afb_service service; + +static const struct afb_binding plugin_desc = { + .type = AFB_BINDING_VERSION_1, + .v1 = { + .info = "Minimal Hello World Sample", + .prefix = "hello", + .verbs = verbs + } +} + +const struct afb_binding *afbBindingV1Register (const struct afb_binding_interface *itf) +{ + interface = itf; + NOTICE(interface, "binding register"); + return &plugin_desc; +} + +int afbBindingV1ServiceInit(struct afb_service svc) +{ + service = svc; + NOTICE(interface, "binding init"); + return 0; +} + +void afbBindingV1ServiceEvent(const char *event, struct json_object *object) +{ + NOTICE(interface, "onevent %s", event); +} +``` + +Becomes: + +```C +static int preinit() +{ + AFB_NOTICE("binding preinit (was register)"); + return 0; +} + +static int init() +{ + AFB_NOTICE("binding init"); + return 0; +} + +static void onevent(const char *event, struct json_object *object) +{ + AFB_NOTICE("onevent %s", event); +} + +const struct afb_binding_v2 afbBindingV2 = { + .api = "hello", + .specification = NULL, + .verbs = verbs, + .preinit = preinit, + .init = init, + .onevent = onevent +}; +``` + +The two functions **afbBindingV1Register** and **afbBindingV1ServiceInit**, +were taking as parameter the ***binder*** interface and the service interface respectively. +These interfaces are now managed hiddenly for the **binding** by the **binder**. +So the variable that ***bindings*** version used to store the ***binder*** interface +and the service interface are no more needed and can be removed. + +On the above example the folowing lines were removed: +```C +const struct afb_binding_interface *interface; +struct afb_service service; + + interface = itf; + + service = svc; +``` + + + +Removes the first parameter of functions of classes **daemon** and **service** +------------------------------------------------------------------------------ + +As explained before, many functions loose there first +arguments, this are the functions of the following classes: + + - functions of class **daemon**: + * functions starting with **afb_daemon_...** + * functions for logging: **ERROR**, **WARNING**, **NOTICE**, **INFO**, **DEBUG** + - functions of class **service**: + * functions starting with **afb_service_...** + - callback functions: + * the register function (that is removed) + * the service init function + * the onevent function + +For these functions, the first parameter is now implicit. + +Example: + +```C + afb_daemon_broadcast_event(afbitf->daemon, reason, description); +``` + +Becomes: + +```C + afb_daemon_broadcast_event(reason, description); +``` + +Also, to avoid possible conflicts, we introduced prefixed logging functions: +the macros **ERROR**, **WARNING**, **NOTICE**, **INFO**, **DEBUG** have now +a prefixed version: **AFB\_ERROR**, **AFB\_WARNING**, **AFB\_NOTICE**, +**AFB\_INFO**, **AFB\_DEBUG**. It is now recommanded to use the prefixed version. + +Example: + +```C + NOTICE(interface, "hello plugin comes to live"); +``` + +Become: + +```C + NOTICE("hello plugin comes to live"); +``` + +or, better: + +```C + AFB_NOTICE("hello plugin comes to live"); +``` + +To remove definition of the unprefixed versions of logging macros **ERROR**, **WARNING**, +**NOTICE**, **INFO**, **DEBUG** and just define **AFB_BINDING_PRAGMA_NO_VERBOSE_UNPREFIX** +before to include **afb/afb-binding.h**. + +```C +#define AFB_BINDING_PRAGMA_NO_VERBOSE_UNPREFIX +#define AFB_BINDING_VERSION 2 +#include +``` + +Consider where to emit logs for requests +---------------------------------------- + +The ***bindings*** v2 now allows to emit log messages associated to ***requests***. +This feature is valuable when debugging because it allows to return +side informations associated to a ***request***. + +The defined macros for logging to requests are: **AFB_REQ_ERROR**, +**AFB_REQ_WARNING**, **AFB_REQ_NOTICE**, **AFB_REQ_INFO**, **AFB_REQ_DEBUG**. + +We encourage the use of these new logging facilities everywhere it makes sense. + +Example: + +```C + INFO(afbitf, "method 'new' called for boardid %d", board->id); +``` + +Might become: + +```C + AFB_REQ_INFO(req, "method 'new' called for boardid %d", board->id); +``` + +Take care of store/unstore change +--------------------------------- + +For efficiency, the version 2 redefined how storing/unstoring of +requests works. Storing request is needed for asynchronous handling +of requests. + +For ***bindings*** version, the signature of the functions were: + +```C +struct afb_req *afb_req_store(struct afb_req req); +struct afb_req afb_req_unstore(struct afb_req *req); +``` + +For version 2 it becomes + +```C +struct afb_stored_req *afb_req_store(struct afb_req req); +struct afb_req afb_req_unstore(struct afb_stored_req *sreq); +``` + +Where the structure ```struct afb_stored_req``` is opaque. + +It should require few code change. + +Also check the following chapter that explain that asynchronous (sub)calls +can be replaced by synchronous one, avoiding the need to store/unstore +requests. + +Consider use of synchronous (sub)call requests +---------------------------------------------- + +***Bindings*** can emit requests for themselves (calls) or for +their clients (subcalls). With ***bindings*** version 2 comes +also synchronous requests for both cases. + +So when migrating to bindings version 2, a developper can consider +to replace the asynchronous requests (with asynchronous call back) +by synchronous ones. + +See functions ***afb_service_call_sync*** and ***afb_req_subcall_sync***. + +Optionnaly, removes explicit struct +----------------------------------- + +The new definitions now includes **typedefs** for common +structures, as shown on below sample: + +```C +typedef struct afb_daemon afb_daemon; +typedef struct afb_event afb_event; +typedef struct afb_arg afb_arg; +typedef struct afb_req afb_req; +typedef struct afb_service afb_service; +``` + +So you can remove the keyword **struct** if it bores you. + +Example: + +```C +static void verb(struct afb_req req) +{ + ... +} +``` + +Might become: + +```C +static void verb(afb_req req) +{ + ... +} +``` + +Example of migration +-------------------- + +The first ***binding*** that migrated from v1 to v2 was +the sample **HelloWorld**. Here is shown the differences between +the version 1 and the version 2. + +```diff +diff --git a/bindings/samples/HelloWorld.c b/bindings/samples/HelloWorld.c +index c6fa779..505aee3 100644 +--- a/bindings/samples/HelloWorld.c ++++ b/bindings/samples/HelloWorld.c +@@ -21,9 +21,9 @@ + + #include + ++#define AFB_BINDING_VERSION 2 + #include + +-const struct afb_binding_interface *interface; + static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + + struct event +@@ -79,7 +80,7 @@ static int event_add(const char *tag, const char *name) + strcpy(e->tag, tag); + + /* make the event */ +- e->event = afb_daemon_make_event(interface->daemon, name); ++ e->event = afb_daemon_make_event(name); + if (!e->event.closure) { free(e); return -1; } + + /* link */ +@@ -140,7 +141,7 @@ static void pingBug (struct afb_req request) + static void pingEvent(struct afb_req request) + { + json_object *query = afb_req_json(request); +- afb_daemon_broadcast_event(interface->daemon, "event", json_object_get(query)); ++ afb_daemon_broadcast_event("event", json_object_get(query)); + ping(request, json_object_get(query), "event"); + } + +@@ -288,38 +289,43 @@ static void exitnow (struct afb_req request) + exit(0); + } + ++static int preinit() ++{ ++ AFB_NOTICE("hello binding comes to live"); ++ return 0; ++} ++ ++static int init() ++{ ++ AFB_NOTICE("hello binding starting"); ++ return 0; ++} ++ + // NOTE: this sample does not use session to keep test a basic as possible + // in real application most APIs should be protected with AFB_SESSION_CHECK +-static const struct afb_verb_desc_v1 verbs[]= { +- {"ping" , AFB_SESSION_NONE, pingSample , "Ping Application Framework"}, +- {"pingfail" , AFB_SESSION_NONE, pingFail , "Fails"}, +- {"pingnull" , AFB_SESSION_NONE, pingNull , "Return NULL"}, +- {"pingbug" , AFB_SESSION_NONE, pingBug , "Do a Memory Violation"}, +- {"pingJson" , AFB_SESSION_NONE, pingJson , "Return a JSON object"}, +- {"pingevent", AFB_SESSION_NONE, pingEvent , "Send an event"}, +- {"subcall", AFB_SESSION_NONE, subcall , "Call api/verb(args)"}, +- {"subcallsync", AFB_SESSION_NONE, subcallsync , "Call api/verb(args)"}, +- {"eventadd", AFB_SESSION_NONE, eventadd , "adds the event of 'name' for the 'tag'"}, +- {"eventdel", AFB_SESSION_NONE, eventdel , "deletes the event of 'tag'"}, +- {"eventsub", AFB_SESSION_NONE, eventsub , "subscribes to the event of 'tag'"}, +- {"eventunsub",AFB_SESSION_NONE, eventunsub , "unsubscribes to the event of 'tag'"}, +- {"eventpush", AFB_SESSION_NONE, eventpush , "pushs the event of 'tag' with the 'data'"}, +- {"exit", AFB_SESSION_NONE, exitnow , "exits from afb-daemon"}, +- {NULL} ++static const struct afb_verb_v2 verbs[]= { ++ { "ping" , pingSample , NULL, AFB_SESSION_NONE }, ++ { "pingfail" , pingFail , NULL, AFB_SESSION_NONE }, ++ { "pingnull" , pingNull , NULL, AFB_SESSION_NONE }, ++ { "pingbug" , pingBug , NULL, AFB_SESSION_NONE }, ++ { "pingJson" , pingJson , NULL, AFB_SESSION_NONE }, ++ { "pingevent", pingEvent , NULL, AFB_SESSION_NONE }, ++ { "subcall", subcall , NULL, AFB_SESSION_NONE }, ++ { "subcallsync", subcallsync, NULL, AFB_SESSION_NONE }, ++ { "eventadd", eventadd , NULL, AFB_SESSION_NONE }, ++ { "eventdel", eventdel , NULL, AFB_SESSION_NONE }, ++ { "eventsub", eventsub , NULL, AFB_SESSION_NONE }, ++ { "eventunsub", eventunsub , NULL, AFB_SESSION_NONE }, ++ { "eventpush", eventpush , NULL, AFB_SESSION_NONE }, ++ { "exit", exitnow , NULL, AFB_SESSION_NONE }, ++ { NULL} + }; + +-static const struct afb_binding plugin_desc = { +- .type = AFB_BINDING_VERSION_1, +- .v1 = { +- .info = "Minimal Hello World Sample", +- .prefix = "hello", +- .verbs = verbs +- } ++const struct afb_binding_v2 afbBindingV2 = { ++ .api = "hello", ++ .specification = NULL, ++ .verbs = verbs, ++ .preinit = preinit, ++ .init = init + }; + +-const struct afb_binding *afbBindingV1Register (const struct afb_binding_interface *itf) +-{ +- interface = itf; +- NOTICE(interface, "hello plugin comes to live"); +- return &plugin_desc; +-} +``` \ No newline at end of file diff --git a/docs/afb-overview.md b/docs/afb-overview.md new file mode 100644 index 00000000..67e5b0d1 --- /dev/null +++ b/docs/afb-overview.md @@ -0,0 +1,98 @@ + +Overview of the binder +====================== + +The ***binder*** provides the way to connect applications to +the services that it needs. It provides a fast way +to securely offer APIs to applications written in +any language and running almost anywhere. + +The ***binder*** is developped for AGL. + +The ***binder*** is the usual name. +The binary is named **afb-daemon**. + +The name **afb-daemon** stands for ***Application +Framework Binder Daemon***. The word *daemon*, here, +denote the fact that the ***binder*** makes witchcraft to +connect applications to their expected services (note that usually +the term of daemon denotes background process but not here). + +Each ***binder*** **afb-daemon** is in charge to bind +one instance of an application or service to the rest +of the system, applications and services. +Within AGL, the connexion between services and/or +applications is tuned by the AGL framework and +the AGL system. + +The basis of the binder +======================= + +The following figure shows main concepts linked to the ***binder***. + + +![Figure: binder basis](pictures/basis.svg) + +The shown elements are: + +* The SECURITY CONTEXT + + The primary intend of any ***binder*** is to provide + a secured environment for any application. On AGL, the + **security context** is ensured by [Smack][Smack] a security context, + the security context of the application or service. + +* The BINDER + + This is the central element. It makes possible to run HTML5 + applications and provides the unified access to APIs provided + by the ***bindings***. + + Running a pure HTML5 application doesn't require any ***binding***. + in that case , the ***binder*** acts as a simple HTTP server for + the web runtime. + +* The BINDINGs + + A ***binding*** adds one **API** to the ***binder***. + + An **API** is a set of **verbs** that can be called + using either REST over HTTP or a kind of JSON RPC. + + ***bindings*** are either: + + - dynamically loaded libraries in the ***binder*** process + - remote service running on the same host + - remote service running on other hosts + + When acting as an HTTP server, the binder treats the language + settings of the HTTP resquests to provide internationalized + content as specified by + [widget specifications](https://www.w3.org/TR/widgets/#internationalization-and-localization). + +* The APPLICATION + + An ***application*** connects to the binder to get access to + the **API** that it provides or to get its HTTP services to access + resources. + +Interconnection of binders +========================== + +The AGL framework interprets the **widget/application** manifests +to setup the ***bindings*** configuration of the ***binders***. + +The figure below shows that ***binders*** are interconnected. + + +![Figure: binder interconnection](pictures/interconnection.svg) + +The figure shows 4 several **application/service**: **A**, **B**, +**C** and **D**. + +The application **A** might use an **API** that is shown as a +local ***binbing*** but that in reality runs within the context +of **D**. + +The framework AGL takes care of making the plumbing working. + diff --git a/docs/index.md b/docs/index.md new file mode 120000 index 00000000..81c25433 --- /dev/null +++ b/docs/index.md @@ -0,0 +1 @@ +afb-overview.md \ No newline at end of file diff --git a/docs/pictures/AFB_for_services.svg b/docs/pictures/AFB_for_services.svg new file mode 100644 index 00000000..6e536c50 --- /dev/null +++ b/docs/pictures/AFB_for_services.svg @@ -0,0 +1,238 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Isolated security context + + + + + + + + APPLICATION + + + + + + + + binderAFB-DAEMON + + + + + + + + BINDINGS + + + + + + + + + + + + + + + + + + + + + D-Bus & CYNARA + + + + + + + + Isolated security context A + + + + + + + + binderAFB-DAEMON + + + + + + + + serviceBINDINGS A + + + + + + + + + + + + + + + Isolated security context B + + + + + + + + binderAFB-DAEMON + + + + + + + + serviceBINDINGS B + + + + + + + + + + + + + + + Isolated security context C + + + + + + + + binderAFB-DAEMON + + + + + + + + serviceBINDINGS C + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/pictures/basis.odg b/docs/pictures/basis.odg new file mode 100644 index 00000000..6025fdbf Binary files /dev/null and b/docs/pictures/basis.odg differ diff --git a/docs/pictures/basis.svg b/docs/pictures/basis.svg new file mode 100644 index 00000000..0d42d764 --- /dev/null +++ b/docs/pictures/basis.svg @@ -0,0 +1,356 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + APPLICATION + + + + + + + + BINDERafb-daemon + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + BINDING + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + BINDING + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + BINDING + + + + + + + + + + + + + + + + + + SECURITYCONTEXT + + + + + + http + + + + + + ws + + + + + + + + \ No newline at end of file diff --git a/docs/pictures/interconnection.odg b/docs/pictures/interconnection.odg new file mode 100644 index 00000000..5e99bfa4 Binary files /dev/null and b/docs/pictures/interconnection.odg differ diff --git a/docs/pictures/interconnection.svg b/docs/pictures/interconnection.svg new file mode 100644 index 00000000..4a102172 --- /dev/null +++ b/docs/pictures/interconnection.svg @@ -0,0 +1,854 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + APPLICATION + + + + + + + + BINDERafb-daemon + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + BINDING + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + BINDING + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + BINDING + + + + + + + + + + + + SECURITYCONTEXT + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + APPLICATION + + + + + + + + BINDERafb-daemon + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + BINDING + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + BINDING + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + BINDING + + + + + + + + + + + + SECURITYCONTEXT + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + APPLICATION + + + + + + + + BINDERafb-daemon + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + BINDING + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + BINDING + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + BINDING + + + + + + + + + + + + SECURITYCONTEXT + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + APPLICATION + + + + + + + + BINDERafb-daemon + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + BINDING + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + BINDING + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + BINDING + + + + + + + + + + + + SECURITYCONTEXT + + + + + + + + + interconnectiondbus, ws,bus1, tls,... + + + + + + + + A + + + + + + + + C + + + + + + + + B + + + + + + + + D + + + + + + + + \ No newline at end of file diff --git a/docs/pictures/signaling-basis.svg b/docs/pictures/signaling-basis.svg new file mode 100644 index 00000000..b13fcf17 --- /dev/null +++ b/docs/pictures/signaling-basis.svg @@ -0,0 +1,145 @@ + + + + + + + request-data + + + + + + client 1 + + + + + + + + client 2 + + + + + + + + : framework + + + + + + + + signaling agent + + + + + + + + + + + request-data + + + + + + afb_daemon_make_event + + + + + + + + + afb_req_subscribe + + + + + reply of request-data + + + + + + afb_req_subscribe + + + + + reply of request-data + + + + + + device + + + + + + + + + setup + + + + + << wake up >> + + + + + afb_event_push + + + + + << event >> + + + + + << event >> + + + + + reply of afb_event_push + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/pictures/tic-tac-toe.svg b/docs/pictures/tic-tac-toe.svg new file mode 100644 index 00000000..7a5fb84e --- /dev/null +++ b/docs/pictures/tic-tac-toe.svg @@ -0,0 +1,289 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Client A + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Client B + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Tic-Tac-Toe + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + move + + + + + + wait + + + + + + success of move + + + + + + success of wait + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/pictures/triskel_iot_bzh.svg b/docs/pictures/triskel_iot_bzh.svg new file mode 100644 index 00000000..096f4244 --- /dev/null +++ b/docs/pictures/triskel_iot_bzh.svg @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/mkdocs.yml b/mkdocs.yml index 87bd6230..b2f24aca 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,11 +1,15 @@ site_name: AGL Application Framework Binder theme: readthedocs -docs_dir: doc +docs_dir: docs pages: - 'Overview' : 'index.md' - - 'Binder daemon vocabulary' : 'afb-daemon-vocabulary.md' + - 'How to write a binding ?': 'afb-binding-writing.md' + - 'Binding references': 'afb-binding-references.md' + - 'Migration from v1 to v2' : 'afb-migration-v1-to-v2.md' - 'Binder events guide' : 'afb-events-guide.md' - 'Binder Application writing guide' : 'afb-application-writing.md' - - 'Binding writing guide' : 'afb-bindings-writing.md' - - 'Binder tests overview' : 'afb-tests-overview.md' - - 'Bindings samples' : 'afb-bindings-overview.md' + - 'Binder daemon vocabulary' : 'afb-daemon-vocabulary.md' + - 'Annexes': + - 'Installing the binder on a desktop': 'afb-desktop-package.md' + - 'Options of afb-daemon' : 'afb-daemon-options.md' + diff --git a/old-docs/FAQ.md b/old-docs/FAQ.md new file mode 100644 index 00000000..5063ed74 --- /dev/null +++ b/old-docs/FAQ.md @@ -0,0 +1,5 @@ +Frequently Asked Question about AFB-DAEMON +========================================== + + + diff --git a/old-docs/afb-application-writing.md b/old-docs/afb-application-writing.md new file mode 100644 index 00000000..cc513b48 --- /dev/null +++ b/old-docs/afb-application-writing.md @@ -0,0 +1,301 @@ +How to write an application on top of AGL FRAMEWORK +==================================================== + +Programming Languages for Applications +----------------------------------------- + +### Writing an HTML5 application + +Developers of HTML5 applications (client side) can easily create +applications for AGL framework using their preferred +HTML5 framework. + +Developers may also take advantage of powerful server side bindings to improve +application behavior. Server side bindings return an application/json mine-type +and can be accessed though either HTTP or Websockets. + +In a near future, JSON-RPC protocol should be added to complete the current +x-afb-json1 protocol. + +Two examples of HTML5 applications are given: + +- [afb-client](https://gerrit.automotivelinux.org/gerrit/gitweb?p=src/app-framework-demo.git;a=tree;f=afb-client) a simple "hello world" application template + +- [afm-client](https://gerrit.automotivelinux.org/gerrit/gitweb?p=src/app-framework-demo.git;a=tree;f=afm-client) a simple "Home screen" application template + +### Writing a Qt application + +Writing Qt applications is also supported. Qt offers standard API to send +request through HTTP or WebSockets. + +It is also possible to write QML applications. A sample QML application +[token-websock] is available: + +- [token-websock](https://gerrit.automotivelinux.org/gerrit/gitweb?p=src/app-framework-binder.git;a=blob;f=test/token-websock.qml) +a simple "hello world" application in QML + +### Writing a "C" application + +C applications can use afb-daemon binder through a websocket connection. + +The library **libafbwsc** is provided for C clients that need +to connect with an afb-daemon binder. + +The program **afb-client-demo** is the C example that use +**libafbwsc** library. +Source code is available here +[src/afb-client-demo.c](https://gerrit.automotivelinux.org/gerrit/gitweb?p=src/app-framework-binder.git;a=blob;f=src/afb-client-demo.c). + +Current implementation relies on libsystemd and file descriptors. +This model might be review in the future to support secure sockets +and get rid of libsystemd dependency. + +Handling sessions within applications +------------------------------------- + +Applications should understand sessions and token management when interacting +with afb-daemon binder. + +Applications communicate with their private binder(afb-daemon) using +a network connection or potentially any other connection channel. While the +current version does not yet implement Unix socket, this feature might be added +in the near future. Developers need to be warn that HTTP protocol is a none +connected protocol and that using HTTP socket connection to authenticate +clients is not supported. + +For this reason, the binder should authenticate the application +by using a shared secret. The secret is named "token" and the identification +of client is named "session". + +The examples **token-websock.qml** and **afb-client** are demonstrating +how authentication and sessions are managed. + +### Handling sessions + +Bindings and other binder features need to keep track of client +instances. This is especially important for bindings running as services +as they may typically have to keep each client's data separated. + +For HTML5 applications, the web runtime handles the cookie of session +that the binder afb-daemon automatically sets. + +Session identifier can be set using the parameter **uuid** or **x-afb-uuid** in +URI requests. Within current version of the framework session UUID is supported +by both HTTP requests and websocket negotiation. + +### Exchanging tokens + +At application start, AGL framework communicates a shared secret to both binder +and client application. This initial secret is called the "**initial token**". + +For each of its client application, the binder manages a current active +token for session management. This authentication token can be use to restrict +the access to some binding's methods. + +The token must be included in URI request on HTTP or during websockets +connection using parameter **token** or **x-afb-token**. + +To ensure security, tokens must be refreshed periodically. + +### Example of session management + +In following examples, we suppose that **afb-daemon** is launched with something +equivalent to: + + $ afb-daemon --port=1234 --token=123456 [...] + +making the expectation that **AuthLogin** binding is requested as default. + +#### Using curl + +First, connects with the initial token, 123456: + + $ curl http://localhost:1234/api/auth/connect?token=123456 + { + "jtype": "afb-reply", + "request": { + "status": "success", + "token": "0aef6841-2ddd-436d-b961-ae78da3b5c5f", + "uuid": "850c4594-1be1-4e9b-9fcc-38cc3e6ff015" + }, + "response": {"token": "A New Token and Session Context Was Created"} + } + +It returns an answer containing session UUID, 850c4594-1be1-4e9b-9fcc-38cc3e6ff015, +and a refreshed token, 850c4594-1be1-4e9b-9fcc-38cc3e6ff015. + +Check if session and token is valid: + + $ curl http://localhost:1234/api/auth/check?token=0aef6841-2ddd-436d-b961-ae78da3b5c5f\&uuid=850c4594-1be1-4e9b-9fcc-38cc3e6ff015 + { + "jtype": "afb-reply", + "request": {"status":"success"}, + "response": {"isvalid":true} + } + +Refresh the token: + + $ curl http://localhost:1234/api/auth/refresh?token=0aef6841-2ddd-436d-b961-ae78da3b5c5f\&uuid=850c4594-1be1-4e9b-9fcc-38cc3e6ff015 + { + "jtype": "afb-reply", + "request": { + "status":"success", + "token":"b8ec3ec3-6ffe-448c-9a6c-efda69ad7bd9" + }, + "response": {"token":"Token was refreshed"} + } + +Close the session: + + curl http://localhost:1234/api/auth/logout?token=b8ec3ec3-6ffe-448c-9a6c-efda69ad7bd9\&uuid=850c4594-1be1-4e9b-9fcc-38cc3e6ff015 + { + "jtype": "afb-reply", + "request": {"status": "success"}, + "response": {"info":"Token and all resources are released"} + } + +Checking on closed session for uuid should be refused: + + curl http://localhost:1234/api/auth/check?token=b8ec3ec3-6ffe-448c-9a6c-efda69ad7bd9\&uuid=850c4594-1be1-4e9b-9fcc-38cc3e6ff015 + { + "jtype": "afb-reply", + "request": { + "status": "failed", + "info": "invalid token's identity" + } + } + +#### Using afb-client-demo + +> The program is packaged within AGL in the rpm **libafbwsc-dev** + +Here is an example of exchange using **afb-client-demo**: + + $ afb-client-demo ws://localhost:1234/api?token=123456 + auth connect + ON-REPLY 1:auth/connect: {"jtype":"afb-reply","request":{"status":"success", + "token":"63f71a29-8b52-4f9b-829f-b3028ba46b68","uuid":"5fcc3f3d-4b84-4fc7-ba66-2d8bd34ae7d1"}, + "response":{"token":"A New Token and Session Context Was Created"}} + auth check + ON-REPLY 2:auth/check: {"jtype":"afb-reply","request":{"status":"success"},"response":{"isvalid":true}} + auth refresh + ON-REPLY 4:auth/refresh: {"jtype":"afb-reply","request":{"status":"success", + "token":"8b8ba8f4-1b0c-48fa-962d-4a00a8c9157e"},"response":{"token":"Token was refreshed"}} + auth check + ON-REPLY 5:auth/check: {"jtype":"afb-reply","request":{"status":"success"},"response":{"isvalid":true}} + auth refresh + ON-REPLY 6:auth/refresh: {"jtype":"afb-reply","request":{"status":"success", + "token":"e83b36f8-d945-463d-b983-5d8ed73ba529"},"response":{"token":"Token was refreshed"}} + +After closing connection, reconnect as here after: + + $ afb-client-demo ws://localhost:1234/api?token=e83b36f8-d945-463d-b983-5d8ed73ba529\&uuid=5fcc3f3d-4b84-4fc7-ba66-2d8bd34ae7d1 auth check + ON-REPLY 1:auth/check: {"jtype":"afb-reply","request":{"status":"success"},"response":{"isvalid":true}} + +Same connection check using **curl**: + + $ curl http://localhost:1234/api/auth/check?token=e83b36f8-d945-463d-b983-5d8ed73ba529\&uuid=5fcc3f3d-4b84-4fc7-ba66-2d8bd34ae7d1 + {"jtype":"afb-reply","request":{"status":"success"},"response":{"isvalid":true}} + +Format of replies +----------------- + +Replies use javascript object returned as serialized JSON. + +This object contains at least 2 mandatory fields of name **jtype** and +**request** and one optional field of name **response**. + +### Template + +This is a template of replies: + +```json +{ + "jtype": "afb-reply", + "request": { + "status": "success", + "info": "informationnal text", + "token": "e83b36f8-d945-463d-b983-5d8ed73ba52", + "uuid": "5fcc3f3d-4b84-4fc7-ba66-2d8bd34ae7d1", + "reqid": "application-generated-id-23456" + }, + "response": ....any response object.... +} +``` + +### Field jtype + +The field **jtype** must have a value of type string equal to **"afb-reply"**. + +### Field request + +The field **request** must have a value of type object. This request object +has at least one field named **status** and four optional fields named +**info**, **token**, **uuid**, **reqid**. + +#### Subfield request.status + +**status** must have a value of type string. This string is equal to **"success"** +only in case of success. + +#### Subfield request.info + +**info** is of type string and represent optional information added to the reply. + +#### Subfield request.token + +**token** is of type string. It is sent either at session creation +or when the token is refreshed. + +#### Subfield request.uuid + +**uuid** is of type string. It is sent at session creation. + +#### Subfield request.reqid + +**reqid** is of type string. It is sent in response to HTTP requests +that added a parameter of name **reqid** or **x-afb-reqid** at request time. +Value returns in the reply has the exact same value as the one received in the +request. + +### Field response + +This field response optionally contains an object returned when request +succeeded. + +Format of events +---------------- + +Events are javascript object serialized as JSON. + +This object contains at least 2 mandatory fields of name **jtype** and **event** +and one optional field of name **data**. + +### Template + +Here is a template of event: + +```json +{ + "jtype": "afb-event", + "event": "sample_api_name/sample_event_name", + "data": ...any event data... +} +``` + +### Field jtype + +The field **jtype** must have a value of type string equal to **"afb-event"**. + +### Field event + +The field **event** carries the event's name. + +The name of the event is made of two parts separated by a slash: +the name of the name of the API that generated the event +and the name of event within the API. + +### Field data + +This field data if present holds the data carried by the event. + diff --git a/old-docs/afb-bindings-overview.md b/old-docs/afb-bindings-overview.md new file mode 100644 index 00000000..c79e17f4 --- /dev/null +++ b/old-docs/afb-bindings-overview.md @@ -0,0 +1,180 @@ + +Overview of bindings shipped with AFB-Daemon +=========================================== + +List of bindings +--------------- + +Here are the bindings shipped in the source tree: + +* Hello World +* Authentication +* Tic Tac Toe +* Audio _(2 backends: ALSA/PulseAudio)_ +* Radio _(1 backend: RTLSDR RTL2832U)_ +* Media _(1 backend: Rygel UPnP)_ + +All bindings may not be built, depending on the development libraries present on +the system at build time. + + +Detail of bindings +----------------- + +### Hello World + +A sample Hello World binding for demonstration and learning purposes. + +This binding provides a few unauthenticated requests, all beginning with +"ping", to demonstrate basic binder capabilities. + +**Verbs**: + +* _ping:_ returns a success response +* _pingfail:_ returns a failure response +* _pingnull:_ returns a success response, with an empty JSON response field +* _pingbug:_ does a memory violation (intercepted by the binder) +* _pingJson:_ returns a success response, with a complex JSON response field +* _pingevent:_ broadcasts a global event + +
+ + +### Authentication + +A sample Authentication binding for demonstration purposes. + +This binding provides a few requests to demonstrate the binder's token-based +security mechanism. + +Calling "_connect_" with a security token will initiate a session, calling +"_refresh_" will issue a new token and invalidate the previous one, calling +"_logout_" will invalidate all tokens and close the session. + +**Verbs**: + +* _ping:_ returns a success response +* _connect:_ creates a session and returns a new token +* _refresh:_ returns a new token +* _check:_ verifies the passed token is valid +* _logout:_ closes the session + +
+ + +### Tic Tac Toe + +A sample Tic Tac Toe game binding. + +This binding provides an interactive Tic Tac Toe game where the binder returns +the grid as a JSON response. + +**Verbs**: + +* _new:_ starts a new game +* _play:_ asks the server to play +* _move:_ gives a client move +* _board:_ gets the current board state, as a JSON structure +* _level_: sets the server level +* _join_: joins an existing board +* _undo_: undo the last move +* _wait_: wait for a move + +
+ + +### Audio + +A sample Audio binding with 2 backends: + +* ALSA (mandatory) +* PulseAudio (optional) + +This binding is able to initialize a specific soundcard, define volume levels, +channels (mono/stereo...), mute sound, and play a 22,050 Hz PCM stream. + +**Verbs**: + +* _ping:_ returns a success response +* _init:_ initializes backend, on the "default" sound card +* _volume:_ gets or sets volume, in % (0-100) +* _channels:_ gets or sets channels count (1-8) +* _mute:_ gets or sets the mute status (on-off) +* _play_: gets or sets the playing status (on-off) + +_(if PulseAudio development libraries are not found at build time, only ALSA +will be available)_ + +_(if a PulseAudio server is not found at runtime, the binding will dynamically +fall back to ALSA)_ + +_(a specifc backend can be forced by using this syntax before running afb-daemon +: **$ export AFB_AUDIO_OUTPUT=Alsa**)_ + +
+ + +### Radio + +A sample AM/FM Radio binding with 1 backend: + +* RTLSDR - Realtek RTL2832U dongles (mandatory) + +This binding is able to initialize specific RTL2832U dongles, switch between +AM/FM modes, define frequency, mute sound, and play sound (if combining with +the **audio** binding). + +**Verbs**: + +* _ping:_ returns a success response +* _init:_ initializes backend, looking for plugged-in devices +* _power:_ sets device power status (on-off) +* _mode:_ sets device reception mode (AM-FM) +* _freq:_ sets device frequency (in Hz) +* _mute_: sets device mute status (on-off) +* _play_: sets device playing status (on-off) + +_(if rtlsdr development libraries are not found at build time, this binding will +not be built)_ + +
+ +### Media + +A sample Media Server binding with 1 backend: + + * Rygel + +This binding is able to detect a local Rygel UPnP media server, list audio +files, select an audio file for playback, play/pause/seek in this file, upload +an audio file to the server. + +**Verbs**: + +* _ping:_ returns a success response +* _init:_ initializes backend, looking for an active local UPnP server +* _list:_ returns list of audio files, as a JSON structure +* _select:_ select an audio files, by index number (001-...) +* _play:_ plays the currently selected audio file +* _stop:_ stops the currently selected audio file +* _pause:_ pauses the currently selected audio file +* _seek:_ seeks in the currently selected audio file, in seconds +* _upload:_ uploads an audio file, with a POST request + +_(if GUPnP/GSSDP development libraries are not found at build time, this binding +will not be built)_ + +
+ + +--- +
+ +Sample command-line applications: _afb-client-demo_ (built by default) + +Sample HTML5 applications: +**test/*.html**, +**[afb-client](https://gerrit.automotivelinux.org/gerrit/gitweb?p=src/app-framework-demo.git;a=tree)**, +**[afb-radio](https://github.com/iotbzh/afb-radio)** + +Sample Qt/QML applications: *test/token-websock.qml* diff --git a/old-docs/afb-bindings-writing.md b/old-docs/afb-bindings-writing.md new file mode 100644 index 00000000..d8335086 --- /dev/null +++ b/old-docs/afb-bindings-writing.md @@ -0,0 +1,1407 @@ + +How to write a binding for AFB-DAEMON +=================================== + +Summary +------- + +The afb-daemon binders serve files through HTTP protocol and offers to +developers the capability to offer application API methods through HTTP or +WebSocket protocol. + +The bindings are used to add API to ***afb-daemon***. +This part describes how to write a binding for***afb-daemon***. + +Excepting this summary, this document target developers. + +Before moving further through an example, here after +a short overview of binder bindings fundamentals. + +### Nature of a binding + +A binding is an independent piece of software. A binding is self contain and +exposes application logic as sharable library. A binding is intended to be +dynamically loaded by ***afb-daemon*** to expose application API. + +Technically, a binder binding does not reference and is not linked with any +***afb-daemon*** library. + +### Class of bindings + +Application binder supports two kinds of bindings: application bindings and +service bindings. Technically both class of binding are equivalent and use the +same coding convention. Only sharing mode and security context diverge. + +#### Application-bindings + +Application-bindings implements the glue in between application's UI and +services. Every AGL application has a corresponding binder that typically +activates one or many bindings to interface the application logic with lower +platform services. When an application is started by the AGL application +framework, a dedicate binder is started that loads/activates application +binding(s). API expose by application-binding are executed within corresponding +application security context. + +Application bindings generally handle a unique context for a unique client. As +the application framework start a dedicated instance of afb_daemon for each AGL +application, if a given binding is used within multiple application each of +those application get a new and private instance of eventually "shared" binding. + +#### Service-bindings + +Service-bindings enable API activation within corresponding service security +context and not within calling application context. Service-bindings are +intended to run as a unique instance. Service-bindings can be shared in between +multiple clients. + +Service-bindings can either be stateless or manage client context. When managing +context each client get a private context. Sharing may either be global to the +platform (ie: GPS service) or dedicated to a given user (ie: user preferences) + +### Live cycle of bindings within ***afb-daemon*** + +Application and service bindings are loaded and activated each time a new +***afb-daemon*** is started. + +At launch time, every loaded binding initialise itself. If a single binding +initialisation fails the corresponding instance of ***afb-daemon*** aborts. + +Conversely, when a binding initialisation succeeds, it should register its +unique name as well as the list of verbs (methods name from binder point of +view) attached to the methods it exposes. + +When initialised, on request from application clients to the right API/verbs +binding methods are activated by the ***afb-daemon*** attached to the +application or service. + +At exit time, no special action is enforced by ***afb-daemon***. When a specific +actions is required at afb-daemon stop, developers should use 'atexit/on_exit' +during binding initialisation sequence to register a custom exit function. + +### Binding Content + +Afb-daemon's bindings register two classes of objects: names and functions. + +Bindings declare categories of names: + - A unique binding name to access all API exposed by this binding, + - One name for each methods/verbs provided by this binding. + +Bindings declare two categories of functions: + - function used for initialisation + - functions implementing the exposed API methods + +Afb-daemon parses URI requests to extract the API(binding name) and the +VERB(method to activate). As an example, URI **foo/bar** translates to binding +named **foo** and method named **bar**. To serve such a request, +***afb-daemon*** looks for an active binding named **foo** and then within this +binding for a method named **bar**. When found ***afb-daemon*** calls +the corresponding method with an attached parameter if any. + +Afb-daemon is case-insensitive when parsing URI. Thus **TicTacToe/Board** and +**tictactoe/board** are equivalent. + +#### The name of the binding + +The name of a given binding is also known as the name +of the API prefix that defines the binding. + +The name of a binding SHOULD be unique within a given ***afb-daemon*** instance. + +For example, when a client of ***afb-daemon*** calls a URI named **foo/bar**. +Afb-daemon extracts the prefix **foo** and the suffix **bar**. **foo** must +match a binding name and **bar** has to match a VERB attached to some method. + +#### Names of methods + +Each binding exposes a set of methods that can be called by the clients of a +given ***afb-daemon***. + +VERB's name attached to a given binding (API) MUST be unique within a binding. + +Bindings static declaration link VERBS to the corresponding methods. +When clients emit requests on a given API/VERB corresponding method is called +by ***afb-daemon***. + +#### Initialisation function + +Binding's initialisation function serves several purposes. + +1. It allows ***afb-daemon*** to control the binding version depending on +the initialisation of function name. As today, the only supported initialisation +function is **afbBindingV1Register**. This identifies version "one" of bindings. + +2. It allows bindings to initialise itself. + +3. It enables names declarations: descriptions, requirements and implementations +of exposed API/VERB. + +#### Functions instantiation of API/VERBs + +When an API/VERB is called, ***afb-daemon*** constructs a request object. Then +it passes this request object to the implementation function corresponding to +requested method, this within attached API binding. + +An implementation function receives a request object used to: get +the arguments of the request, send an answer, store session data. + +A binding MUST set an answer to every received requests. + +Nevertheless, there are two implementations, *synchronous* and *asynchronous*. +API/VERB implementation that set an answer before returning are called +*synchronous implementations*. Those that do not systematically set an answer +before returning are called *asynchronous implementations*. + +Asynchronous implementations typically launch asynchronous actions. They record +some context at request time and provide an answer to the request only at +completion of asynchronous actions. + +The Tic-Tac-Toe example +----------------------- + +This part explains how to write an afb-binding. For the sake of being practical +it uses many examples based on tic-tac-toe. This binding example is in +*bindings/samples/tic-tac-toe.c*. + +This binding is named ***tictactoe***. + +Dependencies when compiling +--------------------------- + +Afb-daemon provides a configuration file for *pkg-config*. +Typing the command + + pkg-config --cflags afb-daemon + +Print flags use for compilation: + + $ pkg-config --cflags afb-daemon + -I/opt/local/include -I/usr/include/json-c + +For linking, you should use + + $ pkg-config --libs afb-daemon + -ljson-c + +Afb-daemon automatically includes the dependency to json-c. +This is activated through **Requires** keyword in pkg-config. +While almost every binding replies on **json-c** this is not a must have +dependency. + +Internally, ***afb-daemon*** relies on **libsystemd** for its event loop, as +well as for its binding to D-Bus. Bindings developers are encouraged to leverage +**libsystemd** when possible. Nevertheless there is no hard dependency to +**libsystemd** if you do not want to use it, feel free to do so. + +> Afb-daemon bindings are fully self contained. They do not enforce dependency +on any libraries from the application framework. +> Afb-daemon dependencies requirer to run AGL bindings are given at runtime +through pointers leveraging read-only +> memory feature. + +Header files to include +----------------------- + +Binding *tictactoe* has following includes: + +```C +#define _GNU_SOURCE +#include +#include +#include +#include +``` + +Header *afb/afb-binding.h* is the only hard dependency, it includes all features +that a binding MUST HAVE. Outside of includes used to support application logic, +common external headers used within bindings are: + +- *json-c/json.h*: should be include to handle json objects; +- *systemd/sd-event.h*: should be include to access event main loop; +- *systemd/sd-bus.h*: should be include for dbus connections. + +The *tictactoe* binding does not leverage systemd features, also only json.h +is used on top of mandatory afb/afb-binding.h. + +When including *afb/afb-binding.h*, the macro **_GNU_SOURCE** MUST be +defined. + +Choosing names +-------------- + +Designers of bindings should define a unique name for every API binding +as well as for methods VERBs. They should also define names for request +arguments passed as name/value pair in URI. + +While forging names, designers should respect few rules to +ensure that created names are valid and easy to use across platforms. + +All names and strings are UTF-8 encoded. + +### Names for API (binding) + +Binding API name are checked. +All characters are authorised except: + +- the control characters (\u0000 .. \u001f) +- the characters of the set { ' ', '"', '#', '%', '&', + '\'', '/', '?', '`', '\x7f' } + +In other words the set of forbidden characters is +{ \u0000..\u0020, \u0022, \u0023, \u0025..\u0027, + \u002f, \u003f, \u0060, \u007f }. + +Afb-daemon makes no distinction between lower case +and upper case when searching for API/VERB. + +### Names for methods + +The names of methods VERBs are totally free and not checked. + +However, the validity rules for method's VERB name are the +same as for Binding API name except that the dot(.) character +is forbidden. + +Afb-daemon makes no case distinction when searching for an API by name. + +### Names for arguments + +Argument's name are not restricted and can be everything you wish. + +> Warning arguments search is case sensitive and "index" and "Index" +> are not two different arguments. + +### Forging names widely available + +The key names of javascript object can be almost +anything using the arrayed notation: + + object[key] = value + +Nevertheless this is not the case with javascript dot notation: + + object.key = value + +Using the dot notation, the key must be a valid javascript +identifier and dash(-) as well as few other reserved characters cannot be used. + +For this reason, we advise developers to chose name compatible with both +javascript and HTML notation. + +It is a good practice, even for arguments not to rely on case sensitivity. +This may reduce headache strength at debug time, especially with interpreted +language like javascript that may not warn you that a variable was not defined. + +Declaration of methods and initialisation of the bindings +--------------------------------------------------------- + +### Declaration of methods + +To be active, binding's methods should be declared to +***afb-daemon***. Furthermore, the binding itself must be recorded. + +The registration mechanism is very basic: when ***afb-daemon*** starts, +it loads all bindings listed in: command line or configuration file. + +Loading a binding follows the following steps: + +1. Afb-daemon loads the binding with *dlopen*. + +2. Afb-daemon searches for a symbol named **afbBindingV1Register** using *dlsym*. +This symbol is assumed to be the exported initialisation function of the binding. + +3. Afb-daemon builds an interface object for the binding. + +4. Afb-daemon calls the found function **afbBindingV1Register** with interface pointer +as parameter. + +5. Function **afbBindingV1Register** setups the binding and initialises it. + +6. Function **afbBindingV1Register** returns the pointer to a structure +describing the binding: version, name (prefix or API name), and list of methods. + +7. Afb-daemon checks that the returned version and name can be managed. +If so, binding and its methods are register to become usable as soon as +***afb-daemon*** initialisation is finished. + +### Initialisation of bindings + +The bindings initialisation is the final step made at the end of declaration of +methods. This will initialize the binding and make its ***afb-daemon***'s +interface fully functional. + +So, afb-daemon binder call **afbBindingV1ServiceInit** as final step to a +binding. This will allows the binding to call features in its name and as saw in +[Binder events guide](afb-events-guide.md) you can create an event only at this +moment and not before. Before that it will fail because afb-daemon doesn't know +the binding name. + +**afbBindingV1ServiceInit** is defined as below: + +```C +/* + * When a binding have an exported implementation of the + * function 'afbBindingV1ServiceInit', defined below, + * the framework calls it for initialising the service after + * registration of all bindings. + * + * The object 'service' should be recorded. It has functions that + * allows the binding to call features with its own personality. + * + * The function should return 0 in case of success or, else, should return + * a negative value. + */ +extern int afbBindingV1ServiceInit(struct afb_service service); +``` + +### Application binding example: tic-tac-toe + +If we continue our tic-tac-toe example, here after the code used for +**afbBindingV1Register** implementation from binding *tic-tac-toe*: + +```C +/* + * activation function for registering the binding called by afb-daemon + */ +const struct afb_binding *afbBindingV1Register(const struct afb_binding_interface *itf) +{ + afbitf = itf; // records the interface for accessing afb-daemon + return &binding_description; // returns the description of the binding +} +``` + +It is a very minimal initialisation function because *tic-tac-toe* binding doesn't +have any application related initialisation step. It merely record daemon's interface +and returns its description. + +The variable **afbitf** is a binding global variable. It keeps the +interface to ***afb-daemon*** that should be used for logging and pushing events. +Here is its declaration: + +```C +/* + * the interface to afb-daemon + */ +const struct afb_binding_interface *afbitf; +``` + +The description of the binding is defined here after. + +```C +/* + * array of the methods exported to afb-daemon + */ +static const struct afb_verb_desc_v1 binding_methods[] = { + /* VERB'S NAME SESSION MANAGEMENT FUNCTION TO CALL SHORT DESCRIPTION */ + { .name= "new", .session= AFB_SESSION_NONE, .callback= new, .info= "Starts a new game" }, + { .name= "play", .session= AFB_SESSION_NONE, .callback= play, .info= "Asks the server to play" }, + { .name= "move", .session= AFB_SESSION_NONE, .callback= move, .info= "Tells the client move" }, + { .name= "board", .session= AFB_SESSION_NONE, .callback= board, .info= "Get the current board" }, + { .name= "level", .session= AFB_SESSION_NONE, .callback= level, .info= "Set the server level" }, + { .name= "join", .session= AFB_SESSION_CHECK,.callback= join, .info= "Join a board" }, + { .name= "undo", .session= AFB_SESSION_NONE, .callback= undo, .info= "Undo the last move" }, + { .name= "wait", .session= AFB_SESSION_NONE, .callback= wait, .info= "Wait for a change" }, + { .name= NULL } /* marker for end of the array */ +}; + +/* + * description of the binding for afb-daemon + */ +static const struct afb_binding binding_description = +{ + /* description conforms to VERSION 1 */ + .type= AFB_BINDING_VERSION_1, + .v1= { /* fills the v1 field of the union when AFB_BINDING_VERSION_1 */ + .prefix= "tictactoe", /* the API name (or binding name or prefix) */ + .info= "Sample tac-tac-toe game", /* short description of of the binding */ + .methods = binding_methods /* the array describing the methods of the API */ + } +}; +``` + +The structure **binding_description** describes the binding. +It declares the type and version of the binding, its name, a short description +and its methods list. + +The list of methods is an array of structures describing the methods and terminated by a NULL marker. + +In version one of afb-damon binding, a method description contains 4 fields: + +- the name of the method, + +- the session management flags, + +- the implementation function to be call for the method, + +- a short description. + +The structure describing methods is defined as follows: + +```C +/* + * Description of one method of the API provided by the binding + * This enumeration is valid for bindings of type 1 + */ +struct afb_verb_desc_v1 +{ + const char *name; /* name of the method */ + enum AFB_session_v1 session; /* authorisation and session requirements of the method */ + void (*callback)(struct afb_req req); /* callback function implementing the method */ + const char *info; /* textual description of the method */ +}; +``` + +For technical reasons, the enumeration **enum AFB_session_v1** is not exactly an +enumeration but the wrapper of constant definitions that can be mixed using bitwise or +(the C operator |). + +The constants that can bit mixed are: + +Constant name | Meaning +-------------------------|------------------------------------------------------------- +**AFB_SESSION_CREATE** | Equals to AFB_SESSION_LOA_EQ_0|AFB_SESSION_RENEW +**AFB_SESSION_CLOSE** | Closes the session after the reply and set the LOA to 0 +**AFB_SESSION_RENEW** | Refreshes the token of authentification +**AFB_SESSION_CHECK** | Just requires the token authentification +**AFB_SESSION_LOA_LE_0** | Requires the current LOA to be lesser then or equal to 0 +**AFB_SESSION_LOA_LE_1** | Requires the current LOA to be lesser then or equal to 1 +**AFB_SESSION_LOA_LE_2** | Requires the current LOA to be lesser then or equal to 2 +**AFB_SESSION_LOA_LE_3** | Requires the current LOA to be lesser then or equal to 3 +**AFB_SESSION_LOA_GE_0** | Requires the current LOA to be greater then or equal to 0 +**AFB_SESSION_LOA_GE_1** | Requires the current LOA to be greater then or equal to 1 +**AFB_SESSION_LOA_GE_2** | Requires the current LOA to be greater then or equal to 2 +**AFB_SESSION_LOA_GE_3** | Requires the current LOA to be greater then or equal to 3 +**AFB_SESSION_LOA_EQ_0** | Requires the current LOA to be equal to 0 +**AFB_SESSION_LOA_EQ_1** | Requires the current LOA to be equal to 1 +**AFB_SESSION_LOA_EQ_2** | Requires the current LOA to be equal to 2 +**AFB_SESSION_LOA_EQ_3** | Requires the current LOA to be equal to 3 + +If any of this flag is set, ***afb-daemon*** requires an authentication token +as if **AFB_SESSION_CHECK** flag was also set. + +The special value **AFB_SESSION_NONE** is zero and can be used to bypass token check. + +> Note that **AFB_SESSION_CREATE** and **AFB_SESSION_CLOSE** might be removed in later versions. + +Sending messages to the log system +---------------------------------- + +Afb-daemon provides 4 levels of verbosity and 5 methods for logging messages. + +The verbosity is managed. Options allow the change the verbosity of ***afb-daemon*** +and the verbosity of the bindings can be set binding by binding. + +The methods for logging messages are defined as macros that test the +verbosity level and that call the real logging function only if the +message must be output. This avoid evaluation of arguments of the +formatting messages if the message must not be output. + +### Verbs for logging messages + +The 5 logging methods are: + +Macro | Verbosity | Meaning | syslog level +--------|:---------:|-----------------------------------|:-----------: +ERROR | 0 | Error conditions | 3 +WARNING | 1 | Warning conditions | 4 +NOTICE | 1 | Normal but significant condition | 5 +INFO | 2 | Informational | 6 +DEBUG | 3 | Debug-level messages | 7 + +You can note that the 2 methods **WARNING** and **NOTICE** have the same level +of verbosity. But they don't have the same *syslog level*. It means that +they are output with a different level on the logging system. + +All of these methods have the same signature: + +```C +void ERROR(const struct afb_binding_interface *afbitf, const char *message, ...); +``` + +The first argument **afbitf** is the interface to afb daemon that the +binding received at initialisation time when **afbBindingV1Register** is called. + +The second argument **message** is a formatting string compatible with printf/sprintf. + +The remaining arguments are arguments of the formating message like with printf. + +### Managing verbosity + +Depending on the level of verbosity, the messages are output or not. +The following table explains what messages will be output depending +ont the verbosity level. + +Level of verbosity | Outputed macro +:-----------------:|-------------------------- +0 | ERROR +1 | ERROR + WARNING + NOTICE +2 | ERROR + WARNING + NOTICE + INFO +3 | ERROR + WARNING + NOTICE + INFO + DEBUG + +### Output format and destination + +The syslog level is used for forging a prefix to the message. +The prefixes are: + +syslog level | prefix +:-----------:|--------------- +0 | <0> EMERGENCY +1 | <1> ALERT +2 | <2> CRITICAL +3 | <3> ERROR +4 | <4> WARNING +5 | <5> NOTICE +6 | <6> INFO +7 | <7> DEBUG + + +The message is pushed to standard error. +The final destination of the message depends on how systemd service +was configured through its variable **StandardError**. It can be +journal, syslog or kmsg. (See man sd-daemon). + +Sending events +-------------- + +Specific documentation exists about [sending events](afb-events-guide.md). + +The binding *tic-tac-toe* broadcasts events when the board changes. This is done +in the function ***changed***: + +```C +/* + * signals a change of the board + */ +static void changed(struct board *board, const char *reason) +{ + ... + struct json_object *description; + + /* get the description */ + description = describe(board); + + ... + + afb_daemon_broadcast_event(afbitf->daemon, reason, description); +} +``` + +The description of the changed board is pushed via the daemon interface. + +Within binding *tic-tac-toe*, *reason* indicates the origin of +the change. In function **afb_daemon_broadcast_event** the second +parameter is the name of broadcasted event. The third argument is the +object that is transmitted with the event. + +Function **afb_daemon_broadcast_event** is defined here after: + +```C +/* + * Broadcasts widely the event of 'name' with the data 'object'. + * 'object' can be NULL. + * 'daemon' MUST be the daemon given in interface when activating the binding. + * + * For convenience, the function calls 'json_object_put' for 'object'. + * Thus, in the case where 'object' should remain available after + * the function returns, the function 'json_object_get' shall be used. + */ +void afb_daemon_broadcast_event(struct afb_daemon daemon, const char *name, struct json_object *object); +``` + +> Be aware, as with reply functions **object** is automatically released using +> **json_object_put** when using this function. Call **json_object_get** before +> calling **afb_daemon_broadcast_event** to keep **object** available +> after function returns. + +Event name received by listeners is prefixed with binding name. +So when a change occurs after a move, the reason is **move** and every clients +receive an event **tictactoe/move**. + +> Note that nothing is said about case sensitivity of event names. +> However, the event is always prefixed with the name that the binding +> declared, with the same case, followed with a slash /. +> Thus it is safe to compare event using a case sensitive comparison. + +Writing a synchronous method implementation +----------------------------------------- + +The method **tictactoe/board** is a synchronous implementation. +Here is its listing: + +```C +/* + * get the board + */ +static void board(struct afb_req req) +{ + struct board *board; + struct json_object *description; + + /* retrieves the context for the session */ + board = board_of_req(req); + INFO(afbitf, "method 'board' called for boardid %d", board->id); + + /* describe the board */ + description = describe(board); + + /* send the board's description */ + afb_req_success(req, description, NULL); +} +``` + +This example shows many aspects of a synchronous +method implementation. Let summarise it: + +1. The function **board_of_req** retrieves the context stored +for the binding: the board. + +2. The macro **INFO** sends a message of kind *INFO* +to the logging system. The global variable named **afbitf** +used represents the interface to ***afb-daemon***. + +3. The function **describe** creates a json_object representing +the board. + +4. The function **afb_req_success** sends the reply, attaching to +it the object *description*. + +### The incoming request + +For any implementation, the request is received by a structure of type +**struct afb_req**. + +> Note that this is a PLAIN structure, not a pointer to a structure. + +The definition of **struct afb_req** is: + +```C +/* + * Describes the request by bindings from afb-daemon + */ +struct afb_req { + const struct afb_req_itf *itf; /* the interfacing functions */ + void *closure; /* the closure for functions */ +}; +``` + +It contains two pointers: first one *itf*, points to functions used +to handle internal request. Second one *closure* point onto function closure. + +> The structure must never be used directly. +> Instead developer should use the intended functions provided +> by ***afb-daemon*** as described here after. + +*req* is used to get arguments of the request, to send +answer, to store session data. + +This object and its interface is defined and documented +in the file names *afb/afb-req-itf.h* + +The above example uses twice *req* object request. + +The first time, to retrieve the board attached to the session of the request. + +The second time, to send the reply: an object that describes the current board. + +### Associating a client context to a session + +When *tic-tac-toe* binding receives a request, it musts get +the board describing the game associated to the session. + +For a binding, having data associated to a session is common. +This data is called "binding context" for the session. +Within *tic-tac-toe* binding the context is the board. + +Requests *afb_req* offer four functions for storing and retrieving session +associated context. + +These functions are: + +- **afb_req_context_get**: + retrieves context data stored for current binding. + +- **afb_req_context_set**: + store context data of current binding. + +- **afb_req_context**: + if exist retrieves context data of current binding. + if context does not yet exist, creates a new context and store it. + +- **afb_req_context_clear**: + reset the stored context data. + +The binding *tictactoe* use a convenient function to retrieve +its context: the board. This function is *board_of_req*: + +```C +/* + * retrieves the board of the request + */ +static inline struct board *board_of_req(struct afb_req req) +{ + return afb_req_context(req, (void*)get_new_board, (void*)release_board); +} +``` + +The function **afb_req_context** ensures an existing context +for the session of the request. +Its two last arguments are functions to allocate and free context. +Note function type casts to avoid compilation warnings. + +Here is the definition of the function **afb_req_context** + +```C +/* + * Gets the pointer stored by the binding for the session of 'req'. + * If the stored pointer is NULL, indicating that no pointer was + * already stored, afb_req_context creates a new context by calling + * the function 'create_context' and stores it with the freeing function + * 'free_context'. + */ +static inline void *afb_req_context(struct afb_req req, void *(*create_context)(), void (*free_context)(void*)) +{ + void *result = afb_req_context_get(req); + if (result == NULL) { + result = create_context(); + afb_req_context_set(req, result, free_context); + } + return result; +} +``` + +The second argument if the function that creates the context. +For binding *tic-tac-toe* (function **get_new_board**). +The function **get_new_board** creates a new board and set usage its count to 1. +The boards are checking usage count to free resources when not used. + +The third argument is a function that frees context resources. +For binding *tic-tac-toe* (function **release_board**). +The function **release_board** decrease usage count of the board passed in +argument. When usage count falls to zero, data board are freed. + +Definition of other functions dealing with contexts: + +```C +/* + * Gets the pointer stored by the binding for the session of 'req'. + * When the binding has not yet recorded a pointer, NULL is returned. + */ +void *afb_req_context_get(struct afb_req req); + +/* + * Stores for the binding the pointer 'context' to the session of 'req'. + * The function 'free_context' will be called when the session is closed + * or if binding stores an other pointer. + */ +void afb_req_context_set(struct afb_req req, void *context, void (*free_context)(void*)); + +/* + * Frees the pointer stored by the binding for the session of 'req' + * and sets it to NULL. + * + * Shortcut for: afb_req_context_set(req, NULL, NULL) + */ +static inline void afb_req_context_clear(struct afb_req req) +{ + afb_req_context_set(req, NULL, NULL); +} +``` + +### Sending reply to a request + +Two kinds of replies: successful or failure. + +> Sending a reply to a request MUST be done once and only once. + +It exists two functions for "success" replies: **afb_req_success** and **afb_req_success_f**. + +```C +/* + * Sends a reply of kind success to the request 'req'. + * The status of the reply is automatically set to "success". + * Its send the object 'obj' (can be NULL) with an + * informationnal comment 'info (can also be NULL). + * + * For convenience, the function calls 'json_object_put' for 'obj'. + * Thus, in the case where 'obj' should remain available after + * the function returns, the function 'json_object_get' shall be used. + */ +void afb_req_success(struct afb_req req, struct json_object *obj, const char *info); + +/* + * Same as 'afb_req_success' but the 'info' is a formatting + * string followed by arguments. + * + * For convenience, the function calls 'json_object_put' for 'obj'. + * Thus, in the case where 'obj' should remain available after + * the function returns, the function 'json_object_get' shall be used. + */ +void afb_req_success_f(struct afb_req req, struct json_object *obj, const char *info, ...); +``` + +It exists two functions for "failure" replies: **afb_req_fail** and **afb_req_fail_f**. + +```C +/* + * Sends a reply of kind failure to the request 'req'. + * The status of the reply is set to 'status' and an + * informational comment 'info' (can also be NULL) can be added. + * + * Note that calling afb_req_fail("success", info) is equivalent + * to call afb_req_success(NULL, info). Thus even if possible it + * is strongly recommended to NEVER use "success" for status. + * + * For convenience, the function calls 'json_object_put' for 'obj'. + * Thus, in the case where 'obj' should remain available after + * the function returns, the function 'json_object_get' shall be used. + */ +void afb_req_fail(struct afb_req req, const char *status, const char *info); + +/* + * Same as 'afb_req_fail' but the 'info' is a formatting + * string followed by arguments. + * + * For convenience, the function calls 'json_object_put' for 'obj'. + * Thus, in the case where 'obj' should remain available after + * the function returns, the function 'json_object_get' shall be used. + */ +void afb_req_fail_f(struct afb_req req, const char *status, const char *info, ...); +``` + +> For convenience, these functions automatically call **json_object_put** to release **obj**. +> Because **obj** usage count is null after being passed to a reply function, it SHOULD not be used anymore. +> If exceptionally **obj** needs to remain usable after reply function then using **json_object_get** on **obj** +> to increase usage count and cancels the effect the **json_object_put** is possible. + +Getting argument of invocation +------------------------------ + +Many methods expect arguments. Afb-daemon's bindings +retrieve arguments by name and not by position. + +Arguments are passed by requests through either HTTP +or WebSockets. + +For example, the method **join** of binding **tic-tac-toe** +expects one argument: the *boardid* to join. Here is an extract: + +```C +/* + * Join a board + */ +static void join(struct afb_req req) +{ + struct board *board, *new_board; + const char *id; + + /* retrieves the context for the session */ + board = board_of_req(req); + INFO(afbitf, "method 'join' called for boardid %d", board->id); + + /* retrieves the argument */ + id = afb_req_value(req, "boardid"); + if (id == NULL) + goto bad_request; + ... +``` + +The function **afb_req_value** searches in the request *req* +for argument name passed in the second argument. When argument name +is not passed, **afb_req_value** returns NULL. + +> The search is case sensitive and *boardid* is not equivalent to *BoardId*. +> Nevertheless having argument names that only differ by name case is not a good idea. + +### Basic functions for querying arguments + +The function **afb_req_value** is defined here after: + +```C +/* + * Gets from the request 'req' the string value of the argument of 'name'. + * Returns NULL if when there is no argument of 'name'. + * Returns the value of the argument of 'name' otherwise. + * + * Shortcut for: afb_req_get(req, name).value + */ +static inline const char *afb_req_value(struct afb_req req, const char *name) +{ + return afb_req_get(req, name).value; +} +``` + +It is defined as a shortcut to call the function **afb_req_get**. +That function is defined here after: + +```C +/* + * Gets from the request 'req' the argument of 'name'. + * Returns a PLAIN structure of type 'struct afb_arg'. + * When the argument of 'name' is not found, all fields of result are set to NULL. + * When the argument of 'name' is found, the fields are filled, + * in particular, the field 'result.name' is set to 'name'. + * + * There is a special name value: the empty string. + * The argument of name "" is defined only if the request was made using + * an HTTP POST of Content-Type "application/json". In that case, the + * argument of name "" receives the value of the body of the HTTP request. + */ +struct afb_arg afb_req_get(struct afb_req req, const char *name); +``` + +That function takes 2 parameters: the request and the name +of the argument to retrieve. It returns a PLAIN structure of +type **struct afb_arg**. + +There is a special name that is defined when the request is +of type HTTP/POST with a Content-Type being application/json. +This name is **""** (the empty string). In that case, the value +of this argument of empty name is the string received as a body +of the post and is supposed to be a JSON string. + +The definition of **struct afb_arg** is: + +```C +/* + * Describes an argument (or parameter) of a request + */ +struct afb_arg { + const char *name; /* name of the argument or NULL if invalid */ + const char *value; /* string representation of the value of the argument */ + /* original filename of the argument if path != NULL */ + const char *path; /* if not NULL, path of the received file for the argument */ + /* when the request is finalized this file is removed */ +}; +``` + +The structure returns the data arguments that are known for the +request. This data include a field named **path**. This **path** +can be accessed using the function **afb_req_path** defined here after: + +```C +/* + * Gets from the request 'req' the path for file attached to the argument of 'name'. + * Returns NULL if when there is no argument of 'name' or when there is no file. + * Returns the path of the argument of 'name' otherwise. + * + * Shortcut for: afb_req_get(req, name).path + */ +static inline const char *afb_req_path(struct afb_req req, const char *name) +{ + return afb_req_get(req, name).path; +} +``` + +The path is only defined for HTTP/POST requests that send file. + +### Arguments for received files + +As it is explained above, clients can send files using HTTP/POST requests. + +Received files are attached to "file" argument name. For example, the +following HTTP fragment (from test/sample-post.html) +will send an HTTP/POST request to the method +**post/upload-image** with 2 arguments named *file* and +*hidden*. + +```html +

Sample Post File

+
+ + +
+ +
+``` + +Argument named **file** should have both its value and path defined. + +The value is the name of the file as it was set by the HTTP client. +Generally it is the filename on client side. + +The path is the effective path of saved file on the temporary local storage +area of the application. This is a randomly generated and unique filename. +It is not linked with the original filename as used on client side. + +After success the binding can use the uploaded file directly from local storage path with no restriction: +read, write, remove, copy, rename... +Nevertheless when request reply is set and query terminated, the uploaded temporary file at +path is destroyed. + +### Arguments as a JSON object + +Bindings may also request every arguments of a given call as one single object. +This feature is provided by the function **afb_req_json** defined here after: + +```C +/* + * Gets from the request 'req' the json object hashing the arguments. + * The returned object must not be released using 'json_object_put'. + */ +struct json_object *afb_req_json(struct afb_req req); +``` + +It returns a json object. This object depends on how the request was built: + +- For HTTP requests, this json object uses key names mapped on argument name. +Values are either string for common arguments or object ie: { "file": "...", "path": "..." } + +- For WebSockets requests, returned directly the object as provided by the client. + +> In fact, for Websockets requests, the function **afb_req_value** +> can be seen as a shortcut to +> ***json_object_get_string(json_object_object_get(afb_req_json(req), name))*** + + +Writing an asynchronous method implementation +------------------------------------------- + +The *tic-tac-toe* example allows two clients or more to share the same board. +This is implemented by the method **join** that illustrated partly how to +retrieve arguments. + +When two or more clients are sharing a same board, one of them can wait +until the state of the board changes, but this could also be implemented using +events because an event is generated each time the board changes. + +In this case, the reply to the wait is sent only when the board changes. +See the diagram below: + +![tic-tac-toe_diagram][tic-tac-toe_diagram] + +Here, this is an invocation of the binding by an other client that +unblock the suspended *wait* call. +Nevertheless in most case this should be a timer, a hardware event, a sync with +a concurrent process or thread, ... + +Common case of an asynchronous implementation. + +Here is the listing of the function **wait**: + +```C +static void wait(struct afb_req req) +{ + struct board *board; + struct waiter *waiter; + + /* retrieves the context for the session */ + board = board_of_req(req); + INFO(afbitf, "method 'wait' called for boardid %d", board->id); + + /* creates the waiter and enqueues it */ + waiter = calloc(1, sizeof *waiter); + waiter->req = req; + waiter->next = board->waiters; + afb_req_addref(req); + board->waiters = waiter; +} +``` + +After retrieving the board, the function adds a new waiter to +waiters list and returns without setting a reply. + +Before returning, it increases **req** request's reference count using **afb_req_addref** function. + +> When a method returns without setting a reply, +> it **MUST** increment request's reference count +> using **afb_req_addref**. If unpredictable behaviour may pop up. + +Later, when a board changes, it calls *tic-tac-toe* **changed** function +with reason of change in parameter. + +Here is the full listing of the function **changed**: + +```C +/* + * signals a change of the board + */ +static void changed(struct board *board, const char *reason) +{ + struct waiter *waiter, *next; + struct json_object *description; + + /* get the description */ + description = describe(board); + + waiter = board->waiters; + board->waiters = NULL; + while (waiter != NULL) { + next = waiter->next; + afb_req_success(waiter->req, json_object_get(description), reason); + afb_req_unref(waiter->req); + free(waiter); + waiter = next; + } + + afb_event_sender_push(afb_daemon_get_event_sender(afbitf->daemon), reason, description); +} +``` + +The list of waiters is walked and a reply is sent to each waiter. +After sending the reply, the reference count of the request +is decremented using **afb_req_unref** to allow resources to be freed. + +> The reference count **MUST** be decremented using **afb_req_unref** to free +> resources and avoid memory leaks. +> This usage count decrement should happen **AFTER** setting reply or +> bad things may happen. + +Sending messages to the log system +---------------------------------- + +Afb-daemon provides 4 levels of verbosity and 5 methods for logging messages. + +The verbosity is managed. Options allow the change the verbosity of ***afb-daemon*** +and the verbosity of the bindings can be set binding by binding. + +The methods for logging messages are defined as macros that test the +verbosity level and that call the real logging function only if the +message must be output. This avoid evaluation of arguments of the +formatting messages if the message must not be output. + +### Verbs for logging messages + +The 5 logging methods are: + +Macro | Verbosity | Meaning | syslog level +--------|:---------:|-----------------------------------|:-----------: +ERROR | 0 | Error conditions | 3 +WARNING | 1 | Warning conditions | 4 +NOTICE | 1 | Normal but significant condition | 5 +INFO | 2 | Informational | 6 +DEBUG | 3 | Debug-level messages | 7 + +You can note that the 2 methods **WARNING** and **NOTICE** have the same level +of verbosity. But they don't have the same *syslog level*. It means that +they are output with a different level on the logging system. + +All of these methods have the same signature: + +```C +void ERROR(const struct afb_binding_interface *afbitf, const char *message, ...); +``` + +The first argument **afbitf** is the interface to afb daemon that the +binding received at initialisation time when **afbBindingV1Register** is called. + +The second argument **message** is a formatting string compatible with printf/sprintf. + +The remaining arguments are arguments of the formating message like with printf. + +### Managing verbosity + +Depending on the level of verbosity, the messages are output or not. +The following table explains what messages will be output depending +ont the verbosity level. + +Level of verbosity | Outputed macro +:-----------------:|-------------------------- +0 | ERROR +1 | ERROR + WARNING + NOTICE +2 | ERROR + WARNING + NOTICE + INFO +3 | ERROR + WARNING + NOTICE + INFO + DEBUG + +### Output format and destination + +The syslog level is used for forging a prefix to the message. +The prefixes are: + +syslog level | prefix +:-----------:|--------------- +0 | <0> EMERGENCY +1 | <1> ALERT +2 | <2> CRITICAL +3 | <3> ERROR +4 | <4> WARNING +5 | <5> NOTICE +6 | <6> INFO +7 | <7> DEBUG + + +The message is pushed to standard error. +The final destination of the message depends on how systemd service +was configured through its variable **StandardError**. It can be +journal, syslog or kmsg. (See man sd-daemon). + +Sending events +-------------- + +Since version 0.5, bindings can broadcast events to any potential listener. +As today only unattended events are supported. Targeted events are expected for +next coming version. + +The binding *tic-tac-toe* broadcasts events when the board changes. +This is done in the function **changed**: + +```C +/* + * signals a change of the board + */ +static void changed(struct board *board, const char *reason) +{ + ... + struct json_object *description; + + /* get the description */ + description = describe(board); + + ... + + afb_daemon_broadcast_event(afbitf->daemon, reason, description); +} +``` + +The description of the changed board is pushed via the daemon interface. + +Within binding *tic-tac-toe*, *reason* indicates the origin of +the change. In function **afb_daemon_broadcast_event** the second +parameter is the name of broadcasted event. The third argument is the +object that is transmitted with the event. + +Function **afb_daemon_broadcast_event** is defined here after: + +```C +/* + * Broadcasts widely the event of 'name' with the data 'object'. + * 'object' can be NULL. + * 'daemon' MUST be the daemon given in interface when activating the binding. + * + * For convenience, the function calls 'json_object_put' for 'object'. + * Thus, in the case where 'object' should remain available after + * the function returns, the function 'json_object_get' shall be used. + */ +void afb_daemon_broadcast_event(struct afb_daemon daemon, const char *name, struct json_object *object); +``` + +> Be aware, as with reply functions **object** is automatically released using +> **json_object_put** when using this function. Call **json_object_get** before +> calling **afb_daemon_broadcast_event** to keep **object** available +> after function returns. + +Event name received by listeners is prefixed with binding name. +So when a change occurs after a move, the reason is **move** and every clients +receive an event **tictactoe/move**. + +> Note that nothing is said about case sensitivity of event names. +> However, the event is always prefixed with the name that the binding +> declared, with the same case, followed with a slash /. +> Thus it is safe to compare event using a case sensitive comparison. + +How to build a binding +--------------------- + +Afb-daemon provides a *pkg-config* configuration file that can be +queried by providing ***afb-daemon*** in command line arguments. +This configuration file provides data that should be used +for bindings compilation. Examples: + +```bash +$ pkg-config --cflags afb-daemon +$ pkg-config --libs afb-daemon +``` + +### Example for cmake meta build system + +This example is the extract for building the binding *afm-main* using *CMAKE*. + +```cmake +pkg_check_modules(afb afb-daemon) +if(afb_FOUND) + message(STATUS "Creation afm-main-binding for AFB-DAEMON") + add_library(afm-main-binding MODULE afm-main-binding.c) + target_compile_options(afm-main-binding PRIVATE ${afb_CFLAGS}) + target_include_directories(afm-main-binding PRIVATE ${afb_INCLUDE_DIRS}) + target_link_libraries(afm-main-binding utils ${afb_LIBRARIES}) + set_target_properties(afm-main-binding PROPERTIES + PREFIX "" + LINK_FLAGS "-Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/afm-main-binding.export-map" + ) + install(TARGETS afm-main-binding LIBRARY DESTINATION ${binding_dir}) +else() + message(STATUS "Not creating the binding for AFB-DAEMON") +endif() +``` + +Let now describe some of these lines. + +```cmake +pkg_check_modules(afb afb-daemon) +``` + +This first lines searches to the *pkg-config* configuration file for +**afb-daemon**. Resulting data are stored in the following variables: + +Variable | Meaning +------------------|------------------------------------------------ +afb_FOUND | Set to 1 if afb-daemon binding development files exist +afb_LIBRARIES | Only the libraries (w/o the '-l') for compiling afb-daemon bindings +afb_LIBRARY_DIRS | The paths of the libraries (w/o the '-L') for compiling afb-daemon bindings +afb_LDFLAGS | All required linker flags for compiling afb-daemon bindings +afb_INCLUDE_DIRS | The '-I' preprocessor flags (w/o the '-I') for compiling afb-daemon bindings +afb_CFLAGS | All required cflags for compiling afb-daemon bindings + +If development files are found, the binding can be added to the set of +target to build. + +```cmake +add_library(afm-main-binding MODULE afm-main-binding.c) +``` + +This line asks to create a shared library having a single +source file named afm-main-binding.c to be compiled. +The default name of the created shared object is +**libafm-main-binding.so**. + +```cmake +set_target_properties(afm-main-binding PROPERTIES + PREFIX "" + LINK_FLAGS "-Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/afm-main-binding.export-map" +) +``` + +This lines are doing two things: + +1. It renames the built library from **libafm-main-binding.so** to **afm-main-binding.so** +by removing the implicitly added prefix *lib*. This step is not mandatory +because afb-daemon doesn't check names of files at load time. +The only filename convention used by afb-daemon relates to **.so** termination. +*.so pattern is used when afb-daemon automatically discovers binding from a directory hierarchy. + +2. It applies a version script at link time to only export the reserved name +**afbBindingV1Register** for registration entry point. By default, when building +a shared library linker exports all the public symbols (C functions that are not **static**). + +Next line are: + +```cmake +target_include_directories(afm-main-binding PRIVATE ${afb_INCLUDE_DIRS}) +target_link_libraries(afm-main-binding utils ${afb_LIBRARIES}) +``` + +As you can see it uses the variables computed by ***pkg_check_modules(afb afb-daemon)*** +to configure the compiler and the linker. + +### Exporting the function afbBindingV1Register + +The function **afbBindingV1Register** MUST be exported. This can be achieved +using a version script at link time. Here after is a version script used for +*tic-tac-toe* (bindings/samples/export.map). + + { global: afbBindingV1Register; local: *; }; + +This sample [version script](https://sourceware.org/binutils/docs-2.26/ld/VERSION.html#VERSION) +exports as global the symbol *afbBindingV1Register* and hides any +other symbols. + +This version script is added to the link options using the +option **--version-script=export.map** is given directly to the +linker or using the option **-Wl,--version-script=export.map** +when the option is given to the C compiler. + +### Building within yocto + +Adding a dependency to afb-daemon is enough. See below: + + DEPENDS += " afb-daemon " + + +[tic-tac-toe_diagram]: pictures/tic-tac-toe.svg diff --git a/old-docs/afb-daemon-vocabulary.md b/old-docs/afb-daemon-vocabulary.md new file mode 100644 index 00000000..c3b7c1ea --- /dev/null +++ b/old-docs/afb-daemon-vocabulary.md @@ -0,0 +1,98 @@ + +Vocabulary for AFB-DAEMON +========================= + +## Binding + +A shared library object intended to add a functionality to an afb-daemon +instance. It implements an API and may provide a service. + +Binding made for services can have specific entry point called after +initialisation and before serving. + +## Event + +Message with data propagated from the services to the client and not expecting +any reply. + +The current implementation allows to widely broadcast events to all clients. + +## Level of assurance (LOA) + +This level that can be from 0 to 3 represent the level of +assurance that the services can expect from the session. + +The exact definition of the meaning of these levels and how to use it remains to +be achieved. + +## Plugin + +Old name for binding, see binding. + +## Request + +A request is an invocation by a client to a binding method using a message +transferred through some protocol: HTTP, WebSocket, DBUS... and served by +***afb-daemon*** + +## Reply/Response + +This is a message sent to client as the result of the request. + +## Service + +Service are made of bindings running by their side on their binder. +It can serve many client. Each one attached to one session. + +The framework establishes connection between the services and +the clients. Using DBus currently but other protocols are considered. + +## Session + +A session is meant to be the unique instance context of a client, +which identify that instance across requests. + +Each session has an identifier. Session identifier generated by afb-daemon are +UUIDs. + +Internally, afb-daemon offers a mechanism to attach data to sessions. +When the session is closed or disappears, the data attached to that session +are freed. + +## Token + +The token is an identifier that the client must give to be authenticated. + +At start, afb-daemon get an initial token. This initial token must be presented +by incoming client to be authenticated. + +A token is valid only for a period. + +The token must be renewed periodically. When the token is renewed, afb-daemon +sends the new token to the client. + +Tokens generated by afb-daemon are UUIDs. + +## UUID + +It stand for Universal Unique IDentifier. + +It is designed to create identifier in a way that avoid has much as possible +conflicts. It means that if two different instances create an UUID, the +probability that they create the same UUID is very low, near to zero. + +## x-afb-reqid + +Argument name that can be used with HTTP request. When this argument is given, +it is automatically added to the "request" object of the answer. + +## x-afb-token + +Argument name meant to give the token without ambiguity. +You can also use the name **token** but it may conflicts with others arguments. + +## x-afb-uuid + +Argument name for giving explicitly the session identifier without ambiguity. +You can also use the name **uuid** but it may conflicts with others arguments. + diff --git a/old-docs/afb-events-guide.md b/old-docs/afb-events-guide.md new file mode 100644 index 00000000..aaa09be1 --- /dev/null +++ b/old-docs/afb-events-guide.md @@ -0,0 +1,475 @@ +Guide for developing with events +================================ + +Signaling agents are services that send events to any clients that +subscribed for receiving it. The sent events carry any data. + +To have a good understanding of how to write a signaling agent, the +actions of subscribing, unsubscribing, producing, sending and receiving +events must be described and explained. + +Overview of events +------------------ + +The basis of a signaling agent is shown in the following figure: + +![scenario of using events](pictures/signaling-basis.svg) + +This figure shows the main role of the signaling framework for the events +propagation. + +For people not familiar with the framework, a signaling agent and +a “binding” are similar. + +### Subscribing and unsubscribing + +Subscribing is the action that makes a client able to receive data from a +signaling agent. Subscription must create resources for generating the data, and +for delivering the data to the client. These two aspects are not handled by the +same piece of software. Generating the data is the responsibility of the +developer of the signaling agent while delivering the data is handled by the +framework. + +When a client subscribes for data, the agent must: + +1. check that the subscription request is correct; +2. establish the computation chain of the required data, if not already + done; +3. create a named event for the computed data, if not already done; +4. ask the framework to establish the subscription to the event for the + request; +5. optionally give indications about the event in the reply to + the client. + +The first two steps are not involving the framework. They are linked to +the business logic of the binding. The request can be any description of +the requested data and the computing stream can be of any nature, this +is specific to the binding. + +As said before, the framework uses and integrates **libsystemd** and its event +loop. Within the framework, **libsystemd** is the standard API/library for +bindings expecting to setup and handle I/O, timer or signal events. + +Steps 3 and 4 are bound to the framework. + +The agent must create an object for handling the propagation of produced +data to its clients. That object is called “event” in the framework. An +event has a name that allows clients to distinguish it from other +events. + +Events are created using the ***afb\_daemon\_make\_event*** function +that takes the name of the event. Example: + +```C + event = afb_daemon_make_event(afb_daemon, name); +``` + +Once created, the event can be used either to push data to its +subscribers or to broadcast data to any listener. + +The event must be used to establish the subscription for the requesting +client. This is done using the ***afb\_req\_subscribe*** function +that takes the current request object and event and associates them +together. Example: + +```C + rc = afb_req_subscribe(afb_req, event); +``` + +When successful, this function make the connection between the event and +the client that emitted the request. The client becomes a subscriber of +the event until it unsubscribes or disconnects. The +***afb\_req\_subscribe*** function will fail if the client +connection is weak: if the request comes from a HTTP link. To receive +signals, the client must be connected. The AGL framework allows connections +using WebSocket. + +The name of the event is either a well known name or an ad hoc name +forged for the use case. + +Let's see a basic example: client A expects to receive the speed in km/h +every second while client B expects the speed in mph twice a second. In +that case, there are two different events because it is not the same +unit and it is not the same frequency. Having two different events +allows to associate clients to the correct event. But this doesn't tell +any word about the name of these events. The designer of the signaling +agent has two options for naming: + +1. names can be the same (“speed” for example) with sent data + self describing itself or having a specific tag (requiring from + clients awareness about requesting both kinds of speed isn't safe). +2. names of the event include the variations (by example: + “speed-km/h-1Hz” and “speed-mph-2Hz”) and, in that case, sent data + can self describe itself or not. + +In both cases, the signaling agent might have to send the name of the +event and/or an associated tag to its client in the reply of the +subscription. This is part of the step 5 above. + +The framework only uses the event (not its name) for subscription, +unsubscription and pushing. + +When the requested data is already generated and the event used for +pushing it already exists, the signaling agent must not instantiate a +new processing chain and must not create a new event object for pushing +data. The signaling agent must reuse the existing chain and event. + +Unsubscribing is made by the signaling agent on a request of its client. +The ***afb\_req\_unsubscribe*** function tells the framework to +remove the requesting client from the event's list of subscribers. +Example: + +```C + afb_req_unsubscribe(afb_req, event); +``` + +Subscription count does not matter to the framework: subscribing the +same client several times has the same effect that subscribing only one +time. Thus, when unsubscribing is invoked, it becomes immediately +effective. + +#### More on naming events + +Within the AGL framework, a signaling agent is a binding that has an API +prefix. This prefix is meant to be unique and to identify the binding +API. The names of the events that this signaling agent creates are +automatically prefixed by the framework, using the API prefix of the +binding. + +Thus, if a signaling agent of API prefix ***api*** creates an event +of name ***event*** and pushes data to that event, the subscribers +will receive an event of name ***api/event***. + +### Generating and pushing signals and data + +This of the responsibility of the designer of the signaling agent to +establish the processing chain for generating events. In many cases, +this can be achieved using I/O or timer or signal events inserted in the +main loop. For this case, the AGL framework uses **libsystemd** and +provide a way to integrates to the main loop of this library using +afb\_daemon\_get\_event\_loop. Example: + +```C + sdev = afb_daemon_get_event_loop(af_daemon); + rc = sd_event_add_io(sdev, &source, fd, EPOLLIN, myfunction, NULL); +``` + +In some other cases, the events are coming from D-Bus. In that case, the +framework also uses **libsystemd** internally to access D-Bus. It provides +two methods to get the available D-Bus objects, already existing and +bound to the main**libsystemd**event loop. Use either +***afb\_daemon\_get\_system\_bus*** or +***afb\_daemon\_get\_user\_bus*** to get the required instance. Then +use functions of **libsystemd** to handle D-Bus. + +In some rare cases, the generation of the data requires to start a new +thread. + +When a data is generated and ready to be pushed, the signaling agent +should call the function ***afb\_event\_push***. Example: + +```C + rc = afb_event_push(event, JSON); + if (rc == 0) { + stop_generating(event); + afb_event_drop(event); + } +``` + +The function ***afb\_event\_push*** pushes json data to all the +subscribers. It then returns the count of subscribers. When the count is +zero, there is no subscriber listening for the event. The example above +shows that in that case, the signaling agent stops to generate data for +the event and delete the event using afb\_event\_drop. This is one +possible option. Other valuable options are: do nothing and continue to +generate and push the event or just stop to generate and push the data +but keep the event existing. + +### Receiving the signals + +Understanding what a client expects when it receives signals, events or +data shall be the most important topic of the designer of a signaling +agent. The good point here is that because JSON[^1] is the exchange +format, structured data can be sent in a flexible way. + +The good design is to allow as much as possible the client to describe +what is needed with the goal to optimize the processing to the +requirements only. + +### The exceptional case of wide broadcast + +Some data or events have so much importance that they can be widely +broadcasted to alert any listening client. Examples of such an alert +are: + +- system is entering/leaving “power safe” mode +- system is shutting down +- the car starts/stops moving +- ... + +An event can be broadcasted using one of the two following methods: +***afb\_daemon\_broadcast\_event*** or +***afb\_event\_broadcast***. + +Example 1: + +```C + afb_daemon_broadcast_event(afb_daemon, name, json); +``` + +Example 2: + +```C + event = afb_daemon_make_event(afb_daemon, name); + . . . . + afb_event_broadcast(event, json); +``` + +As for other events, the name of events broadcasted using +***afb\_daemon\_broadcast\_event*** are automatically prefixed by +the framework with API prefix of the binding (signaling agent). + +Reference of functions +---------------------- + +### Function afb\_event afb\_daemon\_make\_event + +The function ***afb\_daemon\_make\_event*** that is defined as below: + +```C +/* + * Creates an event of 'name' and returns it. + * 'daemon' MUST be the daemon given in interface when activating the binding. + */ +struct afb_event afb_daemon_make_event(struct afb_daemon daemon, const char *name); +``` + +The daemon is the handler to the application framework binder daemon +received during initialisation steps of the binding. + +Calling the function ***afb\_daemon\_make\_event*** within the initialisation +function ***afbBindingV1Register*** will _fail_ because the binding +name is not known at this time. + +The correct way to create the event at initialisation is to call the function +***afb\_daemon\_make\_event*** within the initialisation +function ***afbBindingV1ServiceInit***. + +### Function afb\_event\_push + +The function ***afb\_event\_push*** is defined as below: + +```C +/* + * Pushes the 'event' with the data 'object' to its observers. + * 'object' can be NULL. + * + * For convenience, the function calls 'json_object_put' for object'. + * Thus, in the case where 'object' should remain available after + * the function returns, the function 'json_object_get' shall be used. + * + * Returns the count of clients that received the event. + */ +int afb_event_push(struct afb_event event, struct json_object *object); +``` + +As the function ***afb\_event\_push*** returns 0 when there is no +more subscriber, a binding can remove such unexpected event using the +function ***afb\_event\_drop***. + +### Function afb\_event\_drop + +The function ***afb\_event\_drop*** is defined as below: + +```C +/* + * Drops the data associated to the event + * After calling this function, the event + * MUST NOT BE USED ANYMORE. + */ +void afb_event_drop(struct afb_event event); +``` + +### Function afb\_req\_subscribe + +The function ***afb\_req\_subscribe*** is defined as below: + +```C +/* + * Establishes for the client link identified by 'req' a subscription + * to the 'event'. + * Returns 0 in case of successful subscription or -1 in case of error. + */ +int afb_req_subscribe(struct afb_req req, struct afb_event event); +``` + +The subscription adds the client of the request to the list of subscribers +to the event. + +### Function afb\_req\_unsubscribe + +The function ***afb\_req\_unsubscribe*** is defined as +below: + +```C +/* + * Revokes the subscription established to the 'event' for the client + * link identified by 'req'. + * Returns 0 in case of successful unsubscription or -1 in case of error. + */ +int afb_req_unsubscribe(struct afb_req req, struct afb_event event); +``` + +The unsubscription removes the client of the request of the list of subscribers +to the event. +When the list of subscribers to the event becomes empty, +the function ***afb\_event\_push*** will return zero. + +### Function afb\_event\_broadcast + +The function ***afb\_event\_broadcast*** is defined as below: + +```C +/* + * Broadcasts widely the 'event' with the data 'object'. + * 'object' can be NULL. + * + * For convenience, the function calls 'json_object_put' for 'object'. + * Thus, in the case where 'object' should remain available after + * the function returns, the function 'json_object_get' shall be used. + * + * Returns the count of clients that received the event. + */ +int afb_event_broadcast(struct afb_event event, struct json_object *object); +``` + +This uses an existing event (created with ***afb\_daemon\_make\_event***) +for broadcasting an event having its name. + + +### Function afb\_daemon\_broadcast\_event + +The function ***afb\_daemon\_broadcast\_event*** is defined as below: + +```C +/* + * Broadcasts widely the event of 'name' with the data 'object'. + * 'object' can be NULL. + * 'daemon' MUST be the daemon given in interface when activating the binding. + * + * For convenience, the function calls 'json_object_put' for 'object'. + * Thus, in the case where 'object' should remain available after + * the function returns, the function 'json_object_get' shall be used. + * + * Returns the count of clients that received the event. + */ +int afb_daemon_broadcast_event(struct afb_daemon daemon, const char *name, struct json_object *object); +``` + +The name is given here explicitly. The name is automatically prefixed +with the name of the binding. For example, a binding of prefix "xxx" +would broadcat the event "xxx/name". + +### Function afbBindingV1ServiceEvent + +Binding can implement function **afbBindingV1ServiceEvent** which will +be called when an event is broadcasted or if service subscribed to an event. +That allow a service to react to an event and do what it is to do if this is +relevant for it (ie: car back camera detects imminent collision and broadcast +it, then appropriate service enable parking brake.). Here is the +**afbBindingV1ServiceEvent** definition: + +```C +/* + * When a binding have an implementation of the function 'afbBindingV1ServiceEvent', + * defined below, the framework calls that function for any broadcasted event or for + * events that the service subscribed to in its name. + * + * It receive the 'event' name and its related data in 'object' (be aware that 'object' + * might be NULL). + */ +extern void afbBindingV1ServiceEvent(const char *event, struct json_object *object); + +The binding *tic-tac-toe* broadcasts events when the board changes. +This is done in the function **changed**: +``` + +Architectural digressions +------------------------- + +Based on their dependencies to hardware, signaling agents can be split +into 2 categories: low-level signaling agents and high-level signaling +agents. + +Low-level signaling agents are bound to the hardware and focused on +interfacing and driving. + +High-level signaling agent are independent of the hardware and focused on +providing service. + +This separation (that may in the corner look artificial) aim to help in +the systems design. The main idea here is that high-level signaling +agents are providing “business logic”, also known as “application +logic”, that is proper to the car industry and that can be reused and +that can evolve as a foundation for the future of the industry. + +The implementation of this decomposition may follow 2 paths: strict +separation or soft composition. + +### Strict separation + +The strict separation implements the modularity composition of signaling +agent through the framework. The high-level signaling agent subscribes +to the low level signaling agent using the standard client API. + +Advantages: + +- Modularity +- Separation of responsibilities +- Possible aggregation of multiple sources +- Soft binding of agent good for maintenance + +Drawbacks: + +- Cost of propagation of data (might serialize) +- Difficulties to abstract low-level signaling agent or to find a + trade-off between abstracting and specializing + +The key is modularity versus cost of propagation. It can be partly +solved when logical group of signaling agent are launched together in +the same binder process. In that particular case, the cost of +propagation of data between agents is reduced[^2] because there is no +serialization. + +This reduction of the propagation cost (and of the resources used) +precludes implementation of strong security between the agents because +they share the same memory. + +### Soft composition + +The soft composition implements the business logic of high-level +signaling agents as libraries that can then be used directly by the low +level signaling agents. + +Advantages: + +- No propagation: same memory, sharing of native structures + +Drawbacks: + +- Cannot be used for aggregation of several sources +- Difficulties to abstract low-level signaling agent or to find a + trade-off between abstracting and specializing +- Source code binding not good for maintenance + +[^1]: There are two aspect in using JSON: the first is the flexible data + structure that mixes common types (booleans, numbers, strings, + arrays, dictionaries, nulls), the second, is the streaming + specification. Streaming is often seen as the bottleneck of using + JSON (see http://bjson.org). When the agent share the same process, + there is no streaming at all. + +[^2]: Within the same process, there is not serialization, the + propagation has the cost of wrapping a json data and calling + callbacks with the benefit of having a powerful callback manager: + the event mechanism of the framework. diff --git a/old-docs/afb-overview.md b/old-docs/afb-overview.md new file mode 100644 index 00000000..eec3f91a --- /dev/null +++ b/old-docs/afb-overview.md @@ -0,0 +1,315 @@ + +Overview of AFB-DAEMON +====================== + +Roles of afb-daemon +------------------- + +The name **afb-daemon** stands for *Application +Framework Binder Daemon*. That is why afb-daemon +is also named ***the binder***. + +**Afb-daemon** is in charge to bind one instance of +an application to the AGL framework and AGL system. + +On the following figure, you can use a typical use +of afb-daemon: + +

Figure: binder afb-daemon, basis

+ +![binder-basis][binder-basis] + +The application and its companion binder run in secured and isolated +environment set for them. Applications are intended to access to AGL +system through the binder. + +The binder afb-daemon serves multiple purposes: + +1. It acts as a gateway for the application to access the system; + +2. It acts as an HTTP server for serving files to HTML5 applications; + +3. It allows HTML5 applications to have native extensions subject +to security enforcement for accessing hardware resources or +for speeding parts of algorithm. + +Use cases of the binder afb-daemon +---------------------------------- + +This section tries to give a better understanding of the binder +usage through several use cases. + +### Remotely running application + +One of the most interesting aspect of using the binder afb-daemon +is the ability to run applications remotely. This feature is +possible because the binder afb-daemon implements native web +protocols. + +So the [figure binder, basis](#binder-fig-basis) would become +when the application is run remotely: + +

Figure: binder afb-daemon and remotely running application

+ + +### Adding native features to HTML5/QML applications + +Applications can provide with their packaged delivery a binding. +That binding will be instantiated for each application instance. +The methods of the binding will be accessible by applications and +will be executed within the security context. + +### Offering services to the system + +It is possible to run the binder afb-daemon as a daemon that provides the +API of its bindings. + +This will be used for: + +1. offering common APIs + +2. provide application's services (services provided as application) + +In that case, the figure showing the whole aspects is + +

Figure: binder afb-daemon for services

+ +![afb-for-services][afb-for-services] + +For this case, the binder afb-daemon takes care to attribute one single session +context to each client instance. It allows bindings to store and retrieve data +associated to each of its client. + +The bindings of the binder afb-daemon +------------------------------------ + +The binder can instantiate bindings. The primary use of bindings +is to add native methods that can be accessed by applications +written with any language through web technologies ala JSON RPC. + +This simple idea is declined to serves multiple purposes: + +1. add native feature to applications + +2. add common API available by any applications + +3. provide customers services + +A specific document explains how to write an afb-daemon binder binding: +[HOWTO WRITE a BINDING for AFB-DAEMON](afb-bindings-writing.html) + + +Launching the binder afb-daemon +------------------------------- + +The launch options for binder **afb-daemon** are: + + --help + + Prints help with available options + + --version + + Display version and copyright + + --verbose + + Increases the verbosity, can be repeated + + --quiet + + Decreases the verbosity, can be repeated + + --port=xxxx + + HTTP listening TCP port [default 1234] + + --workdir=xxxx + + Directory where the daemon must run [default: $PWD if defined + or the current working directory] + + --uploaddir=xxxx + + Directory where uploaded files are temporarily stored [default: workdir] + + --rootdir=xxxx + + Root directory of the application to serve [default: workdir] + + --roothttp=xxxx + + Directory of HTTP served files. If not set, files are not served + but apis are still accessibles. + + --rootbase=xxxx + + Angular Base Root URL [default /opa] + + This is used for any application of kind OPA (one page application). + When set, any missing document whose url has the form /opa/zzz + is translated to /opa/#!zzz + + --rootapi=xxxx + + HTML Root API URL [default /api] + + The bindings are available within that url. + + --alias=xxxx + + Maps a path located anywhere in the file system to the + a subdirectory. The syntax for mapping a PATH to the + subdirectory NAME is: --alias=/NAME:PATH. + + Example: --alias=/icons:/usr/share/icons maps the + content of /usr/share/icons within the subpath /icons. + + This option can be repeated. + + --no-httpd + + Tells to not start the HTTP server. + + --apitimeout=xxxx + + binding API timeout in seconds [default 20] + + Defines how many seconds maximum a method is allowed to run. + 0 means no limit. + + --cntxtimeout=xxxx + + Client Session Timeout in seconds [default 3600] + + --cache-eol=xxxx + + Client cache end of live [default 100000 that is 27,7 hours] + + --session-max=xxxx + + Maximum count of simultaneous sessions [default 10] + + --ldpaths=xxxx + + Load bindings from given paths separated by colons + as for dir1:dir2:binding1.so:... [default = $libdir/afb] + + You can mix path to directories and to bindings. + The sub-directories of the given directories are searched + recursively. + + The bindings are the files terminated by '.so' (the extension + so denotes shared object) that contain the public entry symbol. + + --binding=xxxx + + Load the binding of given path. + + --token=xxxx + + Initial Secret token to authenticate. + + If not set, no client can authenticate. + + If set to the empty string, then any initial token is accepted. + + --random-token + + Generate a random starting token. See option --exec. + + --mode=xxxx + + Set the mode: either local, remote or global. + + The mode indicate if the application is run locally on the host + or remotely through network. + + --readyfd=xxxx + + Set the #fd to signal when ready + + If set, the binder afb-daemon will write "READY=1\n" on the file + descriptor whose number if given (/proc/self/fd/xxx). + + --dbus-client=xxxx + + Transparent binding to a binder afb-daemon service through dbus. + + It creates an API of name xxxx that is implemented remotely + and queried via DBUS. + + --dbus-server=xxxx + + Provides a binder afb-daemon service through dbus. + + The name xxxx must be the name of an API defined by a binding. + This API is exported through DBUS. + + --ws-client=xxxx + + Transparent binding to a binder afb-daemon service through a WebSocket. + + The value of xxxx is either a unix naming socket, of the form "unix:path/api", + or an internet socket, of the form "host:port/api". + + --ws-server=xxxx + + Provides a binder afb-daemon service through WebSocket. + + The value of xxxx is either a unix naming socket, of the form "unix:path/api", + or an internet socket, of the form "host:port/api". + + --foreground + + Get all in foreground mode (default) + + --daemon + + Get all in background mode + + --no-httpd + + Forbids HTTP serve + + --exec + + Must be the last option for afb-daemon. The remaining + arguments define a command that afb-daemon will launch. + The sequences @p, @t and @@ of the arguments are replaced + with the port, the token and @. + + --tracereq=xxxx + + Trace the processing of requests in the log file. + + Valid values are 'no' (default), 'common', 'extra' or 'all'. + + + + + +Future development of afb-daemon +-------------------------------- + +- The binder afb-daemon would launch the applications directly. + +- The current setting of mode (local/remote/global) might be reworked to a +mechanism for querying configuration variables. + +- Implements "one-shot" initial token. It means that after its first +authenticated use, the initial token is removed and no client can connect +anymore. + +- Creates some intrinsic APIs. + +- Make the service connection using WebSocket not DBUS. + +- Management of targeted events. + +- Securing LOA. + +- Integration of the protocol JSON-RPC for the websockets. + +[binder-basis]: pictures/AFB_overview.svg +[afb-for-services]: pictures/AFB_for_services.svg diff --git a/old-docs/afb-tests-overview.md b/old-docs/afb-tests-overview.md new file mode 100644 index 00000000..d6f619fe --- /dev/null +++ b/old-docs/afb-tests-overview.md @@ -0,0 +1,89 @@ + +Overview of tests shipped with AFB-Daemon +========================================= + +List of tests +------------- + +Here are the tests shipped in the source tree: + +* **afb-client-demo** (command-line WebSockets) + +* **token-websock.qml** (Qt/QML WebSockets) + +* ***.html** (HTML5/JS HTTP-REST & WebSockets) + + +Detail of tests +--------------- + +### afb-client-demo (command-line WebSockets) + +This clients interactively calls bindings APIs from the command line, using the binder +[WebSockets](https://en.wikipedia.org/wiki/WebSocket) facility. + +If _afb-daemon_ has been launched with the following parameters: + + + $ afb-daemon --port=1234 --token=123456 [...] + + +Then run the client with : + + afb-client-demo ws://localhost:1234/api?token=123456 [ []] + +For instance, to initialize the Audio binding from the command line : + + afb-client-demo ws://localhost:1234/api?token=123456 + +The command doesn't return. You should type requests of type []. +So, try: + + auth connect + hello pingjson true + +
+ + + +### token-websock.qml (Qt/QML WebSockets) + +If _afb-daemon_ has been launched with the following parameters: + + $ afb-daemon --port=1234 --token=123456 [...] + +and Qt5 is installed. + +For installing Qt5 on **Ubuntu 16.04**: + + $ apt-get install qmlscene qml-module-qtwebsockets qml-module-qtquick-controls + +For installing Qt5 on **Fedora >= 22** : + + $ dnf install qt5-qtdeclarative-devel qt5-qtwebsockets-devel qt5-qtquickcontrols + + +Then run the client with : + + qmlscene test/token-websock.qml + +and interactively press the buttons, "Connect", "Refresh", "Logout". + +
+ + +### *.html (HTML5/JS HTTP-REST & WebSockets) + +If _afb-daemon_ has been launched with the following parameters: + + $ afb-daemon --port=1234 --rootdir=$PWD/test [...] + +_("$PWD/test_" being the "test" subdirectory of the source tree)_ + + +Then open your preferred Web browser, connect to the following URL: + + http://localhost:1234 + +and interactively run the various tests. + diff --git a/old-docs/index.md b/old-docs/index.md new file mode 120000 index 00000000..81c25433 --- /dev/null +++ b/old-docs/index.md @@ -0,0 +1 @@ +afb-overview.md \ No newline at end of file diff --git a/old-docs/pictures/AFB_for_services.svg b/old-docs/pictures/AFB_for_services.svg new file mode 100644 index 00000000..6e536c50 --- /dev/null +++ b/old-docs/pictures/AFB_for_services.svg @@ -0,0 +1,238 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Isolated security context + + + + + + + + APPLICATION + + + + + + + + binderAFB-DAEMON + + + + + + + + BINDINGS + + + + + + + + + + + + + + + + + + + + + D-Bus & CYNARA + + + + + + + + Isolated security context A + + + + + + + + binderAFB-DAEMON + + + + + + + + serviceBINDINGS A + + + + + + + + + + + + + + + Isolated security context B + + + + + + + + binderAFB-DAEMON + + + + + + + + serviceBINDINGS B + + + + + + + + + + + + + + + Isolated security context C + + + + + + + + binderAFB-DAEMON + + + + + + + + serviceBINDINGS C + + + + + + + + + + + + + + \ No newline at end of file diff --git a/old-docs/pictures/AFB_overview.svg b/old-docs/pictures/AFB_overview.svg new file mode 100644 index 00000000..240dae89 --- /dev/null +++ b/old-docs/pictures/AFB_overview.svg @@ -0,0 +1,140 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Isolated security context + + + + + + + + APPLICATION + + + + + + + + binderAFB-DAEMON + + + + + + + + BINDINGS + + + + + + AGL SYSTEM + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/old-docs/pictures/signaling-basis.svg b/old-docs/pictures/signaling-basis.svg new file mode 100644 index 00000000..b13fcf17 --- /dev/null +++ b/old-docs/pictures/signaling-basis.svg @@ -0,0 +1,145 @@ + + + + + + + request-data + + + + + + client 1 + + + + + + + + client 2 + + + + + + + + : framework + + + + + + + + signaling agent + + + + + + + + + + + request-data + + + + + + afb_daemon_make_event + + + + + + + + + afb_req_subscribe + + + + + reply of request-data + + + + + + afb_req_subscribe + + + + + reply of request-data + + + + + + device + + + + + + + + + setup + + + + + << wake up >> + + + + + afb_event_push + + + + + << event >> + + + + + << event >> + + + + + reply of afb_event_push + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/old-docs/pictures/tic-tac-toe.svg b/old-docs/pictures/tic-tac-toe.svg new file mode 100644 index 00000000..7a5fb84e --- /dev/null +++ b/old-docs/pictures/tic-tac-toe.svg @@ -0,0 +1,289 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Client A + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Client B + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Tic-Tac-Toe + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + move + + + + + + wait + + + + + + success of move + + + + + + success of wait + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/old-docs/pictures/triskel_iot_bzh.svg b/old-docs/pictures/triskel_iot_bzh.svg new file mode 100644 index 00000000..096f4244 --- /dev/null +++ b/old-docs/pictures/triskel_iot_bzh.svg @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + -- cgit