improves documentation
authorJosé Bollo <jose.bollo@iot.bzh>
Fri, 27 May 2016 20:18:26 +0000 (22:18 +0200)
committerJosé Bollo <jose.bollo@iot.bzh>
Fri, 27 May 2016 20:18:26 +0000 (22:18 +0200)
Change-Id: Idbd1b735571c2e35daed23d43f8d5d3990881533
Signed-off-by: José Bollo <jose.bollo@iot.bzh>
doc/FAQ.html
doc/FAQ.md
doc/afb-daemon-vocabulary.html
doc/afb-daemon-vocabulary.md
doc/afb-plugin-writing.html
doc/afb-plugin-writing.md
include/afb/afb-plugin.h
plugins/samples/HelloWorld.c
plugins/samples/tic-tac-toe.c

index ffda81a..9833781 100644 (file)
@@ -8,7 +8,7 @@
 <h1>Frequently Asked Question about AFB-DAEMON</h1>
 
 <pre><code>version: 1
-Date:    26 mai 2016
+Date:    27 mai 2016
 Author:  José Bollo
 </code></pre>
 
index 9364731..6110582 100644 (file)
@@ -1,7 +1,7 @@
 Frequently Asked Question about AFB-DAEMON
 ==========================================
     version: 1
-    Date:    26 mai 2016
+    Date:    27 mai 2016
     Author:  José Bollo
 
 TABLE-OF-CONTENT-HERE
index 096f507..fadd1de 100644 (file)
@@ -8,7 +8,7 @@
 <h1>Vocabulary for AFB-DAEMON</h1>
 
 <pre><code>version: 1
-Date:    26 mai 2016
+Date:    27 mai 2016
 Author:  José Bollo
 </code></pre>
 
index 7177194..8427b73 100644 (file)
@@ -1,7 +1,7 @@
 Vocabulary for AFB-DAEMON
 =========================
     version: 1
-    Date:    26 mai 2016
+    Date:    27 mai 2016
     Author:  José Bollo
 
 TABLE-OF-CONTENT-HERE
index 9a98ffe..9873b62 100644 (file)
@@ -37,6 +37,8 @@ Author:  José Bollo
   </ul>
   </li>
   <li><a href="#The.Tic-Tac-Toe.example">The Tic-Tac-Toe example</a></li>
+  <li><a href="#Dependencies.when.compiling">Dependencies when compiling</a></li>
+  <li><a href="#Header.files.to.include">Header files to include</a></li>
   <li><a href="#Choosing.names">Choosing names</a>
   <ul>
    <li><a href="#Names.for.API..plugin.">Names for API (plugin)</a></li>
@@ -45,8 +47,6 @@ Author:  José Bollo
    <li><a href="#Forging.names.widely.available">Forging names widely available</a></li>
   </ul>
   </li>
-  <li><a href="#Options.to.set.when.compiling.plugins">Options to set when compiling plugins</a></li>
-  <li><a href="#Header.files.to.include">Header files to include</a></li>
   <li><a href="#Writing.a.synchronous.verb.implementation">Writing a synchronous verb implementation</a>
   <ul>
    <li><a href="#The.incoming.request">The incoming request</a></li>
@@ -265,6 +265,72 @@ This plugin example is in <em>plugins/samples/tic-tac-toe.c</em>.</p>
 
 <p>This plugin is named <strong><em>tictactoe</em></strong>.</p>
 
