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>
30 #include "utils-jbus.h"
33 * max depth of json messages
35 #define MAX_JSON_DEPTH 10
38 * errors messages generated by jbus
40 static const char out_of_memory_string[] = "out of memory";
43 * structure for services
46 struct jservice *next; /* link to the next service */
47 char *method; /* method name for the service */
48 void (*oncall_s) (struct sd_bus_message *, const char *, void *);
50 void (*oncall_j) (struct sd_bus_message *, struct json_object *, void *);
52 void *data; /* closure data for the callbacks */
56 * structure for signals
59 struct jsignal *next; /* link to the next signal */
60 char *name; /* name of the expected signal */
61 void (*onsignal_s) (const char *, void *);
63 void (*onsignal_j) (struct json_object *, void *);
65 void *data; /* closure data for the callbacks */
69 * structure for asynchronous requests
73 void (*onresp_s) (int, const char *, void *);
75 void (*onresp_j) (int, struct json_object *, void *);
77 void *data; /* closure data for the callbacks */
81 * structure for handling either client or server jbus on dbus
84 int refcount; /* referenced how many time */
86 struct sd_bus_slot *sservice;
87 struct sd_bus_slot *ssignal;
88 struct json_tokener *tokener; /* string to json tokenizer */
89 struct jservice *services; /* first service */
90 struct jsignal *signals; /* first signal */
91 char *path; /* dbus path */
92 char *name; /* dbus name */
95 /*********************** STATIC COMMON METHODS *****************/
97 static int mkerrno(int rc)
106 * Replies the error "out of memory".
107 * This function is intended to be used in services when an
108 * allocation fails. Thus, it set errno to ENOMEM and
111 static inline int reply_out_of_memory(struct sd_bus_message *smsg)
113 jbus_reply_error_s(smsg, out_of_memory_string);
119 * Parses the json-string 'msg' to create a json object stored
120 * in 'obj'. It uses the tokener of 'jbus'. This is a small
121 * improvement to avoid recreation of tokeners.
123 * Returns 1 in case of success and put the result in *'obj'.
124 * Returns 0 in case of error and put NULL in *'obj'.
126 static int jparse(struct jbus *jbus, const char *msg, struct json_object **obj)
128 json_tokener_reset(jbus->tokener);
129 *obj = json_tokener_parse_ex(jbus->tokener, msg, -1);
130 if (json_tokener_get_error(jbus->tokener) == json_tokener_success)
132 json_object_put(*obj);
137 static int on_service_call(struct sd_bus_message *smsg, struct jbus *jbus, sd_bus_error *error)
139 struct jservice *service;
140 const char *member, *content;
141 struct json_object *obj;
144 if (!sd_bus_message_has_signature(smsg, "s")
145 || sd_bus_message_read_basic(smsg, 's', &content) < 0) {
146 sd_bus_error_set_const(error, "bad signature", "");
151 member = sd_bus_message_get_member(smsg);
152 service = jbus->services;
153 while (service != NULL) {
154 if (!strcmp(service->method, member)) {
155 if (service->oncall_s)
156 service->oncall_s(smsg, content, service->data);
157 else if (service->oncall_j) {
158 if (!jparse(jbus, content, &obj))
159 obj = json_object_new_string(content);
160 service->oncall_j(smsg, obj, service->data);
161 json_object_put(obj);
165 service = service->next;
171 * Adds to 'jbus' a service of name 'method'. The service is
172 * performed by one of the callback 'oncall_s' (for string) or
173 * 'oncall_j' (for json) that will receive the request and the
174 * closure parameter 'data'.
176 * returns 0 in case of success or -1 in case of error (ENOMEM).
178 static int add_service(
181 void (*oncall_s) (struct sd_bus_message *, const char *, void *),
182 void (*oncall_j) (struct sd_bus_message *, struct json_object *, void *),
186 struct jservice *srv;
188 /* connection of the service */
189 if (jbus->sservice == NULL) {
190 rc = sd_bus_add_object(jbus->sdbus, &jbus->sservice, jbus->path, (void*)on_service_call, jbus);
198 srv = malloc(sizeof *srv);
203 srv->method = strdup(method);
209 /* record the service */
210 srv->oncall_s = oncall_s;
211 srv->oncall_j = oncall_j;
213 srv->next = jbus->services;
214 jbus->services = srv;
224 static int on_signal_event(struct sd_bus_message *smsg, struct jbus *jbus, sd_bus_error *error)
226 struct jsignal *signal;
227 const char *member, *content;
228 struct json_object *obj;
231 if (!sd_bus_message_has_signature(smsg, "s")
232 || sd_bus_message_read_basic(smsg, 's', &content) < 0)
236 member = sd_bus_message_get_member(smsg);
237 signal = jbus->signals;
238 while (signal != NULL) {
239 if (!strcmp(signal->name, member)) {
240 if (signal->onsignal_s)
241 signal->onsignal_s(content, signal->data);
242 else if (signal->onsignal_j) {
243 if (!jparse(jbus, content, &obj))
244 obj = json_object_new_string(content);
245 signal->onsignal_j(obj, signal->data);
246 json_object_put(obj);
249 signal = signal->next;
255 * Adds to 'jbus' a handler for the signal of 'name' emmited by
256 * the sender and the interface that 'jbus' is linked to.
257 * The signal is handled by one of the callback 'onsignal_s'
258 * (for string) or 'onsignal_j' (for json) that will receive
259 * parameters associated with the signal and the closure
262 * returns 0 in case of success or -1 in case of error (ENOMEM).
264 static int add_signal(
267 void (*onsignal_s) (const char *, void *),
268 void (*onsignal_j) (struct json_object *, void *),
275 /* connection of the signal */
276 if (jbus->ssignal == NULL) {
277 rc = asprintf(&match, "type='signal',path='%s',interface='%s'", jbus->path, jbus->name);
282 rc = sd_bus_add_match(jbus->sdbus, &jbus->ssignal, match, (void*)on_signal_event, jbus);
291 sig = malloc(sizeof *sig);
296 sig->name = strdup(name);
302 /* record the signal */
303 sig->onsignal_s = onsignal_s;
304 sig->onsignal_j = onsignal_j;
306 sig->next = jbus->signals;
317 static int on_reply(struct sd_bus_message *smsg, struct jrespw *jrespw, sd_bus_error *error)
319 struct json_object *obj;
324 if (!sd_bus_message_has_signature(smsg, "s")
325 || sd_bus_message_read_basic(smsg, 's', &reply) < 0) {
326 sd_bus_error_set_const(error, "bad signature", "");
329 iserror = sd_bus_message_is_method_error(smsg, NULL);
331 /* dispatch string? */
332 if (jrespw->onresp_s != NULL) {
333 jrespw->onresp_s(iserror, reply, jrespw->data);
338 if (!jparse(jrespw->jbus, reply, &obj))
339 obj = json_object_new_string(reply);
340 jrespw->onresp_j(iserror, obj, jrespw->data);
341 json_object_put(obj);
349 * Creates a message for 'method' with one string parameter being 'query'
350 * and sends it to the destination, object and interface linked to 'jbus'.
352 * Adds to 'jbus' the response handler defined by the callbacks 'onresp_s'
353 * (for string) and 'onresp_j' (for json) and the closure parameter 'data'.
355 * Returns 0 in case of success or -1 in case of error.
361 void (*onresp_s) (int, const char *, void *),
362 void (*onresp_j) (int, struct json_object *, void *),
368 /* allocates the response structure */
369 resp = malloc(sizeof *resp);
375 /* fulfill the response structure */
377 resp->onresp_s = onresp_s;
378 resp->onresp_j = onresp_j;
381 rc = sd_bus_call_method_async(jbus->sdbus, NULL, jbus->name, jbus->path, jbus->name, method, (void*)on_reply, resp, "s", query);
395 /********************* MAIN FUNCTIONS *****************************************/
398 * Creates a 'jbus' bound the 'path' and it derived names and linked
399 * either to the DBUS SYSTEM when 'session' is nul or to the DBUS SESSION
400 * if 'session' is not nul.
402 * The parameter 'path' is intended to be the path of a DBUS single object.
403 * Single means that it exists only one instance of the object on the
404 * given bus. That path implies 2 derived DBUS names:
405 * 1. the destination name of the program that handles the object
406 * 2. the interface name of the object
407 * These names are derived by removing the heading slash (/) and
408 * by replacing all occurences of slashes by dots.
409 * For example, passing path = /a/b/c means that the object /a/b/c is
410 * handled by the destination a.b.c and replies to the interface a.b.c
412 * Returns the created jbus or NULL in case of error.
414 struct jbus *create_jbus(struct sd_bus *sdbus, const char *path)
419 /* create the jbus object */
420 jbus = calloc(1, sizeof *jbus);
427 /* create the tokener */
428 jbus->tokener = json_tokener_new_ex(MAX_JSON_DEPTH);
429 if (jbus->tokener == NULL) {
434 /* records the path */
435 jbus->path = strdup(path);
436 if (jbus->path == NULL) {
441 /* makes the name from the path */
444 jbus->name = name = strdup(path);
455 while (name >= jbus->name && *name == '.')
462 /* connect and init */
463 jbus->sdbus = sd_bus_ref(sdbus);
474 * Adds one reference to 'jbus'.
476 void jbus_addref(struct jbus *jbus)
482 * Removes one reference to 'jbus'. Destroys 'jbus' and it related
483 * data if the count of references decrease to zero.
485 void jbus_unref(struct jbus *jbus)
487 struct jservice *srv;
489 if (!--jbus->refcount) {
490 while ((srv = jbus->services) != NULL) {
491 jbus->services = srv->next;
495 while ((sig = jbus->signals) != NULL) {
496 jbus->signals = sig->next;
500 if (jbus->sservice != NULL)
501 sd_bus_slot_unref(jbus->sservice);
502 if (jbus->ssignal != NULL)
503 sd_bus_slot_unref(jbus->ssignal);
504 if (jbus->tokener != NULL)
505 json_tokener_free(jbus->tokener);
506 sd_bus_unref(jbus->sdbus);
514 * Replies an error of string 'error' to the request handled by 'smsg'.
515 * Also destroys the request 'smsg' that must not be used later.
517 * Returns 0 in case of success or -1 in case of error.
519 int jbus_reply_error_s(struct sd_bus_message *smsg, const char *error)
521 return mkerrno(sd_bus_reply_method_errorf(smsg, "error", "%s", error));
525 * Replies an error of json 'reply' to the request handled by 'smsg'.
526 * Also destroys the request 'smsg' that must not be used later.
528 * Returns 0 in case of success or -1 in case of error.
530 int jbus_reply_error_j(struct sd_bus_message *smsg, struct json_object *reply)
532 const char *str = json_object_to_json_string(reply);
533 return str ? jbus_reply_error_s(smsg, str) : reply_out_of_memory(smsg);
537 * Replies normally the string 'reply' to the request handled by 'smsg'.
538 * Also destroys the request 'smsg' that must not be used later.
540 * Returns 0 in case of success or -1 in case of error.
542 int jbus_reply_s(struct sd_bus_message *smsg, const char *reply)
544 return mkerrno(sd_bus_reply_method_return(smsg, "s", reply));
548 * Replies normally the json 'reply' to the request handled by 'smsg'.
549 * Also destroys the request 'smsg' that must not be used later.
551 * Returns 0 in case of success or -1 in case of error.
553 int jbus_reply_j(struct sd_bus_message *smsg, struct json_object *reply)
555 const char *str = json_object_to_json_string(reply);
556 return str ? jbus_reply_s(smsg, str) : reply_out_of_memory(smsg);
560 * Sends from 'jbus' the signal of 'name' handling the string 'content'.
562 * Returns 0 in case of success or -1 in case of error.
564 int jbus_send_signal_s(struct jbus *jbus, const char *name, const char *content)
566 return mkerrno(sd_bus_emit_signal(jbus->sdbus, jbus->path, jbus->name, name, "s", content));
570 * Sends from 'jbus' the signal of 'name' handling the json 'content'.
572 * Returns 0 in case of success or -1 in case of error.
574 int jbus_send_signal_j(struct jbus *jbus, const char *name,
575 struct json_object *content)
577 const char *str = json_object_to_json_string(content);
582 return jbus_send_signal_s(jbus, name, str);
586 * Adds to 'jbus' a service handling calls to the 'method' using
587 * the "string" callback 'oncall' and the closure value 'data'.
589 * The callback 'oncall' is invoked for handling incoming method
590 * calls. It receives 3 parameters:
591 * 1. struct sd_bus_message *: a handler to data to be used for replying
592 * 2. const char *: the received string
593 * 3. void *: the closure 'data' set by this function
595 * Returns 0 in case of success or -1 in case of error.
597 int jbus_add_service_s(
600 void (*oncall) (struct sd_bus_message *, const char *, void *),
603 return add_service(jbus, method, oncall, NULL, data);
607 * Adds to 'jbus' a service handling calls to the 'method' using
608 * the "json" callback 'oncall' and the closure value 'data'.
610 * The callback 'oncall' is invoked for handling incoming method
611 * calls. It receives 3 parameters:
612 * 1. struct sd_bus_message *: a handler to data to be used for replying
613 * 2. struct json_object *: the received json
614 * 3. void *: the closure 'data' set by this function
616 * Returns 0 in case of success or -1 in case of error.
618 int jbus_add_service_j(
621 void (*oncall) (struct sd_bus_message *, struct json_object *, void *),
624 return add_service(jbus, method, NULL, oncall, data);
628 * Start to serve: activate services declared for 'jbus'.
629 * This function, in fact, declares 'jbus' as the receiver
630 * for calls to the destination derived from the path set at
632 * It also allows 'jbus' to emit signals of that origin.
634 * Returns 0 in case of success or -1 in case of error.
636 int jbus_start_serving(struct jbus *jbus)
638 return mkerrno(sd_bus_request_name(jbus->sdbus, jbus->name, 0));
642 * Asynchronous call to 'method' of 'jbus' passing the string 'query'.
643 * On response, the function 'onresp' is called with the returned string
644 * value and the closure 'data'.
645 * The function 'onresp' is invoked with 3 parameters:
646 * 1. int: 0 if no error or -1 if error.
647 * 2. const char *: the returned string (might be NULL if error)
648 * 3. void *: the closure 'data'
650 * Returns 0 in case of success or -1 in case of error.
656 void (*onresp) (int, const char *, void *),
659 return call(jbus, method, query, onresp, NULL, data);
663 * Asynchronous call to 'method' of 'jbus' passing the string 'query'.
664 * On response, the function 'onresp' is called with the returned json
665 * value and the closure 'data'.
666 * The function 'onresp' is invoked with 3 parameters:
667 * 1. int: 0 if no error or -1 if error.
668 * 2. const char *: the returned json (might be NULL if error)
669 * 3. void *: the closure 'data'
671 * Returns 0 in case of success or -1 in case of error.
677 void (*onresp) (int, struct json_object *, void *),
680 return call(jbus, method, query, NULL, onresp, data);
684 * Asynchronous call to 'method' of 'jbus' passing the json 'query'.
685 * On response, the function 'onresp' is called with the returned string
686 * value and the closure 'data'.
687 * The function 'onresp' is invoked with 3 parameters:
688 * 1. int: 0 if no error or -1 if error.
689 * 2. const char *: the returned string (might be NULL if error)
690 * 3. void *: the closure 'data'
692 * Returns 0 in case of success or -1 in case of error.
697 struct json_object *query,
698 void (*onresp) (int, const char *, void *),
701 const char *str = json_object_to_json_string(query);
706 return call(jbus, method, str, onresp, NULL, data);
710 * Asynchronous call to 'method' of 'jbus' passing the json 'query'.
711 * On response, the function 'onresp' is called with the returned json
712 * value and the closure 'data'.
713 * The function 'onresp' is invoked with 3 parameters:
714 * 1. int: 0 if no error or -1 if error.
715 * 2. const char *: the returned json (might be NULL if error)
716 * 3. void *: the closure 'data'
718 * Returns 0 in case of success or -1 in case of error.
723 struct json_object *query,
724 void (*onresp) (int, struct json_object *, void *),
727 const char *str = json_object_to_json_string(query);
732 return call(jbus, method, str, NULL, onresp, data);
736 * Synchronous call to 'method' of 'jbus' passing the string 'query'.
737 * The returned string response is returned.
739 * Returns the string response or NULL in case of error.
741 char *jbus_call_ss_sync(
746 sd_bus_message *smsg = NULL;
747 sd_bus_error error = SD_BUS_ERROR_NULL;
752 if (mkerrno(sd_bus_call_method(jbus->sdbus, jbus->name, jbus->path, jbus->name, method, &error, &smsg, "s", query)) < 0)
756 if (sd_bus_message_is_method_error(smsg, NULL))
759 /* check the returned type */
760 if (!sd_bus_message_has_signature(smsg, "s")
761 || sd_bus_message_read_basic(smsg, 's', &reply) < 0)
765 result = strdup(reply);
768 sd_bus_message_unref(smsg);
769 sd_bus_error_free(&error);
774 * Synchronous call to 'method' of 'jbus' passing the string 'query'.
775 * The returned json response is returned.
777 * Returns the json response or NULL in case of error.
779 struct json_object *jbus_call_sj_sync(
784 struct json_object *obj;
785 char *str = jbus_call_ss_sync(jbus, method, query);
789 jparse(jbus, str, &obj);
796 * Synchronous call to 'method' of 'jbus' passing the json 'query'.
797 * The returned string response is returned.
799 * Returns the string response or NULL in case of error.
801 char *jbus_call_js_sync(
804 struct json_object *query)
806 const char *str = json_object_to_json_string(query);
811 return jbus_call_ss_sync(jbus, method, str);
815 * Synchronous call to 'method' of 'jbus' passing the json 'query'.
816 * The returned json response is returned.
818 * Returns the json response or NULL in case of error.
820 struct json_object *jbus_call_jj_sync(
823 struct json_object *query)
825 const char *str = json_object_to_json_string(query);
830 return jbus_call_sj_sync(jbus, method, str);
834 * Records for 'jbus' the string signal handler 'onsig' with closure 'data'
835 * for the signal of 'name'.
836 * The callback handler is called with 2 arguments:
837 * 1. char *: the string parameter associated to the signal
838 * 2. void *: the closure data.
840 * Returns 0 in case of success or -1 otherwise.
842 int jbus_on_signal_s(
845 void (*onsig) (const char *, void *),
848 return add_signal(jbus, name, onsig, NULL, data);
852 * Records for 'jbus' the json signal handler 'onsig' with closure 'data'
853 * for the signal of 'name'.
854 * The callback handler is called with 2 arguments:
855 * 1. struct json_object *: the json parameter associated to the signal
856 * 2. void *: the closure data.
858 * Returns 0 in case of success or -1 otherwise.
860 int jbus_on_signal_j(
863 void (*onsig) (struct json_object *, void *),
866 return add_signal(jbus, name, NULL, onsig, data);
869 /****************** FEW LITTLE TESTS *****************************************/
871 #if defined(SERVER)||defined(CLIENT)
875 static struct sd_bus *msbus()
877 static struct sd_bus *r = NULL;
880 sd_event_default(&e);
881 sd_bus_open_user(&r);
882 sd_bus_attach_event(r, e, 0);
887 static sd_event *events()
889 static sd_event *ev = NULL;
891 ev = sd_bus_get_event(msbus());
895 static int mwait(int timeout, void *closure)
897 sd_event_run(events(), -1);
901 static struct jbus *jbus;
904 void ping(struct sd_bus_message *smsg, struct json_object *request, void *unused)
906 printf("ping(%s) -> %s\n", json_object_to_json_string(request),
907 json_object_to_json_string(request));
908 jbus_reply_j(smsg, request);
909 json_object_put(request);
912 void incr(struct sd_bus_message *smsg, struct json_object *request, void *unused)
914 static int counter = 0;
915 struct json_object *res = json_object_new_int(++counter);
916 printf("incr(%s) -> %s\n", json_object_to_json_string(request),
917 json_object_to_json_string(res));
918 jbus_reply_j(smsg, res);
919 jbus_send_signal_j(jbus, "incremented", res);
920 json_object_put(res);
921 json_object_put(request);
927 jbus = create_jbus(msbus(), "/bzh/iot/jdbus");
928 s1 = jbus_add_service_j(jbus, "ping", ping, NULL);
929 s2 = jbus_add_service_j(jbus, "incr", incr, NULL);
930 s3 = jbus_start_serving(jbus);
931 printf("started %d %d %d\n", s1, s2, s3);
932 while (!mwait(-1,jbus)) ;
938 void onresp(int status, struct json_object *response, void *data)
940 printf("resp: %d, %s, %s\n", status, (char *)data,
941 json_object_to_json_string(response));
942 json_object_put(response);
945 void signaled(const char *content, void *data)
947 printf("signaled with {%s}/%s\n", content, (char*)data);
953 jbus = create_jbus(msbus(), "/bzh/iot/jdbus");
954 jbus_on_signal_s(jbus, "incremented", signaled, "closure-signal");
956 jbus_call_sj(jbus, "ping", "{\"toto\":[1,2,3,4,true,\"toto\"]}",
958 jbus_call_sj(jbus, "incr", "{\"doit\":\"for-me\"}", onresp,
963 jbus_call_ss_sync(jbus, "ping", "\"formidable!\""));
964 while (!mwait(-1,jbus)) ;