1 HOWTO WRITE a PLUGIN for AFB-DAEMON
2 ===================================
12 The binder afb-daemon serves files through
13 the HTTP protocol and offers access to API's through
14 HTTP or WebSocket protocol.
16 The plugins are used to add API's to afb-daemon.
17 This part describes how to write a plugin for afb-daemon.
18 Excepting this summary, this part is intended to be read
21 Before going into details, through a tiny example,
22 a short overview plugins basis is needed.
24 ### Nature of a plugin
26 A plugin is a separate piece of code made of a shared library.
27 The plugin is loaded and activated by afb-daemon when afb-daemon
30 Technically, a plugin is not linked to any library of afb-daemon.
32 ### Live cycle of a plugin within afb-daemon
34 The plugins are loaded and activated when afb-daemon starts.
36 At start, the plugin initialise itself.
37 If it fails to initialise then afb-daemon stops.
39 Conversely, if it success to initialize, it must declare
40 a name, that must be unique, and a list of API's verbs.
42 When initialized, the functions implementing the API's verbs
43 of the plugin are activated on call.
45 At the end, nothing special is done by afb-daemon.
46 Consequently, developpers of plugins should use 'atexit'
47 or 'on_exit' during initialisation if they need to
48 perform specific actions when stopping.
50 ### Content of a plugin
52 For afb-daemon, a plugin contains 2 different
53 things: names and functions.
55 There is two kind of names:
56 - the name of the plugin,
57 - the names of the verbs.
59 There is two kind of functions:
60 - the initialisation function
61 - functions implementing verbs
63 Afb-daemon translates the name of the method that is
64 invoked to a pair of API and verb names. For example,
65 the method named **foo/bar** translated to the API
66 name **foo** and the verb name **bar**.
67 To serve it, afb-daemon search the plugin that record
68 the name **foo** and if it also recorded the verb **bar**,
69 it calls the implementation function declared for this verb.
71 Afb-daemon make no distinction between lower case
72 and upper case when searching for a method.
73 Thus, The names **TicTacToe/Board** and **tictactoe/borad**
76 #### The name of the plugin
78 The name of the plugin is also known as the name
79 of the API that defines the plugin.
81 This name is also known as the prefix.
83 The name of a plugin MUST be unique within afb-daemon.
85 For example, when a client of afb-daemon
86 calls a method named **foo/bar**. Afb-daemon
87 extracts the prefix **foo** and the suffix **bar**.
88 **foo** is the API name and must match a plugin name,
89 the plugin that implements the verb **bar**.
93 Each plugin exposes a set of verbs that can be called
94 by client of afb-daemon.
96 The name of a verb MUST be unique within a plugin.
98 Plugins link verbs to functions that are called
99 when clients emit requests for that verb.
101 For example, when a client of afb-daemon
102 calls a method named **foo/bar**.
104 #### The initialisation function
106 The initialisation function serves several purposes.
108 1. It allows afb-daemon to check the version
109 of the plugin using the name of the initialisation
110 functions that it found. Currently, the initialisation
111 function is named **pluginAfbV1Register**. It identifies
112 the first version of plugins.
114 2. It allows the plugin to initialise itself.
116 3. It serves to the plugin to declare names, descriptions,
117 requirements and implmentations of the verbs that it exposes.
119 #### Functions implementing verbs
121 When a method is called, afb-daemon constructs a request
122 object and pass it to the implementation function for verb
123 within the plugin of the API.
125 An implementation function receives a request object that
126 is used to get arguments of the request, to send
127 answer, to store session data.
129 A plugin MUST send an answer to the request.
131 But it is not mandatory to send the answer
132 before to return from the implementing function.
133 This behaviour is important for implementing
134 asynchronous actions.
136 Implementation functions that always reply to the request
137 before returning are named *synchronous implementations*.
138 Those that don't always reply to the request before
139 returning are named *asynchronous implementations*.
141 Asynchronous implementations typically initiate an
142 asynchronous action and record to send the reply
143 on completion of this action.
145 The Tic-Tac-Toe example
146 -----------------------
148 This part explains how to write an afb-plugin.
149 For the sake of being practical we will use many
150 examples from the tic-tac-toe example.
151 This plugin example is in *plugins/samples/tic-tac-toe.c*.
153 This plugin is named ***tictactoe***.
158 The designer of a plugin must defines names for its plugin
159 (or its API) and for the verbs of its API. He also
160 must defines names for arguments given by name.
162 While forging names, the designer should take into account
163 the rules for making valid names and some rules that make
164 the names easy to use across plaforms.
166 The names and strings used ALL are UTF-8 encoded.
168 ### Names for API (plugin)
170 The names of the API are checked.
171 All characters are authorised except:
173 - the control characters (\u0000 .. \u001f)
174 - the characters of the set { ' ', '"', '#', '%', '&',
175 '\'', '/', '?', '`', '\x7f' }
177 In other words the set of forbidden characters is
178 { \u0000..\u0020, \u0022, \u0023, \u0025..\u0027,
179 \u002f, \u003f, \u0060, \u007f }.
181 Afb-daemon make no distinction between lower case
182 and upper case when searching for an API by its name.
186 The names of the verbs are not checked.
188 However, the validity rules for verb's names are the
189 same as for API's names except that the dot (.) character
192 Afb-daemon make no distinction between lower case
193 and upper case when searching for an API by its name.
195 ### Names for arguments
197 The names for arguments are not restricted and can be
200 The arguments are searched with the case sensitive
201 string comparison. Thus the names "index" and "Index"
204 ### Forging names widely available
206 The key names of javascript object can be almost
207 anything using the arrayed notation:
211 That is not the case with the dot notation:
215 Using the dot notation, the key must be a valid javascript
218 For this reason, the chosen names should better be
219 valid javascript identifier.
221 It is also a good practice, even for arguments, to not
222 rely on the case sensitivity and to avoid the use of
223 names different only by the case.
225 Options to set when compiling plugins
226 -------------------------------------
228 Afb-daemon provides a configuration file for *pkg-config*.
231 pkg-config --cflags afb-daemon
233 will print the flags to use for compiling, like this:
235 $ pkg-config --cflags afb-daemon
236 -I/opt/local/include -I/usr/include/json-c
238 For linking, you should use
240 $ pkg-config --libs afb-daemon
243 As you see, afb-daemon automatically includes dependency to json-c.
244 This is done through the **Requires** keyword of pkg-config.
246 If this behaviour is a problem, let us know.
248 Header files to include
249 -----------------------
251 The plugin *tictactoe* has the following lines for its includes:
256 #include <json-c/json.h>
257 #include <afb/afb-plugin.h>
259 The header *afb/afb-plugin.h* includes all the features that a plugin
260 needs except two foreign header that must be included by the plugin
263 - *json-c/json.h*: this header must be include to handle json objects;
264 - *systemd/sd-event.h*: this must be include to access the main loop;
265 - *systemd/sd-bus.h*: this may be include to use dbus connections.
267 The *tictactoe* plugin does not use systemd features so it is not included.
269 When including *afb/afb-plugin.h*, the macro **_GNU_SOURCE** must be
272 Writing a synchronous verb implementation
273 -----------------------------------------
275 The verb **tictactoe/board** is a synchronous implementation.
281 static void board(struct afb_req req)
284 struct json_object *description;
286 /* retrieves the context for the session */
287 board = board_of_req(req);
288 INFO(afbitf, "method 'board' called for boardid %d", board->id);
290 /* describe the board */
291 description = describe(board);
293 /* send the board's description */
294 afb_req_success(req, description, NULL);
297 This examples show many aspects of writing a synchronous
298 verb implementation. Let summarize it:
300 1. The function **board_of_req** retrieves the context stored
301 for the plugin: the board.
303 2. The macro **INFO** sends a message of kind *INFO*
304 to the logging system. The global variable named **afbitf**
305 used represents the interface to afb-daemon.
307 3. The function **describe** creates a json_object representing
310 4. The function **afb_req_success** sends the reply, attaching to
311 it the object *description*.
313 ### The incoming request
315 For any implementation, the request is received by a structure of type
318 ***Important: note that this is a PLAIN structure, not a pointer to a structure.***
320 This structure, here named *req*, is used
322 *req* is used to get arguments of the request, to send
323 answer, to store session data.
325 This object and its interface is defined and documented
326 in the file names *afb/afb-req-itf.h*
328 The above example uses 2 times the request object *req*.
330 The first time, it is used for retrieving the board attached to
331 the session of the request.
333 The second time, it is used to send the reply: an object that
334 describes the current board.
336 ### Associating an object to the session for the plugin
338 When the plugin *tic-tac-toe* receives a request, it musts regain
339 the board that describes the game associated to the session.
341 For a plugin, having data associated to a session is a common case.
342 This data is called the context of the plugin for the session.
343 For the plugin *tic-tac-toe*, the context is the board.
345 The requests *afb_req* offer four functions for
346 storing and retrieving the context associated to the session.
350 - **afb_req_context_get**:
351 retrieves the context data stored for the plugin.
353 - **afb_req_context_set**:
354 store the context data of the plugin.
356 - **afb_req_context**:
357 retrieves the context data of the plugin,
358 if needed, creates the context and store it.
360 - **afb_req_context_clear**:
361 reset the stored data.
363 The plugin *tictactoe* use a convenient function to retrieve
364 its context: the board. This function is *board_of_req*:
367 * retrieves the board of the request
369 static inline struct board *board_of_req(struct afb_req req)
371 return afb_req_context(req, (void*)get_new_board, (void*)release_board);
374 The function **afb_req_context** ensure an existing context
375 for the session of the request.
376 Its two last arguments are functions. Here, the casts are required
377 to avoid a warning when compiling.
379 Here is the definition of the function **afb_req_context**
382 * Gets the pointer stored by the plugin for the session of 'req'.
383 * If the stored pointer is NULL, indicating that no pointer was
384 * already stored, afb_req_context creates a new context by calling
385 * the function 'create_context' and stores it with the freeing function
388 static inline void *afb_req_context(struct afb_req req, void *(*create_context)(), void (*free_context)(void*))
390 void *result = afb_req_context_get(req);
391 if (result == NULL) {
392 result = create_context();
393 afb_req_context_set(req, result, free_context);
398 The second argument if the function that creates the context.
399 For the plugin *tic-tac-toe* it is the function **get_new_board**.
400 The function **get_new_board** creates a new board and set its
401 count of use to 1. The boards are counting their count of use
402 to free there ressources when no more used.
404 The third argument if the function that frees the context.
405 For the plugin *tic-tac-toe* it is the function **release_board**.
406 The function **release_board** decrease the the count of use of
407 the board given as argument. If the use count decrease to zero,
408 the board data are freed.
410 ### Sending the reply to a request
412 Sending a reply to a request must be done at most one time.
414 Two kinds of replies can be made: successful replies and
417 The functions to send replies are defined as below:
420 * Sends a reply of kind success to the request 'req'.
421 * The status of the reply is automatically set to "success".
422 * Its send the object 'obj' (can be NULL) with an
423 * informationnal comment 'info (can also be NULL).
425 static inline void afb_req_success(struct afb_req req, struct json_object *obj, const char *info)
427 req.itf->success(req.closure, obj, info);
431 * Same as 'afb_req_success' but the 'info' is a formatting
432 * string followed by arguments.
434 static inline void afb_req_success_f(struct afb_req req, struct json_object *obj, const char *info, ...)
438 va_start(args, info);
439 if (info == NULL || vasprintf(&message, info, args) < 0)
442 afb_req_success(req, obj, message);
447 * Sends a reply of kind failure to the request 'req'.
448 * The status of the reply is set to 'status' and an
449 * informationnal comment 'info' (can also be NULL) can be added.
451 * Note that calling afb_req_fail("success", info) is equivalent
452 * to call afb_req_success(NULL, info). Thus even if possible it
453 * is strongly recommanded to NEVER use "success" for status.
455 static inline void afb_req_fail(struct afb_req req, const char *status, const char *info)
457 req.itf->fail(req.closure, status, info);
461 * Same as 'afb_req_fail' but the 'info' is a formatting
462 * string followed by arguments.
464 static inline void afb_req_fail_f(struct afb_req req, const char *status, const char *info, ...)
468 va_start(args, info);
469 if (info == NULL || vasprintf(&message, info, args) < 0)
472 afb_req_fail(req, status, message);
478 Getting argument of invocation
479 ------------------------------
481 Sending messages to the log system
482 ----------------------------------
484 How to build a plugin
485 ---------------------
487 Afb-daemon provides a *pkg-config* configuration file.