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 if (service->oncall_s)
157 service->oncall_s(smsg, content, service->data);
158 else if (service->oncall_j) {
159 if (!jparse(jbus, content, &obj))
160 obj = json_object_new_string(content);
161 service->oncall_j(smsg, obj, service->data);
162 json_object_put(obj);
166 service = service->next;
172 * Adds to 'jbus' a service of name 'method'. The service is
173 * performed by one of the callback 'oncall_s' (for string) or
174 * 'oncall_j' (for json) that will receive the request and the
175 * closure parameter 'data'.
177 * returns 0 in case of success or -1 in case of error (ENOMEM).
179 static int add_service(
182 void (*oncall_s) (struct sd_bus_message *, const char *, void *),
183 void (*oncall_j) (struct sd_bus_message *, struct json_object *, void *),
187 struct jservice *srv;
189 /* connection of the service */
190 if (jbus->sservice == NULL) {
191 rc = sd_bus_add_object(jbus->sdbus, &jbus->sservice, jbus->path, (void*)on_service_call, jbus);
199 srv = malloc(sizeof *srv);
204 srv->method = strdup(method);
210 /* record the service */
211 srv->oncall_s = oncall_s;
212 srv->oncall_j = oncall_j;
214 srv->next = jbus->services;
215 jbus->services = srv;
225 static int on_signal_event(struct sd_bus_message *smsg, struct jbus *jbus, sd_bus_error *error)
227 struct jsignal *signal;
228 const char *member, *content;
229 struct json_object *obj;
232 if (!sd_bus_message_has_signature(smsg, "s")
233 || sd_bus_message_read_basic(smsg, 's', &content) < 0)
237 member = sd_bus_message_get_member(smsg);
238 signal = jbus->signals;
239 while (signal != NULL) {
240 if (!strcmp(signal->name, member)) {
241 if (signal->onsignal_s)
242 signal->onsignal_s(content, signal->data);
243 else if (signal->onsignal_j) {
244 if (!jparse(jbus, content, &obj))
245 obj = json_object_new_string(content);
246 signal->onsignal_j(obj, signal->data);
247 json_object_put(obj);
250 signal = signal->next;
256 * Adds to 'jbus' a handler for the signal of 'name' emmited by
257 * the sender and the interface that 'jbus' is linked to.
258 * The signal is handled by one of the callback 'onsignal_s'
259 * (for string) or 'onsignal_j' (for json) that will receive
260 * parameters associated with the signal and the closure
263 * returns 0 in case of success or -1 in case of error (ENOMEM).
265 static int add_signal(
268 void (*onsignal_s) (const char *, void *),
269 void (*onsignal_j) (struct json_object *, void *),
276 /* connection of the signal */
277 if (jbus->ssignal == NULL) {
278 rc = asprintf(&match, "type='signal',path='%s',interface='%s'", jbus->path, jbus->name);
283 rc = sd_bus_add_match(jbus->sdbus, &jbus->ssignal, match, (void*)on_signal_event, jbus);
292 sig = malloc(sizeof *sig);
297 sig->name = strdup(name);
303 /* record the signal */
304 sig->onsignal_s = onsignal_s;
305 sig->onsignal_j = onsignal_j;
307 sig->next = jbus->signals;
318 static int on_reply(struct sd_bus_message *smsg, struct jrespw *jrespw, sd_bus_error *error)
320 struct json_object *obj;
325 if (!sd_bus_message_has_signature(smsg, "s")
326 || sd_bus_message_read_basic(smsg, 's', &reply) < 0) {
327 sd_bus_error_set_const(error, "bad signature", "");
330 iserror = sd_bus_message_is_method_error(smsg, NULL);
332 /* dispatch string? */
333 if (jrespw->onresp_s != NULL) {
334 jrespw->onresp_s(iserror, reply, jrespw->data);
339 if (!jparse(jrespw->jbus, reply, &obj))
340 obj = json_object_new_string(reply);
341 jrespw->onresp_j(iserror, obj, jrespw->data);
342 json_object_put(obj);
350 * Creates a message for 'method' with one string parameter being 'query'
351 * and sends it to the destination, object and interface linked to 'jbus'.
353 * Adds to 'jbus' the response handler defined by the callbacks 'onresp_s'
354 * (for string) and 'onresp_j' (for json) and the closure parameter 'data'.
356 * Returns 0 in case of success or -1 in case of error.
362 void (*onresp_s) (int, const char *, void *),
363 void (*onresp_j) (int, struct json_object *, void *),
369 /* allocates the response structure */
370 resp = malloc(sizeof *resp);
376 /* fulfill the response structure */
378 resp->onresp_s = onresp_s;
379 resp->onresp_j = onresp_j;
382 rc = sd_bus_call_method_async(jbus->sdbus, NULL, jbus->name, jbus->path, jbus->name, method, (void*)on_reply, resp, "s", query);
396 /********************* MAIN FUNCTIONS *****************************************/
399 * Creates a 'jbus' bound the 'path' and it derived names and linked
400 * either to the DBUS SYSTEM when 'session' is nul or to the DBUS SESSION
401 * if 'session' is not nul.
403 * The parameter 'path' is intended to be the path of a DBUS single object.
404 * Single means that it exists only one instance of the object on the
405 * given bus. That path implies 2 derived DBUS names:
406 * 1. the destination name of the program that handles the object
407 * 2. the interface name of the object
408 * These names are derived by removing the heading slash (/) and
409 * by replacing all occurences of slashes by dots.
410 * For example, passing path = /a/b/c means that the object /a/b/c is
411 * handled by the destination a.b.c and replies to the interface a.b.c
413 * Returns the created jbus or NULL in case of error.
415 struct jbus *create_jbus(struct sd_bus *sdbus, const char *path)
420 /* create the jbus object */
421 jbus = calloc(1, sizeof *jbus);
428 /* create the tokener */
429 jbus->tokener = json_tokener_new_ex(MAX_JSON_DEPTH);
430 if (jbus->tokener == NULL) {
435 /* records the path */
436 jbus->path = strdup(path);
437 if (jbus->path == NULL) {
442 /* makes the name from the path */
445 jbus->name = name = strdup(path);
456 while (name >= jbus->name && *name == '.')
463 /* connect and init */
464 jbus->sdbus = sd_bus_ref(sdbus);
475 * Adds one reference to 'jbus'.
477 void jbus_addref(struct jbus *jbus)
483 * Removes one reference to 'jbus'. Destroys 'jbus' and it related
484 * data if the count of references decrease to zero.
486 void jbus_unref(struct jbus *jbus)
488 struct jservice *srv;
490 if (!--jbus->refcount) {
491 while ((srv = jbus->services) != NULL) {
492 jbus->services = srv->next;
496 while ((sig = jbus->signals) != NULL) {
497 jbus->signals = sig->next;
501 if (jbus->sservice != NULL)
502 sd_bus_slot_unref(jbus->sservice);
503 if (jbus->ssignal != NULL)
504 sd_bus_slot_unref(jbus->ssignal);
505 if (jbus->tokener != NULL)
506 json_tokener_free(jbus->tokener);
507 sd_bus_unref(jbus->sdbus);
515 * Replies an error of string 'error' to the request handled by 'smsg'.
516 * Also destroys the request 'smsg' that must not be used later.
518 * Returns 0 in case of success or -1 in case of error.
520 int jbus_reply_error_s(struct sd_bus_message *smsg, const char *error)
522 return mkerrno(sd_bus_reply_method_errorf(smsg, SD_BUS_ERROR_FAILED, "%s", error));
526 * Replies an error of json 'reply' to the request handled by 'smsg'.
527 * Also destroys the request 'smsg' that must not be used later.
529 * Returns 0 in case of success or -1 in case of error.
531 int jbus_reply_error_j(struct sd_bus_message *smsg, struct json_object *reply)
533 const char *str = json_object_to_json_string(reply);
534 return str ? jbus_reply_error_s(smsg, str) : reply_out_of_memory(smsg);
538 * Replies normally the string 'reply' to the request handled by 'smsg'.
539 * Also destroys the request 'smsg' that must not be used later.
541 * Returns 0 in case of success or -1 in case of error.
543 int jbus_reply_s(struct sd_bus_message *smsg, const char *reply)
545 return mkerrno(sd_bus_reply_method_return(smsg, "s", reply));
549 * Replies normally the json 'reply' to the request handled by 'smsg'.
550 * Also destroys the request 'smsg' that must not be used later.
552 * Returns 0 in case of success or -1 in case of error.
554 int jbus_reply_j(struct sd_bus_message *smsg, struct json_object *reply)
556 const char *str = json_object_to_json_string(reply);
557 return str ? jbus_reply_s(smsg, str) : reply_out_of_memory(smsg);
561 * Sends from 'jbus' the signal of 'name' handling the string 'content'.
563 * Returns 0 in case of success or -1 in case of error.
565 int jbus_send_signal_s(struct jbus *jbus, const char *name, const char *content)
567 return mkerrno(sd_bus_emit_signal(jbus->sdbus, jbus->path, jbus->name, name, "s", content));
571 * Sends from 'jbus' the signal of 'name' handling the json 'content'.
573 * Returns 0 in case of success or -1 in case of error.
575 int jbus_send_signal_j(struct jbus *jbus, const char *name,
576 struct json_object *content)
578 const char *str = json_object_to_json_string(content);
583 return jbus_send_signal_s(jbus, name, str);
587 * Adds to 'jbus' a service handling calls to the 'method' using
588 * the "string" callback 'oncall' and the closure value 'data'.
590 * The callback 'oncall' is invoked for handling incoming method
591 * calls. It receives 3 parameters:
592 * 1. struct sd_bus_message *: a handler to data to be used for replying
593 * 2. const char *: the received string
594 * 3. void *: the closure 'data' set by this function
596 * Returns 0 in case of success or -1 in case of error.
598 int jbus_add_service_s(
601 void (*oncall) (struct sd_bus_message *, const char *, void *),
604 return add_service(jbus, method, oncall, NULL, data);
608 * Adds to 'jbus' a service handling calls to the 'method' using
609 * the "json" callback 'oncall' and the closure value 'data'.
611 * The callback 'oncall' is invoked for handling incoming method
612 * calls. It receives 3 parameters:
613 * 1. struct sd_bus_message *: a handler to data to be used for replying
614 * 2. struct json_object *: the received json
615 * 3. void *: the closure 'data' set by this function
617 * Returns 0 in case of success or -1 in case of error.
619 int jbus_add_service_j(
622 void (*oncall) (struct sd_bus_message *, struct json_object *, void *),
625 return add_service(jbus, method, NULL, oncall, data);
629 * Start to serve: activate services declared for 'jbus'.
630 * This function, in fact, declares 'jbus' as the receiver
631 * for calls to the destination derived from the path set at
633 * It also allows 'jbus' to emit signals of that origin.
635 * Returns 0 in case of success or -1 in case of error.
637 int jbus_start_serving(struct jbus *jbus)
639 return mkerrno(sd_bus_request_name(jbus->sdbus, jbus->name, 0));
643 * Asynchronous call to 'method' of 'jbus' passing the string 'query'.
644 * On response, the function 'onresp' is called with the returned string
645 * value and the closure 'data'.
646 * The function 'onresp' is invoked with 3 parameters:
647 * 1. int: 0 if no error or -1 if error.
648 * 2. const char *: the returned string (might be NULL if error)
649 * 3. void *: the closure 'data'
651 * Returns 0 in case of success or -1 in case of error.
657 void (*onresp) (int, const char *, void *),
660 return call(jbus, method, query, onresp, NULL, data);
664 * Asynchronous call to 'method' of 'jbus' passing the string 'query'.
665 * On response, the function 'onresp' is called with the returned json
666 * value and the closure 'data'.
667 * The function 'onresp' is invoked with 3 parameters:
668 * 1. int: 0 if no error or -1 if error.
669 * 2. const char *: the returned json (might be NULL if error)
670 * 3. void *: the closure 'data'
672 * Returns 0 in case of success or -1 in case of error.
678 void (*onresp) (int, struct json_object *, void *),
681 return call(jbus, method, query, NULL, onresp, data);
685 * Asynchronous call to 'method' of 'jbus' passing the json 'query'.
686 * On response, the function 'onresp' is called with the returned string
687 * value and the closure 'data'.
688 * The function 'onresp' is invoked with 3 parameters:
689 * 1. int: 0 if no error or -1 if error.
690 * 2. const char *: the returned string (might be NULL if error)
691 * 3. void *: the closure 'data'
693 * Returns 0 in case of success or -1 in case of error.
698 struct json_object *query,
699 void (*onresp) (int, const char *, void *),
702 const char *str = json_object_to_json_string(query);
707 return call(jbus, method, str, onresp, NULL, data);
711 * Asynchronous call to 'method' of 'jbus' passing the json 'query'.
712 * On response, the function 'onresp' is called with the returned json
713 * value and the closure 'data'.
714 * The function 'onresp' is invoked with 3 parameters:
715 * 1. int: 0 if no error or -1 if error.
716 * 2. const char *: the returned json (might be NULL if error)
717 * 3. void *: the closure 'data'
719 * Returns 0 in case of success or -1 in case of error.
724 struct json_object *query,
725 void (*onresp) (int, struct json_object *, void *),
728 const char *str = json_object_to_json_string(query);
733 return call(jbus, method, str, NULL, onresp, data);
737 * Synchronous call to 'method' of 'jbus' passing the string 'query'.
738 * The returned string response is returned.
740 * Returns the string response or NULL in case of error.
742 char *jbus_call_ss_sync(
747 sd_bus_message *smsg = NULL;
748 sd_bus_error error = SD_BUS_ERROR_NULL;
753 if (mkerrno(sd_bus_call_method(jbus->sdbus, jbus->name, jbus->path, jbus->name, method, &error, &smsg, "s", query)) < 0)
757 if (sd_bus_message_is_method_error(smsg, NULL))
760 /* check the returned type */
761 if (!sd_bus_message_has_signature(smsg, "s")
762 || sd_bus_message_read_basic(smsg, 's', &reply) < 0)
766 result = strdup(reply);
769 sd_bus_message_unref(smsg);
770 sd_bus_error_free(&error);
775 * Synchronous call to 'method' of 'jbus' passing the string 'query'.
776 * The returned json response is returned.
778 * Returns the json response or NULL in case of error.
780 struct json_object *jbus_call_sj_sync(
785 struct json_object *obj;
786 char *str = jbus_call_ss_sync(jbus, method, query);
790 jparse(jbus, str, &obj);
797 * Synchronous call to 'method' of 'jbus' passing the json 'query'.
798 * The returned string response is returned.
800 * Returns the string response or NULL in case of error.
802 char *jbus_call_js_sync(
805 struct json_object *query)
807 const char *str = json_object_to_json_string(query);
812 return jbus_call_ss_sync(jbus, method, str);
816 * Synchronous call to 'method' of 'jbus' passing the json 'query'.
817 * The returned json response is returned.
819 * Returns the json response or NULL in case of error.
821 struct json_object *jbus_call_jj_sync(
824 struct json_object *query)
826 const char *str = json_object_to_json_string(query);
831 return jbus_call_sj_sync(jbus, method, str);
835 * Records for 'jbus' the string signal handler 'onsig' with closure 'data'
836 * for the signal of 'name'.
837 * The callback handler is called with 2 arguments:
838 * 1. char *: the string parameter associated to the signal
839 * 2. void *: the closure data.
841 * Returns 0 in case of success or -1 otherwise.
843 int jbus_on_signal_s(
846 void (*onsig) (const char *, void *),
849 return add_signal(jbus, name, onsig, NULL, data);
853 * Records for 'jbus' the json signal handler 'onsig' with closure 'data'
854 * for the signal of 'name'.
855 * The callback handler is called with 2 arguments:
856 * 1. struct json_object *: the json parameter associated to the signal
857 * 2. void *: the closure data.
859 * Returns 0 in case of success or -1 otherwise.
861 int jbus_on_signal_j(
864 void (*onsig) (struct json_object *, void *),
867 return add_signal(jbus, name, NULL, onsig, data);
870 /****************** FEW LITTLE TESTS *****************************************/
872 #if defined(SERVER)||defined(CLIENT)
876 static struct sd_bus *msbus()
878 static struct sd_bus *r = NULL;
881 sd_event_default(&e);
882 sd_bus_open_user(&r);
883 sd_bus_attach_event(r, e, 0);
888 static sd_event *events()
890 static sd_event *ev = NULL;
892 ev = sd_bus_get_event(msbus());
896 static int mwait(int timeout, void *closure)
898 sd_event_run(events(), -1);
902 static struct jbus *jbus;
905 void ping(struct sd_bus_message *smsg, struct json_object *request, void *unused)
907 printf("ping(%s) -> %s\n", json_object_to_json_string(request),
908 json_object_to_json_string(request));
909 jbus_reply_j(smsg, request);
910 json_object_put(request);
913 void incr(struct sd_bus_message *smsg, struct json_object *request, void *unused)
915 static int counter = 0;
916 struct json_object *res = json_object_new_int(++counter);
917 printf("incr(%s) -> %s\n", json_object_to_json_string(request),
918 json_object_to_json_string(res));
919 jbus_reply_j(smsg, res);
920 jbus_send_signal_j(jbus, "incremented", res);
921 json_object_put(res);
922 json_object_put(request);
928 jbus = create_jbus(msbus(), "/bzh/iot/jdbus");
929 s1 = jbus_add_service_j(jbus, "ping", ping, NULL);
930 s2 = jbus_add_service_j(jbus, "incr", incr, NULL);
931 s3 = jbus_start_serving(jbus);
932 printf("started %d %d %d\n", s1, s2, s3);
933 while (!mwait(-1,jbus)) ;
939 void onresp(int status, struct json_object *response, void *data)
941 printf("resp: %d, %s, %s\n", status, (char *)data,
942 json_object_to_json_string(response));
943 json_object_put(response);
946 void signaled(const char *content, void *data)
948 printf("signaled with {%s}/%s\n", content, (char*)data);
954 jbus = create_jbus(msbus(), "/bzh/iot/jdbus");
955 jbus_on_signal_s(jbus, "incremented", signaled, "closure-signal");
957 jbus_call_sj(jbus, "ping", "{\"toto\":[1,2,3,4,true,\"toto\"]}",
959 jbus_call_sj(jbus, "incr", "{\"doit\":\"for-me\"}", onresp,
964 jbus_call_ss_sync(jbus, "ping", "\"formidable!\""));
965 while (!mwait(-1,jbus)) ;