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.
29 #include "utils-sbus.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 sbusmsg *, const char *, void *);
50 void (*oncall_j) (struct sbusmsg *, 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) (int, struct json_object *, void *);
75 void *data; /* closure data for the callbacks */
79 * structure for handling either client or server jbus on dbus
82 int refcount; /* referenced how many time */
84 struct sbus_service *sservice;
85 struct sbus_signal *ssignal;
86 struct json_tokener *tokener; /* string to json tokenizer */
87 struct jservice *services; /* first service */
88 struct jsignal *signals; /* first signal */
89 char *path; /* dbus path */
90 char *name; /* dbus name */
93 /*********************** STATIC COMMON METHODS *****************/
96 * Replies the error "out of memory".
97 * This function is intended to be used in services when an
98 * allocation fails. Thus, it set errno to ENOMEM and
101 static inline int reply_out_of_memory(struct sbusmsg *smsg)
103 jbus_reply_error_s(smsg, out_of_memory_string);
109 * Parses the json-string 'msg' to create a json object stored
110 * in 'obj'. It uses the tokener of 'jbus'. This is a small
111 * improvement to avoid recreation of tokeners.
113 * Returns 1 in case of success and put the result in *'obj'.
114 * Returns 0 in case of error and put NULL in *'obj'.
116 static int jparse(struct jbus *jbus, const char *msg, struct json_object **obj)
118 json_tokener_reset(jbus->tokener);
119 *obj = json_tokener_parse_ex(jbus->tokener, msg, -1);
120 if (json_tokener_get_error(jbus->tokener) == json_tokener_success)
122 json_object_put(*obj);
127 static void on_service_call(struct sbusmsg *smsg, const char *content, struct jbus *jbus)
129 struct jservice *service;
131 struct json_object *obj;
133 member = sbus_member(smsg);
134 service = jbus->services;
135 while (service != NULL) {
136 if (!strcmp(service->method, member)) {
137 if (service->oncall_s)
138 service->oncall_s(smsg, content, service->data);
139 else if (service->oncall_j) {
140 if (!jparse(jbus, content, &obj))
141 obj = json_object_new_string(content);
142 service->oncall_j(smsg, obj, service->data);
143 json_object_put(obj);
146 service = service->next;
151 * Adds to 'jbus' a service of name 'method'. The service is
152 * performed by one of the callback 'oncall_s' (for string) or
153 * 'oncall_j' (for json) that will receive the request and the
154 * closure parameter 'data'.
156 * returns 0 in case of success or -1 in case of error (ENOMEM).
158 static int add_service(
161 void (*oncall_s) (struct sbusmsg *, const char *, void *),
162 void (*oncall_j) (struct sbusmsg *, struct json_object *, void *),
165 struct jservice *srv;
167 /* connection of the service */
168 if (jbus->sservice == NULL) {
169 jbus->sservice = sbus_add_service(jbus->sbus,
170 NULL, jbus->path, jbus->name, NULL,
171 (void*)on_service_call, jbus);
172 if (jbus->sservice == NULL)
177 srv = malloc(sizeof *srv);
182 srv->method = strdup(method);
188 /* record the service */
189 srv->oncall_s = oncall_s;
190 srv->oncall_j = oncall_j;
192 srv->next = jbus->services;
193 jbus->services = srv;
204 static void on_signal_event(const struct sbusmsg *smsg, const char *content, struct jbus *jbus)
206 struct jsignal *signal;
208 struct json_object *obj;
210 member = sbus_member(smsg);
211 signal = jbus->signals;
212 while (signal != NULL) {
213 if (!strcmp(signal->name, member)) {
214 if (signal->onsignal_s)
215 signal->onsignal_s(content, signal->data);
216 else if (signal->onsignal_j) {
217 if (!jparse(jbus, content, &obj))
218 obj = json_object_new_string(content);
219 signal->onsignal_j(obj, signal->data);
220 json_object_put(obj);
223 signal = signal->next;
228 * Adds to 'jbus' a handler for the signal of 'name' emmited by
229 * the sender and the interface that 'jbus' is linked to.
230 * The signal is handled by one of the callback 'onsignal_s'
231 * (for string) or 'onsignal_j' (for json) that will receive
232 * parameters associated with the signal and the closure
235 * returns 0 in case of success or -1 in case of error (ENOMEM).
237 static int add_signal(
240 void (*onsignal_s) (const char *, void *),
241 void (*onsignal_j) (struct json_object *, void *),
246 /* connection of the signal */
247 if (jbus->ssignal == NULL) {
248 jbus->ssignal = sbus_add_signal(jbus->sbus,
249 NULL, jbus->path, jbus->name, NULL,
250 (void*)on_signal_event, jbus);
251 if (jbus->ssignal == NULL)
256 sig = malloc(sizeof *sig);
259 sig->name = strdup(name);
263 /* record the signal */
264 sig->onsignal_s = onsignal_s;
265 sig->onsignal_j = onsignal_j;
267 sig->next = jbus->signals;
279 static void on_reply_j(int status, const char *reply, struct jrespw *jrespw)
281 struct json_object *obj;
283 if (!jparse(jrespw->jbus, reply, &obj))
284 obj = json_object_new_string(reply);
285 jrespw->onresp(status, obj, jrespw->data);
286 json_object_put(obj);
291 * Creates a message for 'method' with one string parameter being 'query'
292 * and sends it to the destination, object and interface linked to 'jbus'.
294 * Adds to 'jbus' the response handler defined by the callbacks 'onresp_s'
295 * (for string) and 'onresp_j' (for json) and the closure parameter 'data'.
297 * Returns 0 in case of success or -1 in case of error.
303 void (*onresp_s) (int, const char *, void *),
304 void (*onresp_j) (int, struct json_object *, void *),
309 if (onresp_j == NULL)
310 return sbus_call(jbus->sbus, jbus->name, jbus->path, jbus->name,
311 method, query, onresp_s, data);
313 /* allocates the response structure */
314 resp = malloc(sizeof *resp);
318 /* fulfill the response structure */
320 resp->onresp = onresp_j;
322 if (sbus_call(jbus->sbus, jbus->name, jbus->path, jbus->name,
323 method, query, (void*)on_reply_j, resp))
335 /********************* MAIN FUNCTIONS *****************************************/
338 * Creates a 'jbus' bound the 'path' and it derived names and linked
339 * either to the DBUS SYSTEM when 'session' is nul or to the DBUS SESSION
340 * if 'session' is not nul.
342 * The parameter 'path' is intended to be the path of a DBUS single object.
343 * Single means that it exists only one instance of the object on the
344 * given bus. That path implies 2 derived DBUS names:
345 * 1. the destination name of the program that handles the object
346 * 2. the interface name of the object
347 * These names are derived by removing the heading slash (/) and
348 * by replacing all occurences of slashes by dots.
349 * For example, passing path = /a/b/c means that the object /a/b/c is
350 * handled by the destination a.b.c and replies to the interface a.b.c
352 * Returns the created jbus or NULL in case of error.
354 struct jbus *create_jbus(struct sbus *sbus, const char *path)
359 /* create the jbus object */
360 jbus = calloc(1, sizeof *jbus);
367 /* create the tokener */
368 jbus->tokener = json_tokener_new_ex(MAX_JSON_DEPTH);
369 if (jbus->tokener == NULL) {
374 /* records the path */
375 jbus->path = strdup(path);
376 if (jbus->path == NULL) {
381 /* makes the name from the path */
384 jbus->name = name = strdup(path);
395 while (name >= jbus->name && *name == '.')
402 /* connect and init */
414 * Adds one reference to 'jbus'.
416 void jbus_addref(struct jbus *jbus)
422 * Removes one reference to 'jbus'. Destroys 'jbus' and it related
423 * data if the count of references decrease to zero.
425 void jbus_unref(struct jbus *jbus)
427 struct jservice *srv;
429 if (!--jbus->refcount) {
430 while ((srv = jbus->services) != NULL) {
431 jbus->services = srv->next;
435 while ((sig = jbus->signals) != NULL) {
436 jbus->signals = sig->next;
440 if (jbus->sservice != NULL)
441 sbus_remove_service(jbus->sbus, jbus->sservice);
442 if (jbus->ssignal != NULL)
443 sbus_remove_signal(jbus->sbus, jbus->ssignal);
444 if (jbus->tokener != NULL)
445 json_tokener_free(jbus->tokener);
446 sbus_unref(jbus->sbus);
454 * Replies an error of string 'error' to the request handled by 'smsg'.
455 * Also destroys the request 'smsg' that must not be used later.
457 * Returns 0 in case of success or -1 in case of error.
459 int jbus_reply_error_s(struct sbusmsg *smsg, const char *error)
461 return sbus_reply_error(smsg, error);
465 * Replies an error of json 'reply' to the request handled by 'smsg'.
466 * Also destroys the request 'smsg' that must not be used later.
468 * Returns 0 in case of success or -1 in case of error.
470 int jbus_reply_error_j(struct sbusmsg *smsg, struct json_object *reply)
472 const char *str = json_object_to_json_string(reply);
473 return str ? jbus_reply_error_s(smsg, str) : reply_out_of_memory(smsg);
477 * Replies normally the string 'reply' to the request handled by 'smsg'.
478 * Also destroys the request 'smsg' that must not be used later.
480 * Returns 0 in case of success or -1 in case of error.
482 int jbus_reply_s(struct sbusmsg *smsg, const char *reply)
484 return sbus_reply(smsg, reply);
488 * Replies normally the json 'reply' to the request handled by 'smsg'.
489 * Also destroys the request 'smsg' that must not be used later.
491 * Returns 0 in case of success or -1 in case of error.
493 int jbus_reply_j(struct sbusmsg *smsg, struct json_object *reply)
495 const char *str = json_object_to_json_string(reply);
496 return str ? jbus_reply_s(smsg, str) : reply_out_of_memory(smsg);
500 * Sends from 'jbus' the signal of 'name' handling the string 'content'.
502 * Returns 0 in case of success or -1 in case of error.
504 int jbus_send_signal_s(struct jbus *jbus, const char *name, const char *content)
506 return sbus_send_signal(jbus->sbus, jbus->name, jbus->path, jbus->name, name, content);
510 * Sends from 'jbus' the signal of 'name' handling the json 'content'.
512 * Returns 0 in case of success or -1 in case of error.
514 int jbus_send_signal_j(struct jbus *jbus, const char *name,
515 struct json_object *content)
517 const char *str = json_object_to_json_string(content);
522 return jbus_send_signal_s(jbus, name, str);
526 * Adds to 'jbus' a service handling calls to the 'method' using
527 * the "string" callback 'oncall' and the closure value 'data'.
529 * The callback 'oncall' is invoked for handling incoming method
530 * calls. It receives 3 parameters:
531 * 1. struct sbusmsg *: a handler to data to be used for replying
532 * 2. const char *: the received string
533 * 3. void *: the closure 'data' set by this function
535 * Returns 0 in case of success or -1 in case of error.
537 int jbus_add_service_s(
540 void (*oncall) (struct sbusmsg *, const char *, void *),
543 return add_service(jbus, method, oncall, NULL, data);
547 * Adds to 'jbus' a service handling calls to the 'method' using
548 * the "json" callback 'oncall' and the closure value 'data'.
550 * The callback 'oncall' is invoked for handling incoming method
551 * calls. It receives 3 parameters:
552 * 1. struct sbusmsg *: a handler to data to be used for replying
553 * 2. struct json_object *: the received json
554 * 3. void *: the closure 'data' set by this function
556 * Returns 0 in case of success or -1 in case of error.
558 int jbus_add_service_j(
561 void (*oncall) (struct sbusmsg *, struct json_object *, void *),
564 return add_service(jbus, method, NULL, oncall, data);
568 * Start to serve: activate services declared for 'jbus'.
569 * This function, in fact, declares 'jbus' as the receiver
570 * for calls to the destination derived from the path set at
572 * It also allows 'jbus' to emit signals of that origin.
574 * Returns 0 in case of success or -1 in case of error.
576 int jbus_start_serving(struct jbus *jbus)
578 return sbus_add_name(jbus->sbus, jbus->name);
582 * Asynchronous call to 'method' of 'jbus' passing the string 'query'.
583 * On response, the function 'onresp' is called with the returned string
584 * value and the closure 'data'.
585 * The function 'onresp' is invoked with 3 parameters:
586 * 1. int: 0 if no error or -1 if error.
587 * 2. const char *: the returned string (might be NULL if error)
588 * 3. void *: the closure 'data'
590 * Returns 0 in case of success or -1 in case of error.
596 void (*onresp) (int, const char *, void *),
599 return call(jbus, method, query, onresp, NULL, data);
603 * Asynchronous call to 'method' of 'jbus' passing the string 'query'.
604 * On response, the function 'onresp' is called with the returned json
605 * value and the closure 'data'.
606 * The function 'onresp' is invoked with 3 parameters:
607 * 1. int: 0 if no error or -1 if error.
608 * 2. const char *: the returned json (might be NULL if error)
609 * 3. void *: the closure 'data'
611 * Returns 0 in case of success or -1 in case of error.
617 void (*onresp) (int, struct json_object *, void *),
620 return call(jbus, method, query, NULL, onresp, data);
624 * Asynchronous call to 'method' of 'jbus' passing the json 'query'.
625 * On response, the function 'onresp' is called with the returned string
626 * value and the closure 'data'.
627 * The function 'onresp' is invoked with 3 parameters:
628 * 1. int: 0 if no error or -1 if error.
629 * 2. const char *: the returned string (might be NULL if error)
630 * 3. void *: the closure 'data'
632 * Returns 0 in case of success or -1 in case of error.
637 struct json_object *query,
638 void (*onresp) (int, const char *, void *),
641 const char *str = json_object_to_json_string(query);
646 return call(jbus, method, str, onresp, NULL, data);
650 * Asynchronous call to 'method' of 'jbus' passing the json 'query'.
651 * On response, the function 'onresp' is called with the returned json
652 * value and the closure 'data'.
653 * The function 'onresp' is invoked with 3 parameters:
654 * 1. int: 0 if no error or -1 if error.
655 * 2. const char *: the returned json (might be NULL if error)
656 * 3. void *: the closure 'data'
658 * Returns 0 in case of success or -1 in case of error.
663 struct json_object *query,
664 void (*onresp) (int, struct json_object *, void *),
667 const char *str = json_object_to_json_string(query);
672 return call(jbus, method, str, NULL, onresp, data);
676 * Synchronous call to 'method' of 'jbus' passing the string 'query'.
677 * The returned string response is returned.
679 * Returns the string response or NULL in case of error.
681 char *jbus_call_ss_sync(
686 return sbus_call_sync(jbus->sbus, jbus->name, jbus->path, jbus->name,
691 * Synchronous call to 'method' of 'jbus' passing the string 'query'.
692 * The returned json response is returned.
694 * Returns the json response or NULL in case of error.
696 struct json_object *jbus_call_sj_sync(
701 struct json_object *obj;
702 char *str = jbus_call_ss_sync(jbus, method, query);
706 jparse(jbus, str, &obj);
713 * Synchronous call to 'method' of 'jbus' passing the json 'query'.
714 * The returned string response is returned.
716 * Returns the string response or NULL in case of error.
718 char *jbus_call_js_sync(
721 struct json_object *query)
723 const char *str = json_object_to_json_string(query);
728 return jbus_call_ss_sync(jbus, method, str);
732 * Synchronous call to 'method' of 'jbus' passing the json 'query'.
733 * The returned json response is returned.
735 * Returns the json response or NULL in case of error.
737 struct json_object *jbus_call_jj_sync(
740 struct json_object *query)
742 const char *str = json_object_to_json_string(query);
747 return jbus_call_sj_sync(jbus, method, str);
751 * Records for 'jbus' the string signal handler 'onsig' with closure 'data'
752 * for the signal of 'name'.
753 * The callback handler is called with 2 arguments:
754 * 1. char *: the string parameter associated to the signal
755 * 2. void *: the closure data.
757 * Returns 0 in case of success or -1 otherwise.
759 int jbus_on_signal_s(
762 void (*onsig) (const char *, void *),
765 return add_signal(jbus, name, onsig, NULL, data);
769 * Records for 'jbus' the json signal handler 'onsig' with closure 'data'
770 * for the signal of 'name'.
771 * The callback handler is called with 2 arguments:
772 * 1. struct json_object *: the json parameter associated to the signal
773 * 2. void *: the closure data.
775 * Returns 0 in case of success or -1 otherwise.
777 int jbus_on_signal_j(
780 void (*onsig) (struct json_object *, void *),
783 return add_signal(jbus, name, NULL, onsig, data);
786 /****************** FEW LITTLE TESTS *****************************************/
788 #if defined(SERVER)||defined(CLIENT)
791 #include "utils-upoll.h"
793 static int mwait(int timeout, void *closure)
799 static const struct sbus_itf uitf = {
800 .wait = (void*)mwait,
801 .open = (void*)upoll_open,
802 .on_readable = (void*)upoll_on_readable,
803 .on_writable = (void*)upoll_on_writable,
804 .on_hangup = (void*)upoll_on_hangup,
805 .close = (void*)upoll_close
808 static struct sbus *sbus;
809 static struct jbus *jbus;
811 static struct sbus *msbus()
813 return sbus ? : (sbus = sbus_session(&uitf, NULL));
817 void ping(struct sbusmsg *smsg, struct json_object *request, void *unused)
819 printf("ping(%s) -> %s\n", json_object_to_json_string(request),
820 json_object_to_json_string(request));
821 jbus_reply_j(smsg, request);
822 json_object_put(request);
825 void incr(struct sbusmsg *smsg, struct json_object *request, void *unused)
827 static int counter = 0;
828 struct json_object *res = json_object_new_int(++counter);
829 printf("incr(%s) -> %s\n", json_object_to_json_string(request),
830 json_object_to_json_string(res));
831 jbus_reply_j(smsg, res);
832 jbus_send_signal_j(jbus, "incremented", res);
833 json_object_put(res);
834 json_object_put(request);
840 jbus = create_jbus(msbus(), "/bzh/iot/jdbus");
841 s1 = jbus_add_service_j(jbus, "ping", ping, NULL);
842 s2 = jbus_add_service_j(jbus, "incr", incr, NULL);
843 s3 = jbus_start_serving(jbus);
844 printf("started %d %d %d\n", s1, s2, s3);
845 while (!mwait(-1,jbus)) ;
851 void onresp(int status, struct json_object *response, void *data)
853 printf("resp: %d, %s, %s\n", status, (char *)data,
854 json_object_to_json_string(response));
855 json_object_put(response);
858 void signaled(const char *content, void *data)
860 printf("signaled with {%s}/%s\n", content, (char*)data);
866 jbus = create_jbus(msbus(), "/bzh/iot/jdbus");
867 jbus_on_signal_s(jbus, "incremented", signaled, "closure-signal");
869 jbus_call_sj(jbus, "ping", "{\"toto\":[1,2,3,4,true,\"toto\"]}",
871 jbus_call_sj(jbus, "incr", "{\"doit\":\"for-me\"}", onresp,
876 jbus_call_ss_sync(jbus, "ping", "\"formidable!\""));
877 while (!mwait(-1,jbus)) ;