snaums.de - OpenWRThttps://www.snaums.de/2021-12-04T18:45:00+01:00Everything Ubus2021-12-04T18:45:00+01:002021-12-04T18:45:00+01:00snaumstag:www.snaums.de,2021-12-04:/informatik/everything-ubus.html<p>Computer scientists seem to reinvent the wheel quite often. For the more cynical observers every new Remote-Procedure-Call implementation looks quite like CORBA or Sun-RPC, but under a different name, different interfaces and - more often than not - some restricted features or inefficiencies. Welcome to Ubus! [<a href="https://git.openwrt.org/project/ubus.git">2</a>, <a href="https://openwrt.org/docs/techref/ubus">4</a>]</p>
<h2 id="what-is-ubus">What is Ubus</h2>
<p>Ubus …</p><p>Computer scientists seem to reinvent the wheel quite often. For the more cynical observers every new Remote-Procedure-Call implementation looks quite like CORBA or Sun-RPC, but under a different name, different interfaces and - more often than not - some restricted features or inefficiencies. Welcome to Ubus! [<a href="https://git.openwrt.org/project/ubus.git">2</a>, <a href="https://openwrt.org/docs/techref/ubus">4</a>]</p>
<h2 id="what-is-ubus">What is Ubus</h2>
<p>Ubus is a (local) message bus, specifically for the embedded Linux distribution OpenWRT [<a href="https://openwrt.org/">1</a>]. OpenWRT is a distribution which brings Linux to many Routers, Access Points or Internet Modems. For example Freifunk mostly uses OpenWRT based installations for their devices.</p>
<p>Ubus contains from a server process (ubusd), which provides the message bus. Client applications can then connect to the Ubus, ask for specific objects on the bus and call methods of these objects, or they can create new objects and provide methods. There are no attributes on objects in the bus, only methods, i.e. remote procedure calls. In a sense, the ubusd-process is more of a broker, handling the discovery of objects.</p>
<p>Ubus is based on libubox [<a href="https://git.openwrt.org/project/libubox.git">3</a>], which offers a message-queue like programming interface, allowing C programs to wait for new messages, some functions for marshalling and demarshalling of user data inside so called blobmessages. libubus is <strong>single threaded</strong> in design. There is a single <code>blob_buf b</code>, so it is only possible to handle one message at a time, adding another thread, which also tries to interact with Ubus will make it crash.</p>
<p>In the remainder of this post I'm gonna refer to the application providing an object and methods as the server, and the one using an object and its methods as the client application.</p>
<h3 id="trying-things-out">Trying things out</h3>
<p>While working on the server is might come in handy to try things out against a - sort of - reference implementation of a client. On OpenWRT there is the <code>ubus</code> tool preinstalled. It can be used to query the ubus objects and their methods and to call methods or subscribe to events. These are some examples:</p>
<div class="highlight"><pre><span class="code-line"><span></span><code>ubus<span class="w"> </span>list</code></span>
<span class="code-line">ubus<span class="w"> </span>-v<span class="w"> </span>list</span>
<span class="code-line">ubus<span class="w"> </span>call<span class="w"> </span><span class="nb">test</span><span class="w"> </span>sendHello<span class="w"> </span><span class="s2">"{'id': 12, 'message': 'Testmessage'}"</span></span>
<span class="code-line"></span></pre></div>
<h2 id="defining-objects-and-methods-on-the-server_1">Defining Objects and Methods on the Server</h2>
<p>First, we need to make a type for ubus to use. A type has a name and some methods. Methods are represented by a name which is used to identify the methods on the client-side. Methods also need the callback to be called when the method is requested by a client and it needs a so called polcy, if it takes any parameters. A policy represents the possible arguments of the function and their types. Not, that return values do not need a policy, but you may want one nonetheless, more on that later.</p>
<p>Let's say we want an object "test", which has two methods: "hello", taking no parameters and returning "hello world" and a second one, taking an id of type int and a string "msg".</p>
<div class="highlight"><pre><span class="code-line"><span></span><code><span class="cp">#include</span><span class="w"> </span><span class="cpf"><libubox/blobmsg_json.h></span></code></span>
<span class="code-line"><span class="cp">#include</span><span class="w"> </span><span class="cpf"><libubus.h></span></span>
<span class="code-line"></span>
<span class="code-line"><span class="c1">// This enum is helpful, but not necessary.</span></span>
<span class="code-line"><span class="c1">// Its entries allow easier access to the methods later on</span></span>
<span class="code-line"><span class="k">enum</span><span class="w"> </span><span class="p">{</span></span>
<span class="code-line"><span class="w"> </span><span class="n">HELLO_ID</span><span class="p">,</span></span>
<span class="code-line"><span class="w"> </span><span class="n">HELLO_MSG</span><span class="p">,</span></span>
<span class="code-line"><span class="w"> </span><span class="n">__HELLO_MAX</span></span>
<span class="code-line"><span class="p">};</span></span>
<span class="code-line"></span>
<span class="code-line"></span>
<span class="code-line"><span class="c1">// the policy describes the paramters of a method</span></span>
<span class="code-line"><span class="k">static</span><span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">blobmsg_policy</span><span class="w"> </span><span class="n">hello_policy</span><span class="p">[]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span></span>
<span class="code-line"><span class="w"> </span><span class="c1">// id is of type int</span></span>
<span class="code-line"><span class="w"> </span><span class="p">[</span><span class="n">HELLO_ID</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="p">.</span><span class="n">name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">"id"</span><span class="p">,</span><span class="w"> </span><span class="p">.</span><span class="n">type</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">BLOBMSG_TYPE_INT32</span><span class="w"> </span><span class="p">},</span></span>
<span class="code-line"><span class="w"> </span><span class="c1">// message is of type string</span></span>
<span class="code-line"><span class="w"> </span><span class="p">[</span><span class="n">HELLO_MSG</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="p">.</span><span class="n">name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">"msg"</span><span class="p">,</span><span class="w"> </span><span class="p">.</span><span class="n">type</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">BLOBMSG_TYPE_STRING</span><span class="w"> </span><span class="p">},</span></span>
<span class="code-line"><span class="p">};</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="c1">// forward definitions of our callbacks</span></span>
<span class="code-line"><span class="kt">int</span><span class="w"> </span><span class="nf">testHello</span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="nc">ubus_context</span><span class="w"> </span><span class="o">*</span><span class="n">ctx</span><span class="p">,</span><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">ubus_object</span><span class="w"> </span><span class="o">*</span><span class="n">obj</span><span class="p">,</span></span>
<span class="code-line"><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">ubus_request_data</span><span class="w"> </span><span class="o">*</span><span class="n">req</span><span class="p">,</span><span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="n">method</span><span class="p">,</span></span>
<span class="code-line"><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">blob_attr</span><span class="w"> </span><span class="o">*</span><span class="n">msg</span><span class="p">);</span></span>
<span class="code-line"><span class="kt">int</span><span class="w"> </span><span class="nf">sendHello</span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="nc">ubus_context</span><span class="w"> </span><span class="o">*</span><span class="n">ctx</span><span class="p">,</span><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">ubus_object</span><span class="w"> </span><span class="o">*</span><span class="n">obj</span><span class="p">,</span></span>
<span class="code-line"><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">ubus_request_data</span><span class="w"> </span><span class="o">*</span><span class="n">req</span><span class="p">,</span><span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="n">method</span><span class="p">,</span></span>
<span class="code-line"><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">blob_attr</span><span class="w"> </span><span class="o">*</span><span class="n">msg</span><span class="p">);</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="c1">// setting up a list of methods, one without arguments (hello)</span></span>
<span class="code-line"><span class="c1">// and one with the sendHelloPolicy, so (int id, char* msg)</span></span>
<span class="code-line"><span class="k">static</span><span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">ubus_method</span><span class="w"> </span><span class="n">test_methods</span><span class="p">[]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span></span>
<span class="code-line"><span class="w"> </span><span class="n">UBUS_METHOD_NOARG</span><span class="p">(</span><span class="s">"hello"</span><span class="p">,</span><span class="w"> </span><span class="n">test_hello</span><span class="p">),</span></span>
<span class="code-line"><span class="w"> </span><span class="n">UBUS_METHOD</span><span class="p">(</span><span class="s">"sendHello"</span><span class="p">,</span><span class="w"> </span><span class="n">sendHello</span><span class="p">,</span><span class="w"> </span><span class="n">sendHelloPolicy</span><span class="p">),</span></span>
<span class="code-line"><span class="p">};</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="c1">// defining the type "test" with the above methods</span></span>
<span class="code-line"><span class="k">static</span><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">ubus_object_type</span><span class="w"> </span><span class="n">test_object_type</span><span class="w"> </span><span class="o">=</span></span>
<span class="code-line"><span class="w"> </span><span class="n">UBUS_OBJECT_TYPE</span><span class="p">(</span><span class="s">"test"</span><span class="p">,</span><span class="w"> </span><span class="n">test_methods</span><span class="p">);</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="c1">// and defining an object of type test with the methods</span></span>
<span class="code-line"><span class="k">static</span><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">ubus_object</span><span class="w"> </span><span class="n">test_object</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span></span>
<span class="code-line"><span class="w"> </span><span class="p">.</span><span class="n">name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">"test"</span><span class="p">,</span></span>
<span class="code-line"><span class="w"> </span><span class="p">.</span><span class="n">type</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="o">&</span><span class="n">test_object_type</span><span class="p">,</span></span>
<span class="code-line"><span class="w"> </span><span class="p">.</span><span class="n">methods</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">test_methods</span><span class="p">,</span></span>
<span class="code-line"><span class="w"> </span><span class="p">.</span><span class="n">n_methods</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">ARRAY_SIZE</span><span class="p">(</span><span class="n">test_methods</span><span class="p">),</span></span>
<span class="code-line"><span class="p">};</span></span>
<span class="code-line"></span></pre></div>
<p>We have now the data structures describing an object "test" of type "test" with the method "hello" and "sendHello". The list of possible types is this. Note there are arrays and tables which we'll need to cover later a bit more in depth:</p>
<div class="highlight"><pre><span class="code-line"><span></span><code><span class="k">enum</span><span class="w"> </span><span class="n">blobmsg_type</span><span class="w"> </span><span class="p">{</span></code></span>
<span class="code-line"><span class="w"> </span><span class="n">BLOBMSG_TYPE_UNSPEC</span><span class="p">,</span></span>
<span class="code-line"><span class="w"> </span><span class="n">BLOBMSG_TYPE_ARRAY</span><span class="p">,</span></span>
<span class="code-line"><span class="w"> </span><span class="n">BLOBMSG_TYPE_TABLE</span><span class="p">,</span></span>
<span class="code-line"><span class="w"> </span><span class="n">BLOBMSG_TYPE_STRING</span><span class="p">,</span></span>
<span class="code-line"><span class="w"> </span><span class="n">BLOBMSG_TYPE_INT64</span><span class="p">,</span></span>
<span class="code-line"><span class="w"> </span><span class="n">BLOBMSG_TYPE_INT32</span><span class="p">,</span></span>
<span class="code-line"><span class="w"> </span><span class="n">BLOBMSG_TYPE_INT16</span><span class="p">,</span></span>
<span class="code-line"><span class="w"> </span><span class="n">BLOBMSG_TYPE_INT8</span><span class="p">,</span></span>
<span class="code-line"><span class="w"> </span><span class="n">BLOBMSG_TYPE_BOOL</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">BLOBMSG_TYPE_INT8</span><span class="p">,</span></span>
<span class="code-line"><span class="w"> </span><span class="n">BLOBMSG_TYPE_DOUBLE</span><span class="p">,</span></span>
<span class="code-line"><span class="w"> </span><span class="n">__BLOBMSG_TYPE_LAST</span><span class="p">,</span></span>
<span class="code-line"><span class="w"> </span><span class="n">BLOBMSG_TYPE_LAST</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">__BLOBMSG_TYPE_LAST</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span></span>
<span class="code-line"><span class="w"> </span><span class="n">BLOBMSG_CAST_INT64</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">__BLOBMSG_TYPE_LAST</span><span class="p">,</span></span>
<span class="code-line"><span class="p">}</span></span>
<span class="code-line"></span></pre></div>
<p>Now let's make the ubus daemon aware of our object:</p>
<div class="highlight"><pre><span class="code-line"><span></span><code><span class="kt">int</span><span class="w"> </span><span class="nf">main</span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">argc</span><span class="p">,</span><span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">**</span><span class="n">argv</span><span class="p">)</span><span class="w"> </span><span class="p">{</span></code></span>
<span class="code-line"><span class="w"> </span><span class="c1">// initialize the ubox library</span></span>
<span class="code-line"><span class="w"> </span><span class="n">uloop_init</span><span class="p">();</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="w"> </span><span class="c1">// connect to the ubus socket</span></span>
<span class="code-line"><span class="w"> </span><span class="c1">// the parameter may be a path to the ubus socket</span></span>
<span class="code-line"><span class="w"> </span><span class="n">ctx</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">ubus_connect</span><span class="p">(</span><span class="nb">NULL</span><span class="p">);</span></span>
<span class="code-line"><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="o">!</span><span class="n">ctx</span><span class="p">)</span><span class="w"> </span><span class="p">{</span></span>
<span class="code-line"><span class="w"> </span><span class="n">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span><span class="w"> </span><span class="s">"Failed to connect to ubus</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span></span>
<span class="code-line"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="mi">-1</span><span class="p">;</span></span>
<span class="code-line"><span class="w"> </span><span class="p">}</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="w"> </span><span class="n">ubus_add_uloop</span><span class="p">(</span><span class="n">ctx</span><span class="p">);</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="w"> </span><span class="c1">// add our object to the ubus object space</span></span>
<span class="code-line"><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">ret</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">ubus_add_object</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span><span class="w"> </span><span class="o">&</span><span class="n">test_object</span><span class="p">);</span></span>
<span class="code-line"><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">ret</span><span class="p">)</span><span class="w"> </span><span class="p">{</span></span>
<span class="code-line"><span class="w"> </span><span class="n">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span><span class="w"> </span><span class="s">"Failed to add object: %s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span><span class="w"> </span><span class="n">ubus_strerror</span><span class="p">(</span><span class="n">ret</span><span class="p">));</span></span>
<span class="code-line"><span class="w"> </span><span class="p">}</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="w"> </span><span class="c1">// wait in the uloop event queue</span></span>
<span class="code-line"><span class="w"> </span><span class="n">uloop_run</span><span class="p">();</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="w"> </span><span class="c1">// deinit ubus and clean up</span></span>
<span class="code-line"><span class="w"> </span><span class="n">ubus_free</span><span class="p">(</span><span class="n">ctx</span><span class="p">);</span></span>
<span class="code-line"><span class="w"> </span><span class="n">uloop_done</span><span class="p">();</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span></span>
<span class="code-line"><span class="p">}</span></span>
<span class="code-line"></span></pre></div>
<p>So now, when we build and run our code, we will see no output of our program, but we will see that there is a new object "test" on the ubus and we will see, that it has two methods.</p>
<div class="highlight"><pre><span class="code-line"><span></span><code><span class="c1"># build</span></code></span>
<span class="code-line">gcc<span class="w"> </span>server.c<span class="w"> </span>-lubus<span class="w"> </span>-lubox</span>
<span class="code-line"></span>
<span class="code-line"><span class="c1"># run</span></span>
<span class="code-line">./a.out</span>
<span class="code-line"></span>
<span class="code-line"><span class="c1"># see</span></span>
<span class="code-line">ubus<span class="w"> </span>list</span>
<span class="code-line">ubus<span class="w"> </span>list<span class="w"> </span>-v</span>
<span class="code-line"></span></pre></div>
<h3 id="methods-and-parameters">Methods and Parameters</h3>
<p>We've seen before the prototype of our callback functions. Let's first have a look into how we demarshal parameters out of the ubus objects:</p>
<div class="highlight"><pre><span class="code-line"><span></span><code><span class="c1">// - ctx, obj and req are pointers to internal state of the ubus library</span></code></span>
<span class="code-line"><span class="c1">// we could check which object is wanted, but we'll not gonna bother here</span></span>
<span class="code-line"><span class="c1">// - method is the name of the method called, useful if there is one</span></span>
<span class="code-line"><span class="c1">// callback for more than 1 method</span></span>
<span class="code-line"><span class="c1">// - msg is the payload of the request</span></span>
<span class="code-line"><span class="kt">int</span><span class="w"> </span><span class="nf">sendHello</span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="nc">ubus_context</span><span class="w"> </span><span class="o">*</span><span class="n">ctx</span><span class="p">,</span><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">ubus_object</span><span class="w"> </span><span class="o">*</span><span class="n">obj</span><span class="p">,</span></span>
<span class="code-line"><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">ubus_request_data</span><span class="w"> </span><span class="o">*</span><span class="n">req</span><span class="p">,</span><span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="n">method</span><span class="p">,</span></span>
<span class="code-line"><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">blob_attr</span><span class="w"> </span><span class="o">*</span><span class="n">msg</span><span class="p">)</span><span class="w"> </span><span class="p">{</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="w"> </span><span class="c1">// tb is an array of pointers to blob_attr.</span></span>
<span class="code-line"><span class="w"> </span><span class="c1">// this serves as indices into the message, from which we'll</span></span>
<span class="code-line"><span class="w"> </span><span class="c1">// be able to get our parameters</span></span>
<span class="code-line"><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">blob_attr</span><span class="w"> </span><span class="o">*</span><span class="n">tb</span><span class="p">[</span><span class="n">__HELLO_MAX</span><span class="p">];</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="w"> </span><span class="c1">// parse the message and fill the tb-pointers</span></span>
<span class="code-line"><span class="w"> </span><span class="n">blobmsg_parse</span><span class="p">(</span><span class="n">hello_policy</span><span class="p">,</span><span class="w"> </span><span class="n">ARRAY_SIZE</span><span class="p">(</span><span class="n">hello_policy</span><span class="p">),</span><span class="w"> </span><span class="n">tb</span><span class="p">,</span><span class="w"> </span><span class="n">blob_data</span><span class="p">(</span><span class="n">msg</span><span class="p">),</span><span class="w"> </span><span class="n">blob_len</span><span class="p">(</span><span class="n">msg</span><span class="p">));</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="w"> </span><span class="c1">// if there was a message present, print it.</span></span>
<span class="code-line"><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">tb</span><span class="p">[</span><span class="n">HELLO_MSG</span><span class="p">])</span><span class="w"> </span><span class="p">{</span></span>
<span class="code-line"><span class="w"> </span><span class="n">printf</span><span class="w"> </span><span class="p">(</span><span class="s">"Received %s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span><span class="w"> </span><span class="n">blobmsg_get_string</span><span class="p">(</span><span class="n">tb</span><span class="p">[</span><span class="n">HELLO_MSG</span><span class="p">]));</span></span>
<span class="code-line"><span class="w"> </span><span class="p">}</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">tb</span><span class="p">[</span><span class="n">HELLO_ID</span><span class="p">)</span><span class="w"> </span><span class="p">{</span></span>
<span class="code-line"><span class="w"> </span><span class="n">printf</span><span class="w"> </span><span class="p">(</span><span class="s">"Recieved ID: %s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span><span class="w"> </span><span class="n">blobmsg_get_u32</span><span class="p">(</span><span class="n">tb</span><span class="p">[</span><span class="n">HELLO_ID</span><span class="p">]));</span></span>
<span class="code-line"><span class="w"> </span><span class="p">}</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="w"> </span><span class="c1">// Todo: respond</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span></span>
<span class="code-line"><span class="p">}</span></span>
<span class="code-line"></span></pre></div>
<p>For most simpler requests, it is sufficient to call <code>blobmsg_parse</code> for demarshalling the message into the <code>tb</code> array. Its prototype is:</p>
<div class="highlight"><pre><span class="code-line"><span></span><code><span class="kt">int</span><span class="w"> </span><span class="n">blobmsg_parse</span><span class="p">(</span><span class="k">const</span><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">blobmsg_policy</span><span class="w"> </span><span class="o">*</span><span class="n">policy</span><span class="p">,</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">policy_len</span><span class="p">,</span></code></span>
<span class="code-line"><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">blob_attr</span><span class="w"> </span><span class="o">**</span><span class="n">tb</span><span class="p">,</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="o">*</span><span class="n">data</span><span class="p">,</span><span class="w"> </span><span class="kt">unsigned</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">len</span><span class="p">)</span></span>
<span class="code-line"></span></pre></div>
<p><code>blobmsg_parse</code> takes the policy and its length, so it knows, which values to expect. The pointer to the array <code>tb</code> is used to write indizes into the <code>tb</code> array, so we can access the message parameter as <code>tb[1]</code> or <code>tb[HELLO_MSG]</code>, as the message-entry of the policy is on place 1. The last two parameter is the pointer to the data and its length.</p>
<p>Afterwards we'll have a filled <code>tb</code> array. If an entry is <code>NULL</code>, the parameter was not found, so the client probably did not send it. Trying to access the parameter will crash our application, so we should always check its existence before using and values and have either sane defaults or ones, that imply that this parameter is missing.</p>
<p>After parsing, we can access our data by using <code>blobmsg_get_...</code>. This will return either the value (for scalar values) or a pointer to a string (if it's a string). For string we'd need to copy it, if we'd need it later on, I'm gonna skip that. Not, that there are no <code>blogmsg_get_...</code> variants for tables and arrays.</p>
<h4 id="arrays-and-tables">Arrays and Tables</h4>
<p>The fun really starts with arrays and tables. Arrays are in a sense lists of entries, they may even have differing types inside them. I'll assume same typed entries for the time being. Tables are like structs in C. In a way they are arrays with names entries, which is how they can be used in ubus.</p>
<h5 id="arrays">Arrays</h5>
<p><code>blobmsg_parse</code> will not get us very far here. Let's assume the following policy of a delete call of a list object:</p>
<div class="highlight"><pre><span class="code-line"><span></span><code><span class="k">enum</span><span class="w"> </span><span class="p">{</span></code></span>
<span class="code-line"><span class="w"> </span><span class="n">DELETE_IDS</span><span class="p">,</span></span>
<span class="code-line"><span class="w"> </span><span class="n">__DELETE_MAX</span></span>
<span class="code-line"><span class="p">};</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="k">static</span><span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">blobmsg_policy</span><span class="w"> </span><span class="n">delete_policy</span><span class="p">[]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span></span>
<span class="code-line"><span class="w"> </span><span class="p">[</span><span class="n">DELETE_IDS</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="p">.</span><span class="n">name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">"ids"</span><span class="p">,</span><span class="w"> </span><span class="p">.</span><span class="n">type</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">BLOBMSG_TYPE_ARRAY</span><span class="w"> </span><span class="p">},</span></span>
<span class="code-line"><span class="p">};</span></span>
<span class="code-line"></span></pre></div>
<p>The delete method takes a single parameter, which has the array-type. However, we don't have to tell ubus at this point which types are inside the array, just that it's an array of things.</p>
<p>Now we can use <code>blobmsg_parse</code> to get us the <code>ids</code> parameter, but then we'll still have a <code>blob_attr*</code> pointer to demarshall. Let's say, we'd want to copy out the array for later use.</p>
<div class="highlight"><pre><span class="code-line"><span></span><code><span class="k">static</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="nf">delete_cb</span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="nc">ubus_context</span><span class="w"> </span><span class="o">*</span><span class="n">ctx</span><span class="p">,</span><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">ubus_object</span><span class="w"> </span><span class="o">*</span><span class="n">obj</span><span class="p">,</span></code></span>
<span class="code-line"><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">ubus_request_data</span><span class="w"> </span><span class="o">*</span><span class="n">req</span><span class="p">,</span><span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="n">method</span><span class="p">,</span></span>
<span class="code-line"><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">blob_attr</span><span class="w"> </span><span class="o">*</span><span class="n">msg</span><span class="w"> </span><span class="p">)</span><span class="w"> </span><span class="p">{</span></span>
<span class="code-line"><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">blob_attr</span><span class="w"> </span><span class="o">*</span><span class="n">tb</span><span class="p">[</span><span class="n">__DELETE_MAX</span><span class="p">];</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="w"> </span><span class="n">blobmsg_parse</span><span class="p">(</span><span class="n">delete_policy</span><span class="p">,</span><span class="w"> </span><span class="n">__DELETE_MAX</span><span class="p">,</span><span class="w"> </span><span class="n">tb</span><span class="p">,</span><span class="w"> </span><span class="n">blob_data</span><span class="p">(</span><span class="n">msg</span><span class="p">),</span><span class="w"> </span><span class="n">blob_len</span><span class="p">(</span><span class="n">msg</span><span class="p">));</span></span>
<span class="code-line"><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="n">tb</span><span class="p">[</span><span class="n">DELETE_IDS</span><span class="p">]</span><span class="w"> </span><span class="p">)</span><span class="w"> </span><span class="p">{</span></span>
<span class="code-line"><span class="w"> </span><span class="c1">// get the length of the array</span></span>
<span class="code-line"><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">len</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">blobmsg_check_array</span><span class="p">(</span><span class="n">tb</span><span class="p">[</span><span class="n">DELETE_IDS</span><span class="p">],</span><span class="w"> </span><span class="n">BLOBMSG_TYPE_INT32</span><span class="p">);</span></span>
<span class="code-line"><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="o">*</span><span class="n">arr</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="kt">int</span><span class="o">*</span><span class="p">)</span><span class="w"> </span><span class="n">malloc</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="n">len</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="k">sizeof</span><span class="p">(</span><span class="kt">int</span><span class="p">)</span><span class="w"> </span><span class="p">);</span></span>
<span class="code-line"><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">i</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">blob_attr</span><span class="w"> </span><span class="o">*</span><span class="n">cur</span><span class="p">;</span></span>
<span class="code-line"><span class="w"> </span><span class="kt">size_t</span><span class="w"> </span><span class="n">rem</span><span class="p">;</span></span>
<span class="code-line"><span class="w"> </span><span class="c1">// this macro sets up a for loop over all entries in the array</span></span>
<span class="code-line"><span class="w"> </span><span class="c1">// cur is always the current object</span></span>
<span class="code-line"><span class="w"> </span><span class="c1">// tb[...] is the array to be iterated over</span></span>
<span class="code-line"><span class="w"> </span><span class="c1">// and rem is an int for the remainder</span></span>
<span class="code-line"><span class="w"> </span><span class="n">blobmsg_for_each_attr</span><span class="p">(</span><span class="n">cur</span><span class="p">,</span><span class="w"> </span><span class="n">tb</span><span class="p">[</span><span class="n">DELETE_IDS</span><span class="p">],</span><span class="w"> </span><span class="n">rem</span><span class="p">)</span><span class="w"> </span><span class="p">{</span></span>
<span class="code-line"><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">blobmsg_type</span><span class="p">(</span><span class="n">cur</span><span class="p">)</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="n">BLOBMSG_TYPE_INT32</span><span class="p">)</span><span class="w"> </span><span class="p">{</span></span>
<span class="code-line"><span class="w"> </span><span class="k">continue</span><span class="p">;</span></span>
<span class="code-line"><span class="w"> </span><span class="p">}</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="w"> </span><span class="n">arr</span><span class="p">[</span><span class="n">i</span><span class="o">++</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">blobmsg_get_u32</span><span class="p">(</span><span class="w"> </span><span class="n">cur</span><span class="w"> </span><span class="p">);</span></span>
<span class="code-line"><span class="w"> </span><span class="c1">// do stuff</span></span>
<span class="code-line"><span class="w"> </span><span class="p">}</span></span>
<span class="code-line"><span class="w"> </span><span class="p">}</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="w"> </span><span class="c1">// respond</span></span>
<span class="code-line"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span></span>
<span class="code-line"><span class="p">}</span></span>
<span class="code-line"></span></pre></div>
<p>So we need to get the length of the array. You could iterate over the list two times, once for checking the size and the second one for copying out the values. This is probably what you need to do when the array has differing types in it.</p>
<p>The foreach-macro is a bit on the kernel-level C coding style, which is why I will not copy it in here. You can look it up and try to understand it, I will not.</p>
<h5 id="tables">Tables</h5>
<p>Tables are in a sense the same as arrays, but its values have names. So lets assume, we'd want to add to a key-value store with a typed value (i.e. the parameters are a name, a type-reference and a value).</p>
<p>We would initialize our policy like this, and maybe create a struct for future reference in our key-value store program:</p>
<div class="highlight"><pre><span class="code-line"><span></span><code><span class="k">struct</span><span class="w"> </span><span class="nc">kv_table</span><span class="w"> </span><span class="p">{</span></code></span>
<span class="code-line"><span class="w"> </span><span class="kt">char</span><span class="o">*</span><span class="w"> </span><span class="n">key</span><span class="p">;</span></span>
<span class="code-line"><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">type</span><span class="p">;</span></span>
<span class="code-line"><span class="w"> </span><span class="k">union</span><span class="w"> </span><span class="p">{</span></span>
<span class="code-line"><span class="w"> </span><span class="kt">void</span><span class="o">*</span><span class="w"> </span><span class="n">value</span><span class="p">;</span></span>
<span class="code-line"><span class="w"> </span><span class="kt">uint32_t</span><span class="w"> </span><span class="n">int_val</span><span class="p">;</span></span>
<span class="code-line"><span class="w"> </span><span class="kt">bool</span><span class="w"> </span><span class="n">bool_val</span><span class="p">;</span></span>
<span class="code-line"><span class="w"> </span><span class="c1">// ...</span></span>
<span class="code-line"><span class="w"> </span><span class="p">};</span></span>
<span class="code-line"><span class="p">};</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="k">enum</span><span class="w"> </span><span class="p">{</span></span>
<span class="code-line"><span class="w"> </span><span class="n">APPEND_TABLE</span><span class="p">,</span></span>
<span class="code-line"><span class="w"> </span><span class="n">__APPEND_MAX</span></span>
<span class="code-line"><span class="p">};</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="k">static</span><span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">blobmsg_policy</span><span class="w"> </span><span class="n">append_policy</span><span class="p">[]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span></span>
<span class="code-line"><span class="w"> </span><span class="p">[</span><span class="n">APPEND_TABLE</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="p">.</span><span class="n">name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">"table"</span><span class="p">,</span><span class="w"> </span><span class="p">.</span><span class="n">type</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">BLOBMSG_TYPE_TABLE</span><span class="w"> </span><span class="p">},</span></span>
<span class="code-line"><span class="p">};</span></span>
<span class="code-line"></span></pre></div>
<p>So then let's take a look into parsing a table. This is a bit boring if the only thing you get passed is a table, but think about nesting table-structures into an array. Then you cannot just add your table names to the policy, but have to iterate over the array and then the table.</p>
<div class="highlight"><pre><span class="code-line"><span></span><code><span class="k">static</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="nf">append_cb</span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="nc">ubus_context</span><span class="w"> </span><span class="o">*</span><span class="n">ctx</span><span class="p">,</span><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">ubus_object</span><span class="w"> </span><span class="o">*</span><span class="n">obj</span><span class="p">,</span></code></span>
<span class="code-line"><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">ubus_request_data</span><span class="w"> </span><span class="o">*</span><span class="n">req</span><span class="p">,</span><span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="n">method</span><span class="p">,</span></span>
<span class="code-line"><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">blob_attr</span><span class="w"> </span><span class="o">*</span><span class="n">msg</span><span class="w"> </span><span class="p">)</span><span class="w"> </span><span class="p">{</span></span>
<span class="code-line"><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">blob_attr</span><span class="w"> </span><span class="o">*</span><span class="n">tb</span><span class="p">[</span><span class="n">__APPEND_MAX</span><span class="p">];</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="w"> </span><span class="n">blobmsg_parse</span><span class="p">(</span><span class="n">append_policy</span><span class="p">,</span><span class="w"> </span><span class="n">__APPPEND_MAX</span><span class="p">,</span><span class="w"> </span><span class="n">tb</span><span class="p">,</span><span class="w"> </span><span class="n">blob_data</span><span class="p">(</span><span class="n">msg</span><span class="p">),</span><span class="w"> </span><span class="n">blob_len</span><span class="p">(</span><span class="n">msg</span><span class="p">));</span></span>
<span class="code-line"><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="n">tb</span><span class="p">[</span><span class="n">APPEND_TABLE</span><span class="p">]</span><span class="w"> </span><span class="p">)</span><span class="w"> </span><span class="p">{</span></span>
<span class="code-line"><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">kv_table</span><span class="w"> </span><span class="n">payload</span><span class="p">;</span></span>
<span class="code-line"><span class="w"> </span><span class="n">payload</span><span class="p">.</span><span class="n">type</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">-1</span><span class="p">;</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">blob_attr</span><span class="w"> </span><span class="o">*</span><span class="n">cur</span><span class="p">;</span></span>
<span class="code-line"><span class="w"> </span><span class="kt">size_t</span><span class="w"> </span><span class="n">rem</span><span class="p">;</span></span>
<span class="code-line"><span class="w"> </span><span class="c1">// as above we'll iterate over the entries of the table</span></span>
<span class="code-line"><span class="w"> </span><span class="n">blobmsg_for_each_attr</span><span class="p">(</span><span class="n">cur</span><span class="p">,</span><span class="w"> </span><span class="n">tb</span><span class="p">[</span><span class="n">APPEND_TABLE</span><span class="p">],</span><span class="w"> </span><span class="n">rem</span><span class="p">)</span><span class="w"> </span><span class="p">{</span></span>
<span class="code-line"><span class="w"> </span><span class="kt">char</span><span class="o">*</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">blobmsg_name</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="n">cur</span><span class="w"> </span><span class="p">);</span></span>
<span class="code-line"><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="n">strcmp</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="s">"name"</span><span class="p">,</span><span class="w"> </span><span class="n">key</span><span class="w"> </span><span class="p">)</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="p">)</span><span class="w"> </span><span class="p">{</span></span>
<span class="code-line"><span class="w"> </span><span class="c1">// you could (should) check the type of the entry here</span></span>
<span class="code-line"><span class="w"> </span><span class="n">payload</span><span class="p">.</span><span class="n">name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">blobmsg_get_string</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="n">cur</span><span class="w"> </span><span class="p">);</span></span>
<span class="code-line"><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="n">strcmp</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="s">"type"</span><span class="p">,</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="p">)</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="p">)</span><span class="w"> </span><span class="p">{</span></span>
<span class="code-line"><span class="w"> </span><span class="n">payload</span><span class="p">.</span><span class="n">type</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">blobmsg_get_u32</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="n">cur</span><span class="w"> </span><span class="p">);</span></span>
<span class="code-line"><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="n">strcmp</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="s">"value"</span><span class="p">,</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="p">)</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="p">)</span><span class="w"> </span><span class="p">{</span></span>
<span class="code-line"><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="n">payload</span><span class="p">.</span><span class="n">type</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mi">-1</span><span class="w"> </span><span class="p">)</span><span class="w"> </span><span class="p">{</span></span>
<span class="code-line"><span class="w"> </span><span class="c1">// check blobmsg_type</span></span>
<span class="code-line"><span class="w"> </span><span class="c1">// ...</span></span>
<span class="code-line"><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span></span>
<span class="code-line"><span class="w"> </span><span class="k">switch</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="n">payload</span><span class="p">.</span><span class="n">type</span><span class="w"> </span><span class="p">)</span><span class="w"> </span><span class="p">{</span></span>
<span class="code-line"><span class="w"> </span><span class="k">case</span><span class="w"> </span><span class="mi">1</span><span class="p">:</span></span>
<span class="code-line"><span class="w"> </span><span class="n">payload</span><span class="p">.</span><span class="n">bool_val</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">blobmsg_get_bool</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="n">cur</span><span class="w"> </span><span class="p">);</span></span>
<span class="code-line"><span class="w"> </span><span class="k">break</span></span>
<span class="code-line"><span class="w"> </span><span class="c1">// ...</span></span>
<span class="code-line"><span class="w"> </span><span class="p">}</span></span>
<span class="code-line"><span class="w"> </span><span class="p">}</span></span>
<span class="code-line"><span class="w"> </span><span class="p">}</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="w"> </span><span class="c1">// do stuff</span></span>
<span class="code-line"><span class="w"> </span><span class="p">}</span></span>
<span class="code-line"><span class="w"> </span><span class="p">}</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="w"> </span><span class="c1">// respond</span></span>
<span class="code-line"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span></span>
<span class="code-line"><span class="p">}</span></span>
<span class="code-line"></span></pre></div>
<h3 id="sending-responses_2">Sending Responses</h3>
<p>Other than the parameters, there is no policy needed on what to return. For replying to requests we need the call <code>ubus_send_reply</code>:</p>
<div class="highlight"><pre><span class="code-line"><span></span><code><span class="kt">int</span><span class="w"> </span><span class="nf">ubus_send_reply</span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="nc">ubus_context</span><span class="w"> </span><span class="o">*</span><span class="n">ctx</span><span class="p">,</span><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">ubus_request_data</span><span class="w"> </span><span class="o">*</span><span class="n">req</span><span class="p">,</span></code></span>
<span class="code-line"><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">blob_attr</span><span class="w"> </span><span class="o">*</span><span class="n">msg</span><span class="p">);</span></span>
<span class="code-line"></span></pre></div>
<p>We need to give it the ubus context and the original request and a <code>blob_attr</code> message to send. Now let's go back to the "hello" method from above, which should just return "hello world".</p>
<p>First we need to initialize a <code>struct blob_buf</code>. We can have that either static in our C-file or on the stack inside the function. After initialization, we can add values to the buffer, here we just add an attribute "message" and send it away with the <code>ubus_send_reply</code> call. We give that the <code>head</code> pointer to the list of parameters it created.</p>
<div class="highlight"><pre><span class="code-line"><span></span><code><span class="k">struct</span><span class="w"> </span><span class="nc">blob_buf</span><span class="w"> </span><span class="n">b</span><span class="p">;</span></code></span>
<span class="code-line"><span class="n">blob_buf_init</span><span class="p">(</span><span class="o">&</span><span class="n">b</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="p">);</span></span>
<span class="code-line"><span class="n">blobmsg_add_string</span><span class="p">(</span><span class="o">&</span><span class="n">b</span><span class="p">,</span><span class="w"> </span><span class="s">"message"</span><span class="p">,</span><span class="w"> </span><span class="s">"Hello World"</span><span class="p">);</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="k">return</span><span class="w"> </span><span class="n">ubus_send_reply</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span><span class="w"> </span><span class="n">req</span><span class="p">,</span><span class="w"> </span><span class="n">buf</span><span class="p">.</span><span class="n">head</span><span class="p">);</span></span>
<span class="code-line"></span></pre></div>
<h3 id="nested-responsed-arrays-and-tables">Nested Responsed: Arrays and Tables</h3>
<p>Sending arrays and tables behaves quite the same, but adds nesting. We will open an attribute of our buffer and add entries to it, either names entries (table) or entries without a name (array). Nesting needs a cookie, which is returned on opening and will be needed for closing, as the nesting operations change the internal state of our buffer-structure and we want to reset it after we're done.</p>
<div class="highlight"><pre><span class="code-line"><span></span><code><span class="c1">// as a reference: the _array and the _table variants just call</span></code></span>
<span class="code-line"><span class="c1">// _nested with a single bool value changed:</span></span>
<span class="code-line"><span class="kt">void</span><span class="w"> </span><span class="o">*</span><span class="nf">blobmsg_open_nested</span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="nc">blob_buf</span><span class="w"> </span><span class="o">*</span><span class="n">buf</span><span class="p">,</span><span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="n">name</span><span class="p">,</span><span class="w"> </span><span class="kt">bool</span><span class="w"> </span><span class="n">array</span><span class="p">);</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="k">static</span><span class="w"> </span><span class="kr">inline</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="o">*</span></span>
<span class="code-line"><span class="nf">blobmsg_open_array</span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="nc">blob_buf</span><span class="w"> </span><span class="o">*</span><span class="n">buf</span><span class="p">,</span><span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="n">name</span><span class="p">)</span></span>
<span class="code-line"><span class="p">{</span></span>
<span class="code-line"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">blobmsg_open_nested</span><span class="p">(</span><span class="n">buf</span><span class="p">,</span><span class="w"> </span><span class="n">name</span><span class="p">,</span><span class="w"> </span><span class="nb">true</span><span class="p">);</span></span>
<span class="code-line"><span class="p">}</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="k">static</span><span class="w"> </span><span class="kr">inline</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="o">*</span></span>
<span class="code-line"><span class="nf">blobmsg_open_table</span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="nc">blob_buf</span><span class="w"> </span><span class="o">*</span><span class="n">buf</span><span class="p">,</span><span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="n">name</span><span class="p">)</span></span>
<span class="code-line"><span class="p">{</span></span>
<span class="code-line"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">blobmsg_open_nested</span><span class="p">(</span><span class="n">buf</span><span class="p">,</span><span class="w"> </span><span class="n">name</span><span class="p">,</span><span class="w"> </span><span class="nb">false</span><span class="p">);</span></span>
<span class="code-line"><span class="p">}</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="c1">// our code:</span></span>
<span class="code-line"><span class="kt">void</span><span class="o">*</span><span class="w"> </span><span class="n">cookie</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">blobmsg_open_array</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="o">&</span><span class="n">buf</span><span class="p">,</span><span class="w"> </span><span class="s">"ids"</span><span class="w"> </span><span class="p">);</span></span>
<span class="code-line"><span class="w"> </span><span class="n">blobmsg_add_u32</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="o">&</span><span class="n">buf</span><span class="p">,</span><span class="w"> </span><span class="nb">NULL</span><span class="p">,</span><span class="w"> </span><span class="mi">12</span><span class="w"> </span><span class="p">);</span></span>
<span class="code-line"><span class="w"> </span><span class="n">blobmsg_add_u32</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="o">&</span><span class="n">buf</span><span class="p">,</span><span class="w"> </span><span class="nb">NULL</span><span class="p">,</span><span class="w"> </span><span class="mi">13</span><span class="w"> </span><span class="p">);</span></span>
<span class="code-line"><span class="w"> </span><span class="n">blobmsg_add_u32</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="o">&</span><span class="n">buf</span><span class="p">,</span><span class="w"> </span><span class="nb">NULL</span><span class="p">,</span><span class="w"> </span><span class="mi">14</span><span class="w"> </span><span class="p">);</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="n">blobmsg_close_array</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="o">&</span><span class="n">buf</span><span class="p">,</span><span class="w"> </span><span class="n">cookie</span><span class="w"> </span><span class="p">);</span></span>
<span class="code-line"></span></pre></div>
<p>When working with tables, the NULL-parameters are used as names for the fields of the table.</p>
<h2 id="client-sending-requests_1">Client: Sending Requests</h2>
<p>Until now we've only looked at the server code. However sending requests is equally important. The <code>ubus</code> tool only gets us so far. First we need to find the object we want to use.</p>
<div class="highlight"><pre><span class="code-line"><span></span><code><span class="kt">int</span><span class="w"> </span><span class="n">rc</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">ubus_lookup_id</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="n">ctx</span><span class="p">,</span><span class="w"> </span><span class="s">"test"</span><span class="p">,</span><span class="w"> </span><span class="o">&</span><span class="n">id</span><span class="w"> </span><span class="p">);</span></code></span>
<span class="code-line"><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="n">rc</span><span class="w"> </span><span class="p">)</span><span class="w"> </span><span class="p">{</span></span>
<span class="code-line"><span class="w"> </span><span class="n">printf</span><span class="w"> </span><span class="p">(</span><span class="s">"Could not find object test</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span></span>
<span class="code-line"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">rc</span></span>
<span class="code-line"><span class="p">}</span></span>
<span class="code-line"></span></pre></div>
<p><code>ubus_lookup_id</code> takes the name of the object we want to have and returns then id of that object. This is an ubus-internal id, we'll be using it to refer to this object, when we call methods of that object.</p>
<p>For calling methods we need either <code>ubus_invoke</code> or <code>ubus_invoke_async</code>. I will tell you a little secret: the synchronous <code>ubus_invoke</code> uses the asynchronous <code>ubus_invoke_async</code> internally and (busy) waits for the response. No matter which one we'll use, we will need a complete-callback, which is called, when the response was received. This is the only place, we will be able to see the response-data.</p>
<div class="highlight"><pre><span class="code-line"><span></span><code><span class="c1">// ubus_data_handler_t, type of the callback</span></code></span>
<span class="code-line"><span class="k">typedef</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="p">(</span><span class="o">*</span><span class="n">ubus_data_handler_t</span><span class="p">)(</span><span class="k">struct</span><span class="w"> </span><span class="nc">ubus_request</span><span class="w"> </span><span class="o">*</span><span class="n">req</span><span class="p">,</span></span>
<span class="code-line"><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">type</span><span class="p">,</span><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">blob_attr</span><span class="w"> </span><span class="o">*</span><span class="n">msg</span><span class="p">);</span></span>
<span class="code-line"></span>
<span class="code-line"></span>
<span class="code-line"><span class="c1">// prototype of ubus_invoke</span></span>
<span class="code-line"><span class="kt">int</span><span class="w"> </span><span class="nf">ubus_invoke</span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="nc">ubus_context</span><span class="w"> </span><span class="o">*</span><span class="n">ctx</span><span class="p">,</span><span class="w"> </span><span class="kt">uint32_t</span><span class="w"> </span><span class="n">obj</span><span class="p">,</span><span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="n">method</span><span class="p">,</span></span>
<span class="code-line"><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">blob_attr</span><span class="w"> </span><span class="o">*</span><span class="n">msg</span><span class="p">,</span><span class="w"> </span><span class="n">ubus_data_handler_t</span><span class="w"> </span><span class="n">cb</span><span class="p">,</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="o">*</span><span class="n">priv</span><span class="p">,</span></span>
<span class="code-line"><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">timeout</span><span class="p">);</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="c1">// usage:</span></span>
<span class="code-line"><span class="n">ubus_invoke</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="n">ctx</span><span class="p">,</span><span class="w"> </span><span class="n">id</span><span class="p">,</span><span class="w"> </span><span class="s">"hello"</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">.</span><span class="n">head</span><span class="p">,</span><span class="w"> </span><span class="n">myCallback</span><span class="p">,</span><span class="w"> </span><span class="nb">NULL</span><span class="p">,</span><span class="w"> </span><span class="mi">3000</span><span class="w"> </span><span class="p">);</span></span>
<span class="code-line"></span></pre></div>
<p><code>ubus_invoke</code> will return, when the response is received and the transaction complete or the timeout (3000ms) is hit. The return value will indicate whether or not the transaction was successful. In the example call, <code>b</code> is again a buffer we filled with values, and NULL is a pointer to private data. This data will be handed right to the complete-callback, so it may be used to hand it memory to write the received response to.</p>
<p>The callback could look like this:</p>
<div class="highlight"><pre><span class="code-line"><span></span><code><span class="k">enum</span><span class="w"> </span><span class="p">{</span></code></span>
<span class="code-line"><span class="w"> </span><span class="n">HELLO_RESP_MSG</span><span class="p">,</span></span>
<span class="code-line"><span class="w"> </span><span class="n">__HELLO_RESP_MAX</span></span>
<span class="code-line"><span class="p">};</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="k">static</span><span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">blobmsg_policy</span><span class="w"> </span><span class="n">hello_response_policy</span><span class="p">[]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span></span>
<span class="code-line"><span class="w"> </span><span class="p">[</span><span class="n">HELLO_RESP_MSG</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="p">.</span><span class="n">name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">"message"</span><span class="p">,</span><span class="w"> </span><span class="p">.</span><span class="n">type</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">BLOBMSG_TYPE_STRING</span><span class="w"> </span><span class="p">},</span></span>
<span class="code-line"><span class="p">};</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="kt">void</span><span class="w"> </span><span class="nf">myCallback</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">ubus_request</span><span class="w"> </span><span class="o">*</span><span class="n">req</span><span class="p">,</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">type</span><span class="p">,</span><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">blob_attr</span><span class="w"> </span><span class="o">*</span><span class="n">msg</span><span class="w"> </span><span class="p">)</span><span class="w"> </span><span class="p">{</span></span>
<span class="code-line"><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">blob_attr</span><span class="w"> </span><span class="o">*</span><span class="n">tb</span><span class="p">[</span><span class="n">__HELLO_RESP_MAX</span><span class="p">];</span></span>
<span class="code-line"><span class="w"> </span><span class="c1">// void* priv = req->priv;</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="w"> </span><span class="n">blobmsg_parse</span><span class="p">(</span><span class="n">hello_response_policy</span><span class="p">,</span><span class="w"> </span><span class="n">__HELLO_RESP_MAX</span><span class="p">,</span><span class="w"> </span><span class="n">tb</span><span class="p">,</span><span class="w"> </span><span class="n">blob_data</span><span class="p">(</span><span class="n">msg</span><span class="p">),</span><span class="w"> </span><span class="n">blob_len</span><span class="p">(</span><span class="n">msg</span><span class="p">));</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="n">tb</span><span class="p">[</span><span class="n">HELLO_RESP_MSG</span><span class="p">]</span><span class="w"> </span><span class="p">)</span><span class="w"> </span><span class="p">{</span></span>
<span class="code-line"><span class="w"> </span><span class="n">printf</span><span class="w"> </span><span class="p">(</span><span class="s">"received: %s</span><span class="se">\n</span><span class="s">, blobmsg_get_string(tb[HELLO_RESP_MSG]);</span></span>
<span class="code-line"><span class="w"> </span><span class="p">}</span></span>
<span class="code-line"><span class="p">}</span></span>
<span class="code-line"></span></pre></div>
<p>Using a policy for return values does make response handling a bit easier. You can of course just handle the parameters yourself. Note, that the private-data pointer is inside of the request-structure.</p>
<p>This is also the point where a code generator would make sense, which generates stubs from a descriptor format, so the boilerplate code is generated, like handling of parameters and return values. This takes some of the flexibility out of it, but makes it easier to use.</p>
<h2 id="events-and-subscriptions">Events and Subscriptions</h2>
<p>Ubus also allows subscribing to objects and sending events. We can subscribe to any object, even ones without a a name (for example when a call returns an object id). As a subscriber, we need a <code>ubus_subscriber</code> callback, which contains a callback for every event and a callback for when the object was removed. When subscribing we implicitely create a new object, to which all events are sent.</p>
<div class="highlight"><pre><span class="code-line"><span></span><code><span class="k">typedef</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="p">(</span><span class="o">*</span><span class="n">ubus_handler_t</span><span class="p">)(</span><span class="k">struct</span><span class="w"> </span><span class="nc">ubus_context</span><span class="w"> </span><span class="o">*</span><span class="n">ctx</span><span class="p">,</span><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">ubus_object</span><span class="w"> </span><span class="o">*</span><span class="n">obj</span><span class="p">,</span></code></span>
<span class="code-line"><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">ubus_request_data</span><span class="w"> </span><span class="o">*</span><span class="n">req</span><span class="p">,</span></span>
<span class="code-line"><span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="n">method</span><span class="p">,</span><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">blob_attr</span><span class="w"> </span><span class="o">*</span><span class="n">msg</span><span class="p">);</span></span>
<span class="code-line"><span class="k">typedef</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="p">(</span><span class="o">*</span><span class="n">ubus_remove_handler_t</span><span class="p">)(</span><span class="k">struct</span><span class="w"> </span><span class="nc">ubus_context</span><span class="w"> </span><span class="o">*</span><span class="n">ctx</span><span class="p">,</span></span>
<span class="code-line"><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">ubus_subscriber</span><span class="w"> </span><span class="o">*</span><span class="n">obj</span><span class="p">,</span><span class="w"> </span><span class="kt">uint32_t</span><span class="w"> </span><span class="n">id</span><span class="p">);</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="k">struct</span><span class="w"> </span><span class="nc">ubus_subscriber</span><span class="w"> </span><span class="p">{</span></span>
<span class="code-line"><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">ubus_object</span><span class="w"> </span><span class="n">obj</span><span class="p">;</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="w"> </span><span class="n">ubus_handler_t</span><span class="w"> </span><span class="n">cb</span><span class="p">;</span></span>
<span class="code-line"><span class="w"> </span><span class="n">ubus_remove_handler_t</span><span class="w"> </span><span class="n">remove_cb</span><span class="p">;</span></span>
<span class="code-line"><span class="p">};</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="kt">int</span><span class="w"> </span><span class="nf">ubus_subscribe</span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="nc">ubus_context</span><span class="w"> </span><span class="o">*</span><span class="n">ctx</span><span class="p">,</span><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">ubus_subscriber</span><span class="w"> </span><span class="o">*</span><span class="n">obj</span><span class="p">,</span><span class="w"> </span><span class="kt">uint32_t</span><span class="w"> </span><span class="n">id</span><span class="p">);</span></span>
<span class="code-line"></span></pre></div>
<p>When we get a remove-callback, we can assume, that the object is gone at will not send any events anymore. Whenever we get an event until then, the <code>cb</code>-callback will be called, again allowing us to parse the messages. The sender can notify us under different names (or methods), which can be used to identify certain states or statechanges.</p>
<p>On the sender-side, first we'll add a subscribe-callback, which is called when a subscriber appears or disappears:</p>
<div class="highlight"><pre><span class="code-line"><span></span><code><span class="kt">void</span><span class="w"> </span><span class="nf">subscriber</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">ubus_context</span><span class="w"> </span><span class="o">*</span><span class="n">ctx</span><span class="p">,</span><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">ubus_object</span><span class="w"> </span><span class="o">*</span><span class="n">obj</span><span class="w"> </span><span class="p">)</span><span class="w"> </span><span class="p">{</span></code></span>
<span class="code-line"><span class="w"> </span><span class="n">printf</span><span class="w"> </span><span class="p">(</span><span class="s">"Subsciber change!</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span></span>
<span class="code-line"><span class="p">}</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="k">static</span><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">ubus_object</span><span class="w"> </span><span class="n">test_object</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span></span>
<span class="code-line"><span class="w"> </span><span class="p">.</span><span class="n">name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">"test"</span><span class="p">,</span></span>
<span class="code-line"><span class="w"> </span><span class="p">.</span><span class="n">type</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="o">&</span><span class="n">test_object_type</span><span class="p">,</span></span>
<span class="code-line"><span class="w"> </span><span class="p">.</span><span class="n">methods</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">test_methods</span><span class="p">,</span></span>
<span class="code-line"><span class="w"> </span><span class="p">.</span><span class="n">n_methods</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">ARRAY_SIZE</span><span class="p">(</span><span class="n">test_methods</span><span class="p">),</span></span>
<span class="code-line"><span class="w"> </span><span class="p">.</span><span class="n">subscribe_cb</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">subscriber</span></span>
<span class="code-line"><span class="p">};</span></span>
<span class="code-line"></span></pre></div>
<p>When an event occurs on the server side, we can notify our subscribers by using <code>ubus_notify</code>.</p>
<div class="highlight"><pre><span class="code-line"><span></span><code><span class="kt">int</span><span class="w"> </span><span class="nf">ubus_notify</span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="nc">ubus_context</span><span class="w"> </span><span class="o">*</span><span class="n">ctx</span><span class="p">,</span><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">ubus_object</span><span class="w"> </span><span class="o">*</span><span class="n">obj</span><span class="p">,</span></code></span>
<span class="code-line"><span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="n">type</span><span class="p">,</span><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">blob_attr</span><span class="w"> </span><span class="o">*</span><span class="n">msg</span><span class="p">,</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">timeout</span><span class="p">);</span></span>
<span class="code-line"></span></pre></div>
<p>Again the <code>msg</code> is payload data like above. <code>type</code> can be used to identify different state changes and will be passed to the subscriber as a method.</p>
<h2 id="timeouts">Timeouts</h2>
<p>As many of the message loop systems do, uloop also provides timeouts. Give <code>uloop_timeout_add</code> a <code>struct uloop_timeout</code> and a timeout, it will execute the appropriate callback function when the timeout has expired.</p>
<div class="highlight"><pre><span class="code-line"><span></span><code><span class="kt">void</span><span class="w"> </span><span class="nf">myTimoutCallback</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">uloop_timeout</span><span class="o">*</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="p">)</span><span class="w"> </span><span class="p">{</span></code></span>
<span class="code-line"><span class="w"> </span><span class="c1">//</span></span>
<span class="code-line"><span class="p">}</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="k">struct</span><span class="w"> </span><span class="nc">uloop_timeout</span><span class="w"> </span><span class="n">myTimeout</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span></span>
<span class="code-line"><span class="w"> </span><span class="p">.</span><span class="n">cb</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">myTimeoutCallback</span></span>
<span class="code-line"><span class="p">};</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="n">uloop_timeout_add</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="o">&</span><span class="n">myTimeout</span><span class="p">,</span><span class="w"> </span><span class="mi">3000</span><span class="w"> </span><span class="p">);</span></span>
<span class="code-line"></span></pre></div>
<p>The callback receives a pointer to the original timeout structure. This is unfortunate, because we cannot add private data to the timeout callback, at least if we don't want to get dirty. If we decide to play dirty, we can use the <code>container_of</code> macro, which every Linux Kernel developer knows so well. libubox has <code>contianer_of</code> itself in the <code>list.h</code>. It is defined as follows:</p>
<div class="highlight"><pre><span class="code-line"><span></span><code><span class="cp">#ifndef container_of</span></code></span>
<span class="code-line"><span class="cp">#define container_of(ptr, type, member) \</span></span>
<span class="code-line"><span class="cp"> ({ \</span></span>
<span class="code-line"><span class="cp"> const __typeof__(((type *) NULL)->member) *__mptr = (ptr); \</span></span>
<span class="code-line"><span class="cp"> (type *) ((char *) __mptr - offsetof(type, member)); \</span></span>
<span class="code-line"><span class="cp"> })</span></span>
<span class="code-line"><span class="cp">#endif</span></span>
<span class="code-line"></span></pre></div>
<p><code>container_of</code> takes a pointer, a type and a member of that type. Let's sway we have a structure like this:</p>
<div class="highlight"><pre><span class="code-line"><span></span><code><span class="k">struct</span><span class="w"> </span><span class="nc">privateData</span><span class="w"> </span><span class="p">{</span></code></span>
<span class="code-line"><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">uloop_timeout</span><span class="w"> </span><span class="n">t</span><span class="p">;</span></span>
<span class="code-line"><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">private</span><span class="p">;</span></span>
<span class="code-line"><span class="p">};</span></span>
<span class="code-line"></span></pre></div>
<p>A <code>uloop_timeout</code> is embedded into our structure. In this special case, it is (most probably, if the compiler likes us) the first member of the structure, so a normal cast would suffice:</p>
<div class="highlight"><pre><span class="code-line"><span></span><code><span class="c1">// do not copy!</span></code></span>
<span class="code-line"><span class="kt">void</span><span class="w"> </span><span class="nf">myTimoutCallback</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">uloop_timeout</span><span class="o">*</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="p">)</span><span class="w"> </span><span class="p">{</span></span>
<span class="code-line"><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">privateData</span><span class="o">*</span><span class="w"> </span><span class="n">priv</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="nc">privateData</span><span class="o">*</span><span class="p">)</span><span class="w"> </span><span class="n">t</span><span class="p">;</span></span>
<span class="code-line"><span class="p">}</span></span>
<span class="code-line"></span></pre></div>
<p>However, if the compiler decides to reorder our structure, we are out of look and just created a bug, which is quite hard to trace. But we can use <code>container_of</code>, which will make the compiler do the offset-calculation. We have a pointer to the embedded <code>uloop_timeout</code>, and want a pointer to the enclosing structure, which is right around the <code>uloop_timeout</code> in memory.</p>
<p>So we tell the compiler the pointer we have, the member inside the enclosing structure, were this pointer points to, and the type of the enclosing structure. Let's alter the structure and see an example:</p>
<div class="highlight"><pre><span class="code-line"><span></span><code><span class="k">struct</span><span class="w"> </span><span class="nc">privateData2</span><span class="w"> </span><span class="p">{</span></code></span>
<span class="code-line"><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">priv2</span><span class="p">;</span></span>
<span class="code-line"><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">uloop_timeout</span><span class="w"> </span><span class="n">t</span><span class="p">;</span></span>
<span class="code-line"><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">private</span><span class="p">;</span></span>
<span class="code-line"><span class="p">};</span></span>
<span class="code-line"></span>
<span class="code-line"></span>
<span class="code-line"><span class="kt">void</span><span class="w"> </span><span class="nf">myTimoutCallback</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">uloop_timeout</span><span class="o">*</span><span class="w"> </span><span class="n">timeout</span><span class="w"> </span><span class="p">)</span><span class="w"> </span><span class="p">{</span></span>
<span class="code-line"><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">privateData</span><span class="o">*</span><span class="w"> </span><span class="n">priv</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">container_of</span><span class="p">(</span><span class="n">timeout</span><span class="p">,</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">privateData2</span><span class="p">);</span></span>
<span class="code-line"><span class="p">}</span></span>
<span class="code-line"></span></pre></div>
<p>Let's see what it does:</p>
<div class="highlight"><pre><span class="code-line"><span></span><code>... [ priv2 | t | private ] ...</code></span>
<span class="code-line"> 0 4 x x+4</span>
<span class="code-line"> / \</span>
<span class="code-line"> |</span>
<span class="code-line"> timeout</span>
<span class="code-line"></span></pre></div>
<p><code>container_of</code> calculates the offset of <code>timeout</code> inside our enclosing structure. We tell it the member to which <code>timeout</code> points, which is t, then calculates the offset of that member inside the struct and substracts that from our original pointer. The problem here is, that we cannot be totally sure, that this structure is <em>always</em> around the timeout. So when using <code>container_of</code> make sure and be absolutely certain, that you <em>always</em> have the embedding structure around the embedded structure. The compiler won't warn you.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Ubus is a (more or less) simple message bus for embedded Linux distributions (like OpenWRT). You can call methods, subscribe and send events and add timeouts. Unfortunately ubus is not designed for multithreaded applications, throwing segementation faults left, right and center when a second thread comes anywhere near ubus-code.</p>
<p>As always with these message-loops you need to call <code>uloop_run</code> repeatedly (or once to that matter), if you want to get new events or messages. If your C-code takes a while, you will not get new messages - of course you don't, you only have one single thread.</p>
<p>A big drawback is, that ubus is essentially written like the Linux Kernel, which sometimes makes it hard to read. Also there is next to no documentation for any of it, apart from the code (at least that I can find). Going through the examples only brings you that far, reading through the tests of libubox gives a bit more insight, but still there is no place, which really explains these things. Unfortunately there is also no (publically available) code generator of stub-code.</p>
<p>But now there is at least some documentation. If you find any problems or mistakes, please let me know!</p>
<h2 id="references">References</h2>
<p>[1] <a href="https://openwrt.org/">https://openwrt.org/</a>
[2] <a href="https://git.openwrt.org/project/ubus.git">https://git.openwrt.org/project/ubus.git</a>
[3] <a href="https://git.openwrt.org/project/libubox.git">https://git.openwrt.org/project/libubox.git</a>
[4] <a href="https://openwrt.org/docs/techref/ubus">https://openwrt.org/docs/techref/ubus</a></p>