+<a name="Dependencies.when.compiling"></a>
+<h2>Dependencies when compiling</h2>
+
+<p>Afb-daemon provides a configuration file for <em>pkg-config</em>.
+Typing the command</p>
+
+<pre><code>pkg-config --cflags afb-daemon
+</code></pre>
+
+<p>will print the flags to use for compiling, like this:</p>
+
+<pre><code>$ pkg-config --cflags afb-daemon
+-I/opt/local/include -I/usr/include/json-c 
+</code></pre>
+
+<p>For linking, you should use</p>
+
+<pre><code>$ pkg-config --libs afb-daemon
+-ljson-c
+</code></pre>
+
+<p>As you see, afb-daemon automatically includes dependency to json-c.
+This is done through the <strong>Requires</strong> keyword of pkg-config
+because almost all plugin will use <strong>json-c</strong>.</p>
+
+<p>If this behaviour is a problem, let us know.</p>
+
+<p>Internally, afb-daemon uses <strong>libsystemd</strong> for its event loop
+and for its binding to D-Bus.
+Plugins developpers are encouraged to also use this library.
+But it is a matter of choice.
+Thus there is no dependency to <strong>libsystemd</strong>.</p>
+
+<blockquote><p>Afb-daemon provides no library for plugins.
+The functions that the plugin need to have are given
+to the plugin at runtime through pointer using read-only
+memory.</p></blockquote>
+
+<a name="Header.files.to.include"></a>
+<h2>Header files to include</h2>
+
+<p>The plugin <em>tictactoe</em> has the following lines for its includes:</p>
+
+<pre><code>#define _GNU_SOURCE
+#include &lt;stdio.h&gt;
+#include &lt;string.h&gt;
+#include &lt;json-c/json.h&gt;
+#include &lt;afb/afb-plugin.h&gt;
+</code></pre>
+
+<p>The header <em>afb/afb-plugin.h</em> includes all the features that a plugin
+needs except two foreign header that must be included by the plugin
+if it needs it:</p>
+
+<ul>
+<li><em>json-c/json.h</em>: this header must be include to handle json objects;</li>
+<li><em>systemd/sd-event.h</em>: this must be include to access the main loop;</li>
+<li><em>systemd/sd-bus.h</em>: this may be include to use dbus connections.</li>
+</ul>
+
+
+<p>The <em>tictactoe</em> plugin does not use systemd features so it is not included.</p>
+
+<p>When including <em>afb/afb-plugin.h</em>, the macro <strong>_GNU_SOURCE</strong> must be
+defined.</p>
+
 <a name="Choosing.names"></a>
 <h2>Choosing names</h2>
 
@@ -344,60 +410,6 @@ valid javascript identifier.</p>
 rely on the case sensitivity and to avoid the use of
 names different only by the case.</p>
 
-<a name="Options.to.set.when.compiling.plugins"></a>
-<h2>Options to set when compiling plugins</h2>
-
-<p>Afb-daemon provides a configuration file for <em>pkg-config</em>.
-Typing the command</p>
-
-<pre><code>pkg-config --cflags afb-daemon
-</code></pre>
-
-<p>will print the flags to use for compiling, like this:</p>
-
-<pre><code>$ pkg-config --cflags afb-daemon
--I/opt/local/include -I/usr/include/json-c 
-</code></pre>
-
-<p>For linking, you should use</p>
-
-<pre><code>$ pkg-config --libs afb-daemon
--ljson-c
-</code></pre>
-
-<p>As you see, afb-daemon automatically includes dependency to json-c.
-This is done through the <strong>Requires</strong> keyword of pkg-config.</p>
-
-<p>If this behaviour is a problem, let us know.</p>
-
-<a name="Header.files.to.include"></a>
-<h2>Header files to include</h2>
-
-<p>The plugin <em>tictactoe</em> has the following lines for its includes:</p>
-
-<pre><code>#define _GNU_SOURCE
-#include &lt;stdio.h&gt;
-#include &lt;string.h&gt;
-#include &lt;json-c/json.h&gt;
-#include &lt;afb/afb-plugin.h&gt;
-</code></pre>
-
-<p>The header <em>afb/afb-plugin.h</em> includes all the features that a plugin
-needs except two foreign header that must be included by the plugin
-if it needs it:</p>
-
-<ul>
-<li><em>json-c/json.h</em>: this header must be include to handle json objects;</li>
-<li><em>systemd/sd-event.h</em>: this must be include to access the main loop;</li>
-<li><em>systemd/sd-bus.h</em>: this may be include to use dbus connections.</li>
-</ul>
-
-
-<p>The <em>tictactoe</em> plugin does not use systemd features so it is not included.</p>
-
-<p>When including <em>afb/afb-plugin.h</em>, the macro <strong>_GNU_SOURCE</strong> must be
-defined.</p>
-
 <a name="Writing.a.synchronous.verb.implementation"></a>
 <h2>Writing a synchronous verb implementation</h2>
 
@@ -879,7 +891,7 @@ const struct AFB_interface *afbitf;
 static const struct AFB_verb_desc_v1 plugin_verbs[] = {
    /* 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= "Tells the server to play" },
+   { .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" },
@@ -1196,9 +1208,85 @@ journal, syslog or kmsg. (See man sd-daemon).</p>
 <a name="Sending.events"></a>
 <h2>Sending events</h2>
 
+<p>Since version 0.5, plugins can broadcast events to any potential listener.
+This kind of bradcast is not targeted. Event targeted will come in a future
+version of afb-daemon.</p>
+
+<p>The plugin <em>tic-tac-toe</em> broadcasts events when the board changes.
+This is done in the function <strong>changed</strong>:</p>
+
+<pre><code>/*
+ * 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-&gt;daemon, reason, description);
+}
+</code></pre>
+
+<p>The description of the changed board is pushed via the daemon interface.</p>
+
+<p>Within the plugin <em>tic-tac-toe</em>, the <em>reason</em> indicates the origin of
+the change. For the function <strong>afb_daemon_broadcast_event</strong>, the second
+parameter is the name of the broadcasted event. The third argument is the
+object that is transmitted with the event.</p>
+
+<p>The function <strong>afb_daemon_broadcast_event</strong> is defined as below:</p>
+
+<pre><code>/*
+ * 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 plugin.
+ */
+void afb_daemon_broadcast_event(struct afb_daemon daemon, const char *name, struct json_object *object);
+</code></pre>
+
+<p>In fact the event name received by the listener is prefixed with
+the name of the plugin. So when the change occurs after a move, the
+reason is <strong>move</strong> and then the clients receive the event <strong>tictactoe/move</strong>.</p>
+
+<blockquote><p>Note that nothing is said about the case sensitivity of event names.
+However, the event is always prefixed with the name that the plugin
+declared, with the same case, followed with a slash /.
+Thus it is safe to compare event using a case sensitive comparison.</p></blockquote>
+
 <a name="Writing.an.asynchronous.verb.implementation"></a>
 <h2>Writing an asynchronous verb implementation</h2>
 
+<p>/<em>
+ * signals a change of the board
+ </em>/
+static void changed(struct board <em>board, const char </em>reason)
+{
+    struct waiter <em>waiter, </em>next;
+    struct json_object *description;</p>
+
+<pre><code>/* get the description */
+description = describe(board);
+
+waiter = board-&gt;waiters;
+board-&gt;waiters = NULL;
+while (waiter != NULL) {
+        next = waiter-&gt;next;
+        afb_req_success(waiter-&gt;req, json_object_get(description), reason);
+        afb_req_unref(waiter-&gt;req);
+        free(waiter);
+        waiter = next;
+}
+
+afb_event_sender_push(afb_daemon_get_event_sender(afbitf-&gt;daemon), reason, description);
+</code></pre>
+
+<p>}</p>
+
 <a name="How.to.build.a.plugin"></a>
 <h2>How to build a plugin</h2>
 
index 734b15a..7861021 100644 (file)
@@ -178,6 +178,65 @@ This plugin example is in *plugins/samples/tic-tac-toe.c*.
 
 This plugin is named ***tictactoe***.
 
+Dependencies when compiling
+---------------------------
+
+Afb-daemon provides a configuration file for *pkg-config*.
+Typing the command
+
+       pkg-config --cflags afb-daemon
+
+will print the flags to use for compiling, like this:
+
+       $ 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
+
+As you see, afb-daemon automatically includes dependency to json-c.
+This is done through the **Requires** keyword of pkg-config
+because almost all plugin will use **json-c**.
+
+If this behaviour is a problem, let us know.
+
+Internally, afb-daemon uses **libsystemd** for its event loop
+and for its binding to D-Bus.
+Plugins developpers are encouraged to also use this library.
+But it is a matter of choice.
+Thus there is no dependency to **libsystemd**.
+
+> Afb-daemon provides no library for plugins.
+> The functions that the plugin need to have are given
+> to the plugin at runtime through pointer using read-only
+> memory.
+
+Header files to include
+-----------------------
+
+The plugin *tictactoe* has the following lines for its includes:
+
+       #define _GNU_SOURCE
+       #include <stdio.h>
+       #include <string.h>
+       #include <json-c/json.h>
+       #include <afb/afb-plugin.h>
+
+The header *afb/afb-plugin.h* includes all the features that a plugin
+needs except two foreign header that must be included by the plugin
+if it needs it:
+
+- *json-c/json.h*: this header must be include to handle json objects;
+- *systemd/sd-event.h*: this must be include to access the main loop;
+- *systemd/sd-bus.h*: this may be include to use dbus connections.
+
+The *tictactoe* plugin does not use systemd features so it is not included.
+
+When including *afb/afb-plugin.h*, the macro **_GNU_SOURCE** must be
+defined.
+
 Choosing names
 --------------
 
@@ -248,53 +307,6 @@ It is also a good practice, even for arguments, to not
 rely on the case sensitivity and to avoid the use of
 names different only by the case.
 
-Options to set when compiling plugins
--------------------------------------
-
-Afb-daemon provides a configuration file for *pkg-config*.
-Typing the command
-
-       pkg-config --cflags afb-daemon
-
-will print the flags to use for compiling, like this:
-
-       $ 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
-
-As you see, afb-daemon automatically includes dependency to json-c.
-This is done through the **Requires** keyword of pkg-config.
-
-If this behaviour is a problem, let us know.
-
-Header files to include
------------------------
-
-The plugin *tictactoe* has the following lines for its includes:
-
-       #define _GNU_SOURCE
-       #include <stdio.h>
-       #include <string.h>
-       #include <json-c/json.h>
-       #include <afb/afb-plugin.h>
-
-The header *afb/afb-plugin.h* includes all the features that a plugin
-needs except two foreign header that must be included by the plugin
-if it needs it:
-
-- *json-c/json.h*: this header must be include to handle json objects;
-- *systemd/sd-event.h*: this must be include to access the main loop;
-- *systemd/sd-bus.h*: this may be include to use dbus connections.
-
-The *tictactoe* plugin does not use systemd features so it is not included.
-
-When including *afb/afb-plugin.h*, the macro **_GNU_SOURCE** must be
-defined.
-
 Writing a synchronous verb implementation
 -----------------------------------------
 
@@ -755,7 +767,7 @@ The description of the plugin is defined as below.
        static const struct AFB_verb_desc_v1 plugin_verbs[] = {
           /* 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= "Tells the server to play" },
+          { .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" },
@@ -920,10 +932,81 @@ journal, syslog or kmsg. (See man sd-daemon).
 Sending events
 --------------
 
+Since version 0.5, plugins can broadcast events to any potential listener.
+This kind of bradcast is not targeted. Event targeted will come in a future
+version of afb-daemon.
+
+The plugin *tic-tac-toe* broadcasts events when the board changes.
+This is done in the function **changed**:
+
+       /*
+        * 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 the plugin *tic-tac-toe*, the *reason* indicates the origin of
+the change. For the function **afb_daemon_broadcast_event**, the second
+parameter is the name of the broadcasted event. The third argument is the
+object that is transmitted with the event.
+
+The function **afb_daemon_broadcast_event** is defined as below:
+
+       /*
+        * 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 plugin.
+        */
+       void afb_daemon_broadcast_event(struct afb_daemon daemon, const char *name, struct json_object *object);
+
+In fact the event name received by the listener is prefixed with
+the name of the plugin. So when the change occurs after a move, the
+reason is **move** and then the clients receive the event **tictactoe/move**.
+
+> Note that nothing is said about the case sensitivity of event names.
+> However, the event is always prefixed with the name that the plugin
+> declared, with the same case, followed with a slash /.
+> Thus it is safe to compare event using a case sensitive comparison.
+
 
 Writing an asynchronous verb implementation
 -------------------------------------------
 
+/*
+ * 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);
+}
 
 How to build a plugin
 ---------------------
index cad55fb..3d16c7a 100644 (file)
@@ -56,10 +56,10 @@ enum  AFB_plugin_version
  */
 enum AFB_session_v1
 {
-       AFB_SESSION_NONE = 0,   /* no session and no authentification required */
-       AFB_SESSION_CREATE = 1, /* obsolete */
-       AFB_SESSION_CLOSE = 2,  /* closes the session after authentification */
-       AFB_SESSION_RENEW = 4,  /* refreshes the token after authentification */
+       AFB_SESSION_NONE = 0,   /* nothing required */
+       AFB_SESSION_CREATE = 1, /* Obsolete */
+       AFB_SESSION_CLOSE = 2,  /* After token authentification, closes the session at end */
+       AFB_SESSION_RENEW = 4,  /* After token authentification, refreshes the token at end */
        AFB_SESSION_CHECK = 8,  /* Requires token authentification */
 
        AFB_SESSION_LOA_GE = 16, /* check that the LOA is greater or equal to the given value */
@@ -67,12 +67,13 @@ enum AFB_session_v1
        AFB_SESSION_LOA_EQ = 48, /* check that the LOA is equal to the given value */
 
        AFB_SESSION_LOA_SHIFT = 6, /* shift for LOA */
-       AFB_SESSION_LOA_MASK = 3,  /* mask for LOA */
+       AFB_SESSION_LOA_MASK = 7,  /* mask for LOA */
 
        AFB_SESSION_LOA_0 = 0,   /* value for LOA of 0 */
        AFB_SESSION_LOA_1 = 64,  /* value for LOA of 1 */
        AFB_SESSION_LOA_2 = 128, /* value for LOA of 2 */
        AFB_SESSION_LOA_3 = 192, /* value for LOA of 3 */
+       AFB_SESSION_LOA_4 = 256, /* value for LOA of 4 */
 
        AFB_SESSION_LOA_LE_0 = AFB_SESSION_LOA_LE | AFB_SESSION_LOA_0, /* check LOA <= 0 */
        AFB_SESSION_LOA_LE_1 = AFB_SESSION_LOA_LE | AFB_SESSION_LOA_1, /* check LOA <= 1 */
@@ -207,6 +208,16 @@ static inline struct sd_bus *afb_daemon_get_system_bus(struct afb_daemon daemon)
        return daemon.itf->get_system_bus(daemon.closure);
 }
 
+/*
+ * 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 plugin.
+ */
+static inline void afb_daemon_broadcast_event(struct afb_daemon daemon, const char *name, struct json_object *object)
+{
+       return afb_event_sender_push(afb_daemon_get_event_sender(daemon), name, object);
+}
+
 /*
  * Send a message described by 'fmt' and following parameters
  * to the journal for the verbosity 'level'.
index 78a1eaf..fe11784 100644 (file)
@@ -54,8 +54,8 @@ static void pingBug (struct afb_req request)
 static void pingEvent(struct afb_req request)
 {
        json_object *query = afb_req_json(request);
-       afb_event_sender_push(afb_daemon_get_event_sender(interface->daemon), "event", query);
-       ping(request, json_object_get(query), "event");
+       afb_daemon_broadcast_event(interface->daemon, "event", json_object_get(query));
+       ping(request, query, "event");
 }
 
 
index 79aaaf5..c372e99 100644 (file)
@@ -299,7 +299,7 @@ static void changed(struct board *board, const char *reason)
                waiter = next;
        }
 
-       afb_event_sender_push(afb_daemon_get_event_sender(afbitf->daemon), reason, description);
+       afb_daemon_broadcast_event(afbitf->daemon, reason, description);
 }
 
 /*
@@ -586,7 +586,7 @@ static void wait(struct afb_req req)
 static const struct AFB_verb_desc_v1 plugin_verbs[] = {
    /* 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= "Tells the server to play" },
+   { .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" },