From c23ad28ea06e5ff335b4fe9171de5e3017c350d3 Mon Sep 17 00:00:00 2001 From: Shankho Boron Ghosh Date: Mon, 30 Nov 2020 02:46:36 +0530 Subject: Added Application Framework Binder in Developer Guides Revised and added Application Framework Binder as a part of Developer Guides. v2 : Corrected typo : MACRO -> MACROS Bug-AGL: [SPEC-3633] Signed-off-by: Shankho Boron Ghosh Change-Id: I7a6d6206f69ed44a18011c81f475ce5eac306a33 Reviewed-on: https://gerrit.automotivelinux.org/gerrit/c/AGL/documentation/+/25663 Reviewed-by: Jan-Simon Moeller Tested-by: Jan-Simon Moeller --- .../Annexes/1_Migration_to_bindings_v3.md | 199 ++++++ .../Annexes/2_WebSocket_protocol_x-afb-ws-json1.md | 308 +++++++++ .../3_Installing_the_binder_on_a_desktop.md | 44 ++ .../Annexes/4_Options_of_afb-daemon.md | 355 ++++++++++ .../Annexes/5_Debugging_binder_and_bindings.md | 38 ++ .../Annexes/6_LEGACY_Migration_from_v1_to_v2.md | 656 ++++++++++++++++++ .../Annexes/7_LEGACY_Binding_v2_references.md | 757 +++++++++++++++++++++ 7 files changed, 2357 insertions(+) create mode 100644 docs/3_Developer_Guides/2_Application_Framework_Binder/Annexes/1_Migration_to_bindings_v3.md create mode 100644 docs/3_Developer_Guides/2_Application_Framework_Binder/Annexes/2_WebSocket_protocol_x-afb-ws-json1.md create mode 100644 docs/3_Developer_Guides/2_Application_Framework_Binder/Annexes/3_Installing_the_binder_on_a_desktop.md create mode 100644 docs/3_Developer_Guides/2_Application_Framework_Binder/Annexes/4_Options_of_afb-daemon.md create mode 100644 docs/3_Developer_Guides/2_Application_Framework_Binder/Annexes/5_Debugging_binder_and_bindings.md create mode 100644 docs/3_Developer_Guides/2_Application_Framework_Binder/Annexes/6_LEGACY_Migration_from_v1_to_v2.md create mode 100644 docs/3_Developer_Guides/2_Application_Framework_Binder/Annexes/7_LEGACY_Binding_v2_references.md (limited to 'docs/3_Developer_Guides/2_Application_Framework_Binder/Annexes') diff --git a/docs/3_Developer_Guides/2_Application_Framework_Binder/Annexes/1_Migration_to_bindings_v3.md b/docs/3_Developer_Guides/2_Application_Framework_Binder/Annexes/1_Migration_to_bindings_v3.md new file mode 100644 index 0000000..bba5354 --- /dev/null +++ b/docs/3_Developer_Guides/2_Application_Framework_Binder/Annexes/1_Migration_to_bindings_v3.md @@ -0,0 +1,199 @@ +--- +title: Migration to bindings v3 +--- + +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 obscure features and cleanup + +The ***binder*** can run ***bindings*** v1, v2 and/or v3 in any combination. +Thus moving from v1 or v2 to v3 is not enforced at this time. But ... + +In the face to face meeting in Karlsruhe it was decided to remove support +of bindings v1 and to deprecate the use of bindings v2. + +So at the end, **IT IS HIGHLY NEEDED TO SWITCH TO VERSION 3** + +This guide covers the migration of bindings from version 2 to version 3. + +The migration from version 1 is not treated here because bindings version 1 +are very old and probably do not exist anymore. If needed you can refer +to the old [guide to migrate bindings from v1 to v2](./6_LEGACY_Migration_from_v1_to_v2.md). + + +Differences between version 2 and version 3 +------------------------------------------- + +** *In v3 all is api.* ** + +The version 3 introduces the concept of "API" that gather what was called before +the daemon and the service. This is the new concept that predates the 2 others. + +The concept of API is intended to allow the definition of multiple APIs +by a same "binding" (a dynamically loaded library). + +Because there is potentially several "API", the functions that were without +context in bindings version 2 need now to tell what API is consumer. + +To be compatible with version 2, bindings v3 still have a default hidden +context: the default API named **afbBindingV3root**. + +To summarize, the functions of class **daemon** and **service** use the default +hidden API. + +It is encouraged to avoid use of functions of class **daemon** and **service**. +You should replace these implicit calls to explicit **api** calls that +reference **afbBindingV3root**. + +Same thing for the logging macros: **AFB_ERROR**, **AFB_WARNING**, +**AFB_NOTICE**, **AFB_INFO**, **AFB_DEBUG** that becomes respectively +**AFB_API_ERROR**, **AFB_API_WARNING**, **AFB_API_NOTICE**, **AFB_API_INFO**, +**AFB_API_DEBUG**. + +Example of 2 equivalent writes: + +```C + AFB_NOTICE("send stress event"); + afb_daemon_broadcast_event(stressed_event, NULL); +``` + +or + +```C + AFB_API_NOTICE(afbBindingV3root, "send stress event"); + afb_api_broadcast_event(afbBindingV3root, stressed_event, NULL); +``` + +**The reply mechanism predates success and fail. Subcall has more power.** + +Task list for the migration +--------------------------- + +This task list is: + +1. Use the automatic migration procedure described below +2. Adapt the functions **preinit**, **init** and **onevent** +3. Consider use of the new reply +4. Consider use of the new (sub)call +5. Consider use of event handlers + +The remaining chapters explain these task with more details. + +Automatic migration! +-------------------- + +A tiny **sed** script is intended to perform a first pass on the code that +you want to upgrade. It can be done using **curl** and applied using **sed** +as below. + +```bash +BASE=https://git.automotivelinux.org/src/app-framework-binder/plain +SED=migration-to-binding-v3.sed +curl -o $SED $BASE/docs/$SED +sed -i -f $SED file1 file2 file3... +``` + +You can also follow +[this link](https://git.automotivelinux.org/src/app-framework-binder/plain/docs/migration-to-binding-v3.sed) +and save the file. + +This automatic action does most of the boring job but not all the job. +The remaining of this guide explains the missing part. + +Adapt the functions preinit, init and onevent +---------------------------------------------- + +The signature of the functions **preinit**, **init** and **onevent** changed +to include the target api. + +The functions of the v2: + +```C +int (*preinit)(); +int (*init)(); +void (*onevent)(const char *event, struct json_object *object); +``` + +Gain a new first argument of type **afb_api_t** as below: + +```C +int (*preinit)(afb_api_t api); +int (*init)(afb_api_t api); +void (*onevent)(afb_api_t api, const char *event, struct json_object *object); +``` + +For the migration, it is enough to just add the new argument without +using it. + +Consider use of the new reply +----------------------------- + +The v3 allows error reply with JSON object. To achieve it, an unified +reply function's family is introduced: + +```C +void afb_req_reply(afb_req_t req, json_object *obj, const char *error, const char *info); +void afb_req_reply_v(afb_req_t req, json_object *obj, const char *error, const char *info, va_list args); +void afb_req_reply_f(afb_req_t req, json_object *obj, const char *error, const char *info, ...); +``` + +The functions **success** and **fail** are still supported. +These functions are now implemented as the following macros: + + +```C +#define afb_req_success(r,o,i) afb_req_reply(r,o,NULL,i) +#define afb_req_success_f(r,o,...) afb_req_reply_f(r,o,NULL,__VA_ARGS__) +#define afb_req_success_v(r,o,f,v) afb_req_reply_v(r,o,NULL,f,v) +#define afb_req_fail(r,e,i) afb_req_reply(r,NULL,e,i) +#define afb_req_fail_f(r,e,...) afb_req_reply_f(r,NULL,e,__VA_ARGS__) +#define afb_req_fail_v(r,e,f,v) afb_req_reply_v(r,NULL,e,f,v) +``` + +This is a decision of the developer to switch to the new family +**afb_req_reply** or to keep the good old functions **afb_req_fail** +adn **afb_req_success**. + +Consider use of the new (sub)call +--------------------------------- + +The new call and subcall (the functions **afb_api_call**, **afb_api_call_sync**, +**afb_req_subcall** and **afb_req_subcall_sync**) functions are redesigned +to better fit the new reply behaviour. In most case the developer will benefit +of the new behavior that directly gives result and error without enforcing +to parse the JSON object result. + +The subcall functions are also fully redesigned to allow precise handling +of the context and event subscriptions. The new design allows you to specify: + + - whether the subcall is made in the session of the caller or in the session + of the service + - whether the credentials to use are those of the caller or those of the + service + - whether the caller or the service or both or none will receive the + eventually events during the subcall. + +See [calls](../3_Binder_References.md#afb_api_call) and +[subcalls](../3_Binder_References.md#subcall-functions). + +The table below list the changes to apply: + +| Name in Version 2 | New name of Version 3 +|:----------------------:|:----------------------------------------------------: +| afb_req_subcall | afb_req_subcall_legacy +| afb_req_subcall_sync | afb_req_subcall_sync_legacy +| afb_service_call | afb_service_call_legacy +| afb_service_call_sync | afb_service_call_sync_legacy +| afb_req_subcall_req | afb_req_subcall_req (same but obsolete) + + +Consider use of event handlers +------------------------------ + +Binding V3 brings new ways of handling event in services. You can register +functions that will handle specific events and that accept closure arguments. + +See [**afb_api_event_handler_add** and **afb_api_event_handler_del**](../3_Binder_References.md#event-functions) \ No newline at end of file diff --git a/docs/3_Developer_Guides/2_Application_Framework_Binder/Annexes/2_WebSocket_protocol_x-afb-ws-json1.md b/docs/3_Developer_Guides/2_Application_Framework_Binder/Annexes/2_WebSocket_protocol_x-afb-ws-json1.md new file mode 100644 index 0000000..7ebf22a --- /dev/null +++ b/docs/3_Developer_Guides/2_Application_Framework_Binder/Annexes/2_WebSocket_protocol_x-afb-ws-json1.md @@ -0,0 +1,308 @@ +--- +title: WebSocket protocol x-afb-ws-json1 +--- + +The WebSocket protocol *x-afb-ws-json1* is used to communicate between +an application and a binder. It allows access to all registered apis +of the binder. + +This protocol is inspired from the protocol **OCPP - SRPC** as described for +example here: +[OCPP transport specification - SRPC over WebSocket](http://www.gir.fr/ocppjs/ocpp_srpc_spec.shtml). + +The registration to the IANA is still to be done, see: +[WebSocket Protocol Registries](https://www.iana.org/assignments/websocket/websocket.xml) + +This document gives a short description of the protocol *x-afb-ws-json1*. +A more formal description has to be done. + +## Architecture + +The protocol is intended to be symmetric. It allows: + +- to CALL a remote procedure that returns a result +- to push and receive EVENT + +## Messages + +Valid messages are made of *text* frames that are all valid JSON. + +Valid messages are: + +Calls: + +```txt +[ 2, ID, PROCN, ARGS ] +[ 2, ID, PROCN, ARGS, TOKEN ] +``` + +Replies (3: OK, 4: ERROR): + +```txt +[ 3, ID, RESP ] +[ 4, ID, RESP ] +``` + +Events: + +```txt +[ 5, EVTN, OBJ ] +``` + +Where: + +| Field | Type | Description +|-------|--------|------------------ +| ID | string | A string that identifies the call. A reply to that call use the ID of the CALL. +| PROCN | string | The procedure name to call of the form "api/verb" +| ARGS | any | Any argument to pass to the call (see afb_req_json that returns it) +| RESP | any | The response to the call +| TOKEN | string | The authorisation token +| EVTN | string | Name of the event in the form "api/event" +| OBJ | any | The companion object of the event + +Below, an example of exchange: + +```txt +C->S: [2,"156","hello/ping",null] +S->C: [3,"156",{"response":"Some String","jtype":"afb-reply","request":{"status":"success","info":"Ping Binder Daemon tag=pingSample count=1 query=\"null\"","uuid":"ec30120c-6997-4529-9d63-c0de0cce56c0"}}] +``` + +## History + +### 14 November 2019 + +Removal of token returning. The replies + +```txt +[ 3, ID, RESP, TOKEN ] +[ 4, ID, RESP, TOKEN ] +``` + +are removed from the specification. + +## Future + +Here are the planned extensions: + +- add binary messages with cbor data +- add calls with unstructured replies + +This could be implemented by extending the current protocol or by +allowing the binder to accept either protocol including the new ones. + +## Javascript implementation + +The file **AFB.js** is a javascript implementation of the protocol. + +Here is that code: + +```javascript +/* + * Copyright (C) 2017-2019 "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. + */ +AFB = function(base, initialtoken){ + +if (typeof base != "object") + base = { base: base, token: initialtoken }; + +var initial = { + base: base.base || "api", + token: base.token || initialtoken || "HELLO", + host: base.host || window.location.host, + url: base.url || undefined +}; + +var urlws = initial.url || "ws://"+initial.host+"/"+initial.base; + +/*********************************************/ +/**** ****/ +/**** AFB_context ****/ +/**** ****/ +/*********************************************/ +var AFB_context; +{ + var UUID = undefined; + var TOKEN = initial.token; + + var context = function(token, uuid) { + this.token = token; + this.uuid = uuid; + } + + context.prototype = { + get token() {return TOKEN;}, + set token(tok) {if(tok) TOKEN=tok;}, + get uuid() {return UUID;}, + set uuid(id) {if(id) UUID=id;} + }; + + AFB_context = new context(); +} +/*********************************************/ +/**** ****/ +/**** AFB_websocket ****/ +/**** ****/ +/*********************************************/ +var AFB_websocket; +{ + var CALL = 2; + var RETOK = 3; + var RETERR = 4; + var EVENT = 5; + + var PROTO1 = "x-afb-ws-json1"; + + AFB_websocket = function(on_open, on_abort) { + var u = urlws; + if (AFB_context.token) { + u = u + '?x-afb-token=' + AFB_context.token; + if (AFB_context.uuid) + u = u + '&x-afb-uuid=' + AFB_context.uuid; + } + this.ws = new WebSocket(u, [ PROTO1 ]); + this.url = u; + this.pendings = {}; + this.awaitens = {}; + this.counter = 0; + this.ws.onopen = onopen.bind(this); + this.ws.onerror = onerror.bind(this); + this.ws.onclose = onclose.bind(this); + this.ws.onmessage = onmessage.bind(this); + this.onopen = on_open; + this.onabort = on_abort; + } + + function onerror(event) { + var f = this.onabort; + if (f) { + delete this.onopen; + delete this.onabort; + f && f(this); + } + this.onerror && this.onerror(this); + } + + function onopen(event) { + var f = this.onopen; + delete this.onopen; + delete this.onabort; + f && f(this); + } + + function onclose(event) { + for (var id in this.pendings) { + try { this.pendings[id][1](); } catch (x) {/*TODO?*/} + } + this.pendings = {}; + this.onclose && this.onclose(); + } + + function fire(awaitens, name, data) { + var a = awaitens[name]; + if (a) + a.forEach(function(handler){handler(data);}); + var i = name.indexOf("/"); + if (i >= 0) { + a = awaitens[name.substring(0,i)]; + if (a) + a.forEach(function(handler){handler(data);}); + } + a = awaitens["*"]; + if (a) + a.forEach(function(handler){handler(data);}); + } + + function reply(pendings, id, ans, offset) { + if (id in pendings) { + var p = pendings[id]; + delete pendings[id]; + try { p[offset](ans); } catch (x) {/*TODO?*/} + } + } + + function onmessage(event) { + var obj = JSON.parse(event.data); + var code = obj[0]; + var id = obj[1]; + var ans = obj[2]; + AFB_context.token = obj[3]; + switch (code) { + case RETOK: + reply(this.pendings, id, ans, 0); + break; + case RETERR: + reply(this.pendings, id, ans, 1); + break; + case EVENT: + default: + fire(this.awaitens, id, ans); + break; + } + } + + function close() { + this.ws.close(); + this.ws.onopen = + this.ws.onerror = + this.ws.onclose = + this.ws.onmessage = + this.onopen = + this.onabort = function(){}; + } + + function call(method, request, callid) { + return new Promise((function(resolve, reject){ + var id, arr; + if (callid) { + id = String(callid); + if (id in this.pendings) + throw new Error("pending callid("+id+") exists"); + } else { + do { + id = String(this.counter = 4095 & (this.counter + 1)); + } while (id in this.pendings); + } + this.pendings[id] = [ resolve, reject ]; + arr = [CALL, id, method, request ]; + if (AFB_context.token) arr.push(AFB_context.token); + this.ws.send(JSON.stringify(arr)); + }).bind(this)); + } + + function onevent(name, handler) { + var id = name; + var list = this.awaitens[id] || (this.awaitens[id] = []); + list.push(handler); + } + + AFB_websocket.prototype = { + close: close, + call: call, + onevent: onevent + }; +} +/*********************************************/ +/**** ****/ +/**** ****/ +/**** ****/ +/*********************************************/ +return { + context: AFB_context, + ws: AFB_websocket +}; +}; +``` diff --git a/docs/3_Developer_Guides/2_Application_Framework_Binder/Annexes/3_Installing_the_binder_on_a_desktop.md b/docs/3_Developer_Guides/2_Application_Framework_Binder/Annexes/3_Installing_the_binder_on_a_desktop.md new file mode 100644 index 0000000..8874bde --- /dev/null +++ b/docs/3_Developer_Guides/2_Application_Framework_Binder/Annexes/3_Installing_the_binder_on_a_desktop.md @@ -0,0 +1,44 @@ +--- +title: Installing the binder on a desktop +--- + +Packages of the ***binder*** (afb-daemon)exist +for common desktop linux distributions. + +- Fedora +- Ubuntu +- Debian +- Suse + +Installing the development package of the ***binder*** +allows to write ***bindings*** that runs on the desktop +computer of the developer. + +It is very convenient to quickly write and debug a binding. + +## Retrieving compiling option with pkg-config + +The ***binder*** afb-daemon provides a configuration +file for **pkg-config**. +Typing the command + +```bash +pkg-config --cflags afb-daemon +``` + +Print flags use for compilation: + +```bash +$ pkg-config --cflags afb-daemon +-I/opt/local/include -I/usr/include/json-c +``` + +For linking, you should use + +```bash +$ 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/3_Developer_Guides/2_Application_Framework_Binder/Annexes/4_Options_of_afb-daemon.md b/docs/3_Developer_Guides/2_Application_Framework_Binder/Annexes/4_Options_of_afb-daemon.md new file mode 100644 index 0000000..0b58eec --- /dev/null +++ b/docs/3_Developer_Guides/2_Application_Framework_Binder/Annexes/4_Options_of_afb-daemon.md @@ -0,0 +1,355 @@ +--- +title: Options of afb-daemon +--- + +The launch options for binder **afb-daemon** are: + +``` + -v, --verbose Verbose Mode, repeat to increase verbosity + -c, --color Colorize the ouput + -q, --quiet Quiet Mode, repeat to decrease verbosity + -l, --log=xxxx Tune log level + --foreground Get all in foreground mode + --background Get all in background mode + -D, --daemon Get all in background mode + -n, --name=xxxx Set the visible name + -p, --port=xxxx HTTP listening TCP port [default 1234] + --roothttp=xxxx HTTP Root Directory [default no root http (files not served but apis still available)] + --rootbase=xxxx Angular Base Root URL [default /opa] + --rootapi=xxxx HTML Root API URL [default /api] + --alias=xxxx Multiple url map outside of rootdir [eg: --alias=/icons:/usr/share/icons] + --apitimeout=xxxx Binding API timeout in seconds [default 20] + --cntxtimeout=xxxx Client Session Context Timeout [default 32000000] + --cache-eol=xxxx Client cache end of live [default 100000] + -w, --workdir=xxxx Set the working directory [default: $PWD or current working directory] + -u, --uploaddir=xxxx Directory for uploading files [default: workdir] relative to workdir + --rootdir=xxxx Root Directory of the application [default: workdir] relative to workdir + --ldpaths=xxxx Load bindings from dir1:dir2:... [default: path of afb-dbus-binding.so] + -b, --binding=xxxx Load the binding of path + --weak-ldpaths=xxxx Same as --ldpaths but ignore errors + --no-ldpaths Discard default ldpaths loading + -t, --token=xxxx Initial Secret [default=random, use --token= to allow any token] + -r, --random-token Enforce a random token + -V, --version Display version and copyright + -h, --help Display this help + --ws-client=xxxx Bind to an afb service through websocket + --ws-server=xxxx Provide an afb service through websockets + -A, --auto-api=xxxx Automatic load of api of the given directory + --session-max=xxxx Max count of session simultaneously [default 200] + --tracereq=xxxx Log the requests: no, common, extra, all + --traceevt=xxxx Log the events: no, common, extra, all + --traceses=xxxx Log the sessions: no, all + --traceapi=xxxx Log the apis: no, common, api, event, all + --traceglob=xxxx Log the globals: none, all + --traceditf=xxxx Log the daemons: no, common, all + --tracesvc=xxxx Log the services: no, all + --call=xxxx call at start, format of val: API/VERB:json-args + --no-httpd Forbid HTTP service + -e, --exec Execute the remaining arguments + -M, --monitoring Enable HTTP monitoring at /monitoring/ + -C, --config=xxxx Load options from the given config file + -Z, --dump-config Dump the config to stdout and exit + -s, --set=xxxx Set parameters ([API]/[KEY]:JSON or {"API":{"KEY":JSON}} + -o, --output=xxxx Redirect stdout and stderr to output file (when --daemon) + --trap-faults=xxxx Trap faults: on, off, yes, no, true, false, 1, 0 (default: true) +``` + +## help + +Prints help with available options + +## version + +Display version and copyright + +## verbose + +Increases the verbosity, can be repeated + +## color + +Add basic colorization to the ouput. + +## quiet + +Decreases the verbosity, can be repeated + +## log=xxxx + +Tune the log level mask. The levels are: + + - error + - warning + - notice + - info + - debug + +The level can be set using + or -. + +| Examples | descritpion +|-----------------|------------------- +| error,warning | selects only the levels error and warning +| +debug | adds level debug to the current verbosity +| -warning | remove the level warning from the current verbosity +| +warning-debug,info | Adds error and remove errors and warnings + +## 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 accessible. + +## 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 32000000 that is 1 year] + +## cache-eol=xxxx + +Client cache end of live [default 100000 that is 27,7 hours] + +## session-max=xxxx + +Maximum count of simultaneous sessions [default 200] + +## 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. + +## weak-ldpaths=xxxx + +Same as --ldpaths but instead of stopping on error, ignore errors and continue. + +## 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. + +## 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'. + +## traceapi=xxxx + +Trace the accesses to functions of class api. + +Valid values are 'no' (default), 'common', 'api', 'event' 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"}}' + +## monitoring + +Enable HTTP monitoring at /monitoring/ + +## name=xxxx + +Set the visible name + +## auto-api=xxxx + +Automatic activation of api of the given directory when the api is missing. + +## config=xxxx + +Load options from the given config file + +This can be used instead of arguments on the command line. + +Example: + + afb-daemon \ + --no-ldpaths \ + --binding /home/15646/bindings/binding45.so \ + --binding /home/15646/bindings/binding3.so \ + --tracereq common \ + --port 5555 \ + --token SPYER \ + --set api45/key:54027a5e3c6cb2ca5ddb97679ce32f185b067b0a557d16a8333758910bc25a72 \ + --exec /home/15646/bin/test654 @p @t + +is equivalent to: + + afb-daemon --config /home/15646/config1 + +when the file **/home/15646/config1** is: + + { + "no-ldpaths": true, + "binding": [ + "\/home\/15646\/bindings\/binding45.so", + "\/home\/15646\/bindings\/binding3.so" + ], + "tracereq": "common", + "port": 5555, + "token": "SPYER", + "set" : { + "api45": { + "key": "54027a5e3c6cb2ca5ddb97679ce32f185b067b0a557d16a8333758910bc25a72" + } + }, + "exec": [ + "\/home\/15646\/bin\/test654", + "@p", + "@t" + ] + } + +The options are the keys of the config object. + +See option --dump-config + +## dump-config + +Output a JSON representation of the configuration resulting from +environment and options. + +## output=xxxx + +Redirect stdout and stderr to output file + +## set=xxxx + +Set values that can be retrieved by bindings. + +The set value can have different formats. + +The most generic format is **{"API1":{"KEY1":VALUE,"KEY2":VALUE2,...},"API2":...}** + +This example set 2 keys for the api *chook*: + + afb-daemon -Z --set '{"chook":{"account":"urn:chook:b2ca5ddb97679","delay":500}}' + { + "set": { + "chook": { + "account": "urn:chook:b2ca5ddb97679", + "delay": 500 + } + } + } + +An other format is: **[API]/[KEY]:VALUE**. +When API is omitted, it take the value " * ". +When KEY is ommitted, it take the value of " * ". + +The settings for the API \* are globals and apply to all bindings. + +The settings for the KEY \* are mixing the value for the API. + +The following examples are all setting the same values: + + afb-daemon --set '{"chook":{"account":"urn:chook:b2ca5ddb97679","delay":500}}' + afb-daemon --set 'chook/*:{"account":"urn:chook:b2ca5ddb97679","delay":500}' + afb-daemon --set 'chook/:{"account":"urn:chook:b2ca5ddb97679","delay":500}' + afb-daemon --set 'chook/account:"urn:chook:b2ca5ddb97679"' --set chook/delay:500 \ No newline at end of file diff --git a/docs/3_Developer_Guides/2_Application_Framework_Binder/Annexes/5_Debugging_binder_and_bindings.md b/docs/3_Developer_Guides/2_Application_Framework_Binder/Annexes/5_Debugging_binder_and_bindings.md new file mode 100644 index 0000000..dbb9bdd --- /dev/null +++ b/docs/3_Developer_Guides/2_Application_Framework_Binder/Annexes/5_Debugging_binder_and_bindings.md @@ -0,0 +1,38 @@ +--- +title: Debugging binder and bindings +--- + +When compiled with the symbol AGL_DEVEL defined, the ***binder*** +understands the 2 configuration variables: + + - AFB_DEBUG_BREAK: to emit interrupts + - AFB_DEBUG_WAIT: to wait interrupts + +To use these variables, assign it the list of break or wait points +to reach. + +Example: + +```bash +$ AFB_DEBUG_BREAK=main-entry AFB_DEBUG_WAIT=start-load,start-exec afb-daemon .... +``` + +This tells to ***afb-daemon*** to break at the point **main-entry** and to +wait at the points **start-load** and **start-exec**. + +The items of the list can be separated using comma, space, tab or new-line. + +The break/wait points are, in the order of their occurrence: + +- main-entry: before decode arguments +- main-args: before daemon setup +- main-start: before starting jobs +- start-entry: before initialisation of sessions and hooks +- start-load: before load and pre-init of bindings +- start-start: before init of bindings +- start-http: before start of http server +- start-call: before execution of requests of the command line (option --call) +- start-exec: before execution of child preocees + +Note also that a call to 'personality' is inserted just after +the point start-start. \ No newline at end of file diff --git a/docs/3_Developer_Guides/2_Application_Framework_Binder/Annexes/6_LEGACY_Migration_from_v1_to_v2.md b/docs/3_Developer_Guides/2_Application_Framework_Binder/Annexes/6_LEGACY_Migration_from_v1_to_v2.md new file mode 100644 index 0000000..6004aec --- /dev/null +++ b/docs/3_Developer_Guides/2_Application_Framework_Binder/Annexes/6_LEGACY_Migration_from_v1_to_v2.md @@ -0,0 +1,656 @@ +--- +title: LEGACY Migration from v1 to v2 +--- + +> LEGACY!!! IT IS NOT EXPECTED THAT YOU STILL NEED THIS GUIDE. +> +> THIS GUIDE WILL BE REMOVED IN A NEAR FUTURE + + +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 obscure 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 RECOMMENDED TO SWITCH TO ONLY VERSION 2: + +- any new development 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 emphasis 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. Optionally, 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; /* authorization 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 authorization */ + uint32_t session; /* authorization 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 +authorization. + +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, explore, 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** according 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 initialization 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 following 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 recommended 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 un-prefixed 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 information 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/un-storing 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 developer 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***. + +Optionally, 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/3_Developer_Guides/2_Application_Framework_Binder/Annexes/7_LEGACY_Binding_v2_references.md b/docs/3_Developer_Guides/2_Application_Framework_Binder/Annexes/7_LEGACY_Binding_v2_references.md new file mode 100644 index 0000000..c790db8 --- /dev/null +++ b/docs/3_Developer_Guides/2_Application_Framework_Binder/Annexes/7_LEGACY_Binding_v2_references.md @@ -0,0 +1,757 @@ +--- +title: LEGACY Binding V2 references +--- + +# 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 char *info; /* some info about the api, can be NULL */ + 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 authorization */ + const char *info; /* some info about the verb, can be NULL */ + uint32_t session; /* authorization and session requirements of the verb */ +}; +``` + +The **session** flags is one 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 (Level Of Assurance) is set, 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. + * + * Calling this function is only forbidden during preinit. + * + * 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. + * + * Calling this function is only forbidden during preinit. + * + * 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 re-executed 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 initialization. + +```C +/* + * Tells that it requires the API of "name" to exist + * and if 'initialized' is not null to be initialized. + * Calling this function is only allowed within init. + * Returns 0 in case of success or -1 in case of error. + */ +int afb_daemon_require_api(const char *name, int initialized) +``` + +This function allows to give a different name to the binding. +It can be called during pre-init. + +```C +/* + * Set the name of the API to 'name'. + * Calling this function is only allowed within preinit. + * Returns 0 in case of success or -1 in case of error. + */ +int afb_daemon_rename_api(const char *name); +``` + +## 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. 'status' a status being 0 on success or negative when an error occurred, + * 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 status, 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 0 in case of success or a negative value 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 destroys 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 + * informational 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 + * 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. + */ +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 asynchronous 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'. + * 'status' is 0 on success or negative when on 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. + * + * See also: + * - 'afb_req_subcall_req' that is convenient to keep request alive automatically. + * - 'afb_req_subcall_sync' the synchronous version + */ +void afb_req_subcall( + struct afb_req req, + const char *api, + const char *verb, + struct json_object *args, + void (*callback)(void *closure, int status, 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'. + * On completion, the function 'callback' is invoked with the + * original request 'req', the 'closure' given at call and two + * other parameters: 'iserror' and 'result'. + * 'status' is 0 on success or negative when on 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. + * + * See also: + * - 'afb_req_subcall' that doesn't keep request alive automatically. + * - 'afb_req_subcall_sync' the synchronous version + */ +static inline void afb_req_subcall_req(struct afb_req req, const char *api, const char *verb, struct json_object *args, void (*callback)(void *closure, int iserror, struct json_object *result, struct afb_req req), void *closure) +{ + req.itf->subcall_req(req.closure, api, verb, args, callback, 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 until completion of the request. + * It returns 0 on success or a negative value on error answer. + * 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. + * + * See also: + * - 'afb_req_subcall_req' that is convenient to keep request alive automatically. + * - 'afb_req_subcall' that doesn't keep request alive automatically. + */ +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, ...); +``` + +The functions below allow a binding involved in the platform security +to explicitly check a permission of a client or to get the calling +application identity. + +```C +/* + * Check whether the 'permission' is granted or not to the client + * identified by 'req'. + * + * Returns 1 if the permission is granted or 0 otherwise. + */ +int afb_req_has_permission(struct afb_req req, const char *permission); + +/* + * Get the application identifier of the client application for the + * request 'req'. + * + * Returns the application identifier or NULL when the application + * can not be identified. + * + * The returned value if not NULL must be freed by the caller + */ +char *afb_req_get_application_id(struct afb_req req); + +/* + * Get the user identifier (UID) of the client application for the + * request 'req'. + * + * Returns -1 when the application can not be identified. + */ +int afb_req_get_uid(struct afb_req req); +``` + +## 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. \ No newline at end of file -- cgit 1.2.3-korg