2 Copyright (C) 2015-2018 IoT.bzh
4 author: José Bollo <jose.bollo@iot.bzh>
6 Licensed under the Apache License, Version 2.0 (the "License");
7 you may not use this file except in compliance with the License.
8 You may obtain a copy of the License at
10 http://www.apache.org/licenses/LICENSE-2.0
12 Unless required by applicable law or agreed to in writing, software
13 distributed under the License is distributed on an "AS IS" BASIS,
14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 See the License for the specific language governing permissions and
16 limitations under the License.
27 #include <json-c/json.h>
28 #include <systemd/sd-bus.h>
29 #include <systemd/sd-bus-protocol.h>
31 #include "utils-jbus.h"
34 * max depth of json messages
36 #define MAX_JSON_DEPTH 10
39 * errors messages generated by jbus
41 static const char out_of_memory_string[] = "out of memory";
44 * structure for services
47 struct jservice *next; /* link to the next service */
48 char *method; /* method name for the service */
49 void (*oncall_s) (struct sd_bus_message *, const char *, void *);
51 void (*oncall_j) (struct sd_bus_message *, struct json_object *, void *);
53 void *data; /* closure data for the callbacks */
57 * structure for signals
60 struct jsignal *next; /* link to the next signal */
61 char *name; /* name of the expected signal */
62 void (*onsignal_s) (const char *, void *);
64 void (*onsignal_j) (struct json_object *, void *);
66 void *data; /* closure data for the callbacks */
70 * structure for asynchronous requests
74 void (*onresp_s) (int, const char *, void *);
76 void (*onresp_j) (int, struct json_object *, void *);
78 void *data; /* closure data for the callbacks */
82 * structure for handling either client or server jbus on dbus
85 int refcount; /* referenced how many time */
87 struct sd_bus_slot *sservice;
88 struct sd_bus_slot *ssignal;
89 struct json_tokener *tokener; /* string to json tokenizer */
90 struct jservice *services; /* first service */
91 struct jsignal *signals; /* first signal */
92 char *path; /* dbus path */
93 char *name; /* dbus name */
96 /*********************** STATIC COMMON METHODS *****************/
98 static int mkerrno(int rc)
107 * Replies the error "out of memory".
108 * This function is intended to be used in services when an
109 * allocation fails. Thus, it set errno to ENOMEM and
112 static inline int reply_out_of_memory(struct sd_bus_message *smsg)
114 jbus_reply_error_s(smsg, out_of_memory_string);
120 * Parses the json-string 'msg' to create a json object stored
121 * in 'obj'. It uses the tokener of 'jbus'. This is a small
122 * improvement to avoid recreation of tokeners.
124 * Returns 1 in case of success and put the result in *'obj'.
125 * Returns 0 in case of error and put NULL in *'obj'.
127 static int jparse(struct jbus *jbus, const char *msg, struct json_object **obj)
129 json_tokener_reset(jbus->tokener);
130 *obj = json_tokener_parse_ex(jbus->tokener, msg, -1);
131 if (json_tokener_get_error(jbus->tokener) == json_tokener_success)
133 json_object_put(*obj);
138 static int on_service_call(struct sd_bus_message *smsg, struct jbus *jbus, sd_bus_error *error)
140 struct jservice *service;
141 const char *member, *content;
142 struct json_object *obj;
145 if (!sd_bus_message_has_signature(smsg, "s")
146 || sd_bus_message_read_basic(smsg, 's', &content) < 0) {
147 sd_bus_error_set_const(error, "bad signature", "");
152 member = sd_bus_message_get_member(smsg);
153 service = jbus->services;
154 while (service != NULL) {
155 if (!strcmp(service->method, member)) {
156 sd_bus_message_ref(smsg);
157 if (service->oncall_s)
158 service->oncall_s(smsg, content, service->data);
159 else if (service->oncall_j) {
160 if (!jparse(jbus, content, &obj))
161 obj = json_object_new_string(content);
162 service->oncall_j(smsg, obj, service->data);
163 json_object_put(obj);
167 service = service->next;
173 * Adds to 'jbus' a service of name 'method'. The service is
174 * performed by one of the callback 'oncall_s' (for string) or
175 * 'oncall_j' (for json) that will receive the request and the
176 * closure parameter 'data'.
178 * returns 0 in case of success or -1 in case of error (ENOMEM).
180 static int add_service(
183 void (*oncall_s) (struct sd_bus_message *, const char *, void *),
184 void (*oncall_j) (struct sd_bus_message *, struct json_object *, void *),
188 struct jservice *srv;
190 /* connection of the service */
191 if (jbus->sservice == NULL) {
192 rc = sd_bus_add_object(jbus->sdbus, &jbus->sservice, jbus->path, (void*)on_service_call, jbus);
200 srv = malloc(sizeof *srv);
205 srv->method = strdup(method);
211 /* record the service */
212 srv->oncall_s = oncall_s;
213 srv->oncall_j = oncall_j;
215 srv->next = jbus->services;
216 jbus->services = srv;
226 static int on_signal_event(struct sd_bus_message *smsg, struct jbus *jbus, sd_bus_error *error)
228 struct jsignal *signal;
229 const char *member, *content;
230 struct json_object *obj;
233 if (!sd_bus_message_has_signature(smsg, "s")
234 || sd_bus_message_read_basic(smsg, 's', &content) < 0)
238 member = sd_bus_message_get_member(smsg);
239 signal = jbus->signals;
240 while (signal != NULL) {
241 if (!strcmp(signal->name, member)) {
242 if (signal->onsignal_s)
243 signal->onsignal_s(content, signal->data);
244 else if (signal->onsignal_j) {
245 if (!jparse(jbus, content, &obj))
246 obj = json_object_new_string(content);
247 signal->onsignal_j(obj, signal->data);
248 json_object_put(obj);
251 signal = signal->next;
257 * Adds to 'jbus' a handler for the signal of 'name' emmited by
258 * the sender and the interface that 'jbus' is linked to.
259 * The signal is handled by one of the callback 'onsignal_s'
260 * (for string) or 'onsignal_j' (for json) that will receive
261 * parameters associated with the signal and the closure
264 * returns 0 in case of success or -1 in case of error (ENOMEM).
266 static int add_signal(
269 void (*onsignal_s) (const char *, void *),
270 void (*onsignal_j) (struct json_object *, void *),
277 /* connection of the signal */
278 if (jbus->ssignal == NULL) {
279 rc = asprintf(&match, "type='signal',path='%s',interface='%s'", jbus->path, jbus->name);
284 rc = sd_bus_add_match(jbus->sdbus, &jbus->ssignal, match, (void*)on_signal_event, jbus);
293 sig = malloc(sizeof *sig);
298 sig->name = strdup(name);
304 /* record the signal */
305 sig->onsignal_s = onsignal_s;
306 sig->onsignal_j = onsignal_j;
308 sig->next = jbus->signals;
319 static int on_reply(struct sd_bus_message *smsg, struct jrespw *jrespw, sd_bus_error *error)
321 struct json_object *obj;
326 if (!sd_bus_message_has_signature(smsg, "s")
327 || sd_bus_message_read_basic(smsg, 's', &reply) < 0) {
328 sd_bus_error_set_const(error, "bad signature", "");
331 iserror = sd_bus_message_is_method_error(smsg, NULL);
333 /* dispatch string? */
334 if (jrespw->onresp_s != NULL) {
335 jrespw->onresp_s(iserror, reply, jrespw->data);
340 if (!jparse(jrespw->jbus, reply, &obj))
341 obj = json_object_new_string(reply);
342 jrespw->onresp_j(iserror, obj, jrespw->data);
343 json_object_put(obj);
351 * Creates a message for 'method' with one string parameter being 'query'
352 * and sends it to the destination, object and interface linked to 'jbus'.
354 * Adds to 'jbus' the response handler defined by the callbacks 'onresp_s'
355 * (for string) and 'onresp_j' (for json) and the closure parameter 'data'.
357 * Returns 0 in case of success or -1 in case of error.
363 void (*onresp_s) (int, const char *, void *),
364 void (*onresp_j) (int, struct json_object *, void *),
370 /* allocates the response structure */
371 resp = malloc(sizeof *resp);
377 /* fulfill the response structure */
379 resp->onresp_s = onresp_s;
380 resp->onresp_j = onresp_j;
383 rc = sd_bus_call_method_async(jbus->sdbus, NULL, jbus->name, jbus->path, jbus->name, method, (void*)on_reply, resp, "s", query);
397 /********************* MAIN FUNCTIONS *****************************************/
400 * Creates a 'jbus' bound the 'path' and it derived names and linked
401 * either to the DBUS SYSTEM when 'session' is nul or to the DBUS SESSION
402 * if 'session' is not nul.
404 * The parameter 'path' is intended to be the path of a DBUS single object.
405 * Single means that it exists only one instance of the object on the
406 * given bus. That path implies 2 derived DBUS names:
407 * 1. the destination name of the program that handles the object
408 * 2. the interface name of the object
409 * These names are derived by removing the heading slash (/) and
410 * by replacing all occurences of slashes by dots.
411 * For example, passing path = /a/b/c means that the object /a/b/c is
412 * handled by the destination a.b.c and replies to the interface a.b.c
414 * Returns the created jbus or NULL in case of error.
416 struct jbus *create_jbus(struct sd_bus *sdbus, const char *path)
421 /* create the jbus object */
422 jbus = calloc(1, sizeof *jbus);
429 /* create the tokener */
430 jbus->tokener = json_tokener_new_ex(MAX_JSON_DEPTH);
431 if (jbus->tokener == NULL) {
436 /* records the path */
437 jbus->path = strdup(path);
438 if (jbus->path == NULL) {
443 /* makes the name from the path */
446 jbus->name = name = strdup(path);
457 while (name >= jbus->name && *name == '.')
464 /* connect and init */
465 jbus->sdbus = sd_bus_ref(sdbus);
476 * Adds one reference to 'jbus'.
478 void jbus_addref(struct jbus *jbus)
484 * Removes one reference to 'jbus'. Destroys 'jbus' and it related
485 * data if the count of references decrease to zero.
487 void jbus_unref(struct jbus *jbus)
489 struct jservice *srv;
491 if (!--jbus->refcount) {
492 while ((srv = jbus->services) != NULL) {
493 jbus->services = srv->next;
497 while ((sig = jbus->signals) != NULL) {
498 jbus->signals = sig->next;
502 if (jbus->sservice != NULL)
503 sd_bus_slot_unref(jbus->sservice);
504 if (jbus->ssignal != NULL)
505 sd_bus_slot_unref(jbus->ssignal);
506 if (jbus->tokener != NULL)
507 json_tokener_free(jbus->tokener);
508 sd_bus_unref(jbus->sdbus);
516 * Replies an error of string 'error' to the request handled by 'smsg'.
517 * Also destroys the request 'smsg' that must not be used later.
519 * Returns 0 in case of success or -1 in case of error.
521 int jbus_reply_error_s(struct sd_bus_message *smsg, const char *error)
523 int rc = sd_bus_reply_method_errorf(smsg, SD_BUS_ERROR_FAILED, "%s", error);
524 sd_bus_message_unref(smsg);
529 * Replies an error of json 'reply' to the request handled by 'smsg'.
530 * Also destroys the request 'smsg' that must not be used later.
532 * Returns 0 in case of success or -1 in case of error.
534 int jbus_reply_error_j(struct sd_bus_message *smsg, struct json_object *reply)
536 const char *str = json_object_to_json_string(reply);
537 return str ? jbus_reply_error_s(smsg, str) : reply_out_of_memory(smsg);
541 * Replies normally the string 'reply' to the request handled by 'smsg'.
542 * Also destroys the request 'smsg' that must not be used later.
544 * Returns 0 in case of success or -1 in case of error.
546 int jbus_reply_s(struct sd_bus_message *smsg, const char *reply)
548 int rc = sd_bus_reply_method_return(smsg, "s", reply);
549 sd_bus_message_unref(smsg);
554 * Replies normally the json 'reply' to the request handled by 'smsg'.
555 * Also destroys the request 'smsg' that must not be used later.
557 * Returns 0 in case of success or -1 in case of error.
559 int jbus_reply_j(struct sd_bus_message *smsg, struct json_object *reply)
561 const char *str = json_object_to_json_string(reply);
562 return str ? jbus_reply_s(smsg, str) : reply_out_of_memory(smsg);
566 * Sends from 'jbus' the signal of 'name' handling the string 'content'.
568 * Returns 0 in case of success or -1 in case of error.
570 int jbus_send_signal_s(struct jbus *jbus, const char *name, const char *content)
572 return mkerrno(sd_bus_emit_signal(jbus->sdbus, jbus->path, jbus->name, name, "s", content));
576 * Sends from 'jbus' the signal of 'name' handling the json 'content'.
578 * Returns 0 in case of success or -1 in case of error.
580 int jbus_send_signal_j(struct jbus *jbus, const char *name,
581 struct json_object *content)
583 const char *str = json_object_to_json_string(content);
588 return jbus_send_signal_s(jbus, name, str);
592 * Adds to 'jbus' a service handling calls to the 'method' using
593 * the "string" callback 'oncall' and the closure value 'data'.
595 * The callback 'oncall' is invoked for handling incoming method
596 * calls. It receives 3 parameters:
597 * 1. struct sd_bus_message *: a handler to data to be used for replying
598 * 2. const char *: the received string
599 * 3. void *: the closure 'data' set by this function
601 * Returns 0 in case of success or -1 in case of error.
603 int jbus_add_service_s(
606 void (*oncall) (struct sd_bus_message *, const char *, void *),
609 return add_service(jbus, method, oncall, NULL, data);
613 * Adds to 'jbus' a service handling calls to the 'method' using
614 * the "json" callback 'oncall' and the closure value 'data'.
616 * The callback 'oncall' is invoked for handling incoming method
617 * calls. It receives 3 parameters:
618 * 1. struct sd_bus_message *: a handler to data to be used for replying
619 * 2. struct json_object *: the received json
620 * 3. void *: the closure 'data' set by this function
622 * Returns 0 in case of success or -1 in case of error.
624 int jbus_add_service_j(
627 void (*oncall) (struct sd_bus_message *, struct json_object *, void *),
630 return add_service(jbus, method, NULL, oncall, data);
634 * Start to serve: activate services declared for 'jbus'.
635 * This function, in fact, declares 'jbus' as the receiver
636 * for calls to the destination derived from the path set at
638 * It also allows 'jbus' to emit signals of that origin.
640 * Returns 0 in case of success or -1 in case of error.
642 int jbus_start_serving(struct jbus *jbus)
644 return mkerrno(sd_bus_request_name(jbus->sdbus, jbus->name, 0));
648 * Asynchronous call to 'method' of 'jbus' passing the string 'query'.
649 * On response, the function 'onresp' is called with the returned string
650 * value and the closure 'data'.
651 * The function 'onresp' is invoked with 3 parameters:
652 * 1. int: 0 if no error or -1 if error.
653 * 2. const char *: the returned string (might be NULL if error)
654 * 3. void *: the closure 'data'
656 * Returns 0 in case of success or -1 in case of error.
662 void (*onresp) (int, const char *, void *),
665 return call(jbus, method, query, onresp, NULL, data);
669 * Asynchronous call to 'method' of 'jbus' passing the string 'query'.
670 * On response, the function 'onresp' is called with the returned json
671 * value and the closure 'data'.
672 * The function 'onresp' is invoked with 3 parameters:
673 * 1. int: 0 if no error or -1 if error.
674 * 2. const char *: the returned json (might be NULL if error)
675 * 3. void *: the closure 'data'
677 * Returns 0 in case of success or -1 in case of error.
683 void (*onresp) (int, struct json_object *, void *),
686 return call(jbus, method, query, NULL, onresp, data);
690 * Asynchronous call to 'method' of 'jbus' passing the json 'query'.
691 * On response, the function 'onresp' is called with the returned string
692 * value and the closure 'data'.
693 * The function 'onresp' is invoked with 3 parameters:
694 * 1. int: 0 if no error or -1 if error.
695 * 2. const char *: the returned string (might be NULL if error)
696 * 3. void *: the closure 'data'
698 * Returns 0 in case of success or -1 in case of error.
703 struct json_object *query,
704 void (*onresp) (int, const char *, void *),
707 const char *str = json_object_to_json_string(query);
712 return call(jbus, method, str, onresp, NULL, data);
716 * Asynchronous call to 'method' of 'jbus' passing the json 'query'.
717 * On response, the function 'onresp' is called with the returned json
718 * value and the closure 'data'.
719 * The function 'onresp' is invoked with 3 parameters:
720 * 1. int: 0 if no error or -1 if error.
721 * 2. const char *: the returned json (might be NULL if error)
722 * 3. void *: the closure 'data'
724 * Returns 0 in case of success or -1 in case of error.
729 struct json_object *query,
730 void (*onresp) (int, struct json_object *, void *),
733 const char *str = json_object_to_json_string(query);
738 return call(jbus, method, str, NULL, onresp, data);
742 * Synchronous call to 'method' of 'jbus' passing the string 'query'.
743 * The returned string response is returned.
745 * Returns the string response or NULL in case of error.
747 char *jbus_call_ss_sync(
752 sd_bus_message *smsg = NULL;
753 sd_bus_error error = SD_BUS_ERROR_NULL;
758 if (mkerrno(sd_bus_call_method(jbus->sdbus, jbus->name, jbus->path, jbus->name, method, &error, &smsg, "s", query)) < 0)
762 if (sd_bus_message_is_method_error(smsg, NULL))
765 /* check the returned type */
766 if (!sd_bus_message_has_signature(smsg, "s")
767 || sd_bus_message_read_basic(smsg, 's', &reply) < 0)
771 result = strdup(reply);
774 sd_bus_message_unref(smsg);
775 sd_bus_error_free(&error);
780 * Synchronous call to 'method' of 'jbus' passing the string 'query'.
781 * The returned json response is returned.
783 * Returns the json response or NULL in case of error.
785 struct json_object *jbus_call_sj_sync(
790 struct json_object *obj;
791 char *str = jbus_call_ss_sync(jbus, method, query);
795 jparse(jbus, str, &obj);
802 * Synchronous call to 'method' of 'jbus' passing the json 'query'.
803 * The returned string response is returned.
805 * Returns the string response or NULL in case of error.
807 char *jbus_call_js_sync(
810 struct json_object *query)
812 const char *str = json_object_to_json_string(query);
817 return jbus_call_ss_sync(jbus, method, str);
821 * Synchronous call to 'method' of 'jbus' passing the json 'query'.
822 * The returned json response is returned.
824 * Returns the json response or NULL in case of error.
826 struct json_object *jbus_call_jj_sync(
829 struct json_object *query)
831 const char *str = json_object_to_json_string(query);
836 return jbus_call_sj_sync(jbus, method, str);
840 * Records for 'jbus' the string signal handler 'onsig' with closure 'data'
841 * for the signal of 'name'.
842 * The callback handler is called with 2 arguments:
843 * 1. char *: the string parameter associated to the signal
844 * 2. void *: the closure data.
846 * Returns 0 in case of success or -1 otherwise.
848 int jbus_on_signal_s(
851 void (*onsig) (const char *, void *),
854 return add_signal(jbus, name, onsig, NULL, data);
858 * Records for 'jbus' the json signal handler 'onsig' with closure 'data'
859 * for the signal of 'name'.
860 * The callback handler is called with 2 arguments:
861 * 1. struct json_object *: the json parameter associated to the signal
862 * 2. void *: the closure data.
864 * Returns 0 in case of success or -1 otherwise.
866 int jbus_on_signal_j(
869 void (*onsig) (struct json_object *, void *),
872 return add_signal(jbus, name, NULL, onsig, data);
875 /****************** FEW LITTLE TESTS *****************************************/
877 #if defined(SERVER)||defined(CLIENT)
881 static struct sd_bus *msbus()
883 static struct sd_bus *r = NULL;
886 sd_event_default(&e);
887 sd_bus_open_user(&r);
888 sd_bus_attach_event(r, e, 0);
893 static sd_event *events()
895 static sd_event *ev = NULL;
897 ev = sd_bus_get_event(msbus());
901 static int mwait(int timeout, void *closure)
903 sd_event_run(events(), -1);
907 static struct jbus *jbus;
910 void ping(struct sd_bus_message *smsg, struct json_object *request, void *unused)
912 printf("ping(%s) -> %s\n", json_object_to_json_string(request),
913 json_object_to_json_string(request));
914 jbus_reply_j(smsg, request);
915 json_object_put(request);
918 void incr(struct sd_bus_message *smsg, struct json_object *request, void *unused)
920 static int counter = 0;
921 struct json_object *res = json_object_new_int(++counter);
922 printf("incr(%s) -> %s\n", json_object_to_json_string(request),
923 json_object_to_json_string(res));
924 jbus_reply_j(smsg, res);
925 jbus_send_signal_j(jbus, "incremented", res);
926 json_object_put(res);
927 json_object_put(request);
933 jbus = create_jbus(msbus(), "/bzh/iot/jdbus");
934 s1 = jbus_add_service_j(jbus, "ping", ping, NULL);
935 s2 = jbus_add_service_j(jbus, "incr", incr, NULL);
936 s3 = jbus_start_serving(jbus);
937 printf("started %d %d %d\n", s1, s2, s3);
938 while (!mwait(-1,jbus)) ;
944 void onresp(int status, struct json_object *response, void *data)
946 printf("resp: %d, %s, %s\n", status, (char *)data,
947 json_object_to_json_string(response));
948 json_object_put(response);
951 void signaled(const char *content, void *data)
953 printf("signaled with {%s}/%s\n", content, (char*)data);
959 jbus = create_jbus(msbus(), "/bzh/iot/jdbus");
960 jbus_on_signal_s(jbus, "incremented", signaled, "closure-signal");
962 jbus_call_sj(jbus, "ping", "{\"toto\":[1,2,3,4,true,\"toto\"]}",
964 jbus_call_sj(jbus, "incr", "{\"doit\":\"for-me\"}", onresp,
969 jbus_call_ss_sync(jbus, "ping", "\"formidable!\""));
970 while (!mwait(-1,jbus)) ;