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.
26 #include <dbus/dbus.h>
28 #include "utils-jbus.h"
34 /* structure for handled requests */
36 DBusConnection *connection;
40 /* structure for recorded services */
42 struct jservice *next;
44 void (*oncall_s)(struct jreq *, const char *);
45 void (*oncall_j)(struct jreq *, struct json_object *);
48 /* structure for signal handlers */
52 void (*onsignal_s)(const char *);
53 void (*onsignal_j)(struct json_object *);
56 /* structure for recording asynchronous requests */
61 void (*onresp_s)(int, const char*, void *);
62 void (*onresp_j)(int, struct json_object*, void *);
65 /* structure for synchronous requests */
71 /* structure for handling either client or server jbus on dbus */
74 struct jservice *services;
75 DBusConnection *connection;
76 struct jsignal *signals;
77 struct jrespw *waiters;
82 /*********************** STATIC COMMON METHODS *****************/
84 static inline void free_jreq(struct jreq *jreq)
86 dbus_message_unref(jreq->request);
87 dbus_connection_unref(jreq->connection);
91 static inline int reply_out_of_memory(struct jreq *jreq)
93 static const char out_of_memory[] = "out of memory";
94 jbus_reply_error_s(jreq, out_of_memory);
99 static inline int reply_invalid_request(struct jreq *jreq)
101 static const char invalid_request[] = "invalid request";
102 jbus_reply_error_s(jreq, invalid_request);
103 return DBUS_HANDLER_RESULT_HANDLED;
106 static int matchitf(struct jbus *jbus, DBusMessage *message)
108 const char *itf = dbus_message_get_interface(message);
109 return itf != NULL && !strcmp(itf, jbus->name);
112 static int add_service(
115 void (*oncall_s)(struct jreq*, const char*),
116 void (*oncall_j)(struct jreq*, struct json_object*)
119 struct jservice *srv;
122 srv = malloc(sizeof * srv);
127 srv->method = strdup(method);
133 /* record the service */
134 srv->oncall_s = oncall_s;
135 srv->oncall_j = oncall_j;
136 srv->next = jbus->services;
137 jbus->services = srv;
147 static int add_signal(
150 void (*onsignal_s)(const char*),
151 void (*onsignal_j)(struct json_object*)
157 /* record the signal */
158 if (jbus->signals == NULL) {
159 if (0 >= asprintf(&rule, "type='signal',sender='%s',interface='%s',path='%s'", jbus->name, jbus->name, jbus->path))
161 dbus_bus_add_match(jbus->connection, rule, NULL);
166 sig = malloc(sizeof * sig);
169 sig->name = strdup(name);
173 /* record the signal */
174 sig->onsignal_s = onsignal_s;
175 sig->onsignal_j = onsignal_j;
176 sig->next = jbus->signals;
192 void (*onresp_s)(int status, const char *response, void *data),
193 void (*onresp_j)(int status, struct json_object *response, void *data),
200 resp = malloc(sizeof * resp);
206 msg = dbus_message_new_method_call(jbus->name, jbus->path, jbus->name, method);
212 if (!dbus_message_append_args(msg, DBUS_TYPE_STRING, &query, DBUS_TYPE_INVALID)) {
217 if (!dbus_connection_send(jbus->connection, msg, &resp->serial)) {
221 dbus_message_unref(msg);
223 resp->onresp_s = onresp_s;
224 resp->onresp_j = onresp_j;
225 resp->next = jbus->waiters;
226 jbus->waiters = resp;
230 dbus_message_unref(msg);
237 static void sync_of_replies(int status, const char *value, void *data)
239 struct respsync *s = data;
240 s->value = status ? NULL : strdup(value ? value : "");
244 static DBusHandlerResult incoming_resp(DBusConnection *connection, DBusMessage *message, struct jbus *jbus, int iserror)
248 struct jrespw *jrw, **prv;
249 struct json_object *reply;
250 dbus_uint32_t serial;
252 /* search for the waiter */
253 serial = dbus_message_get_reply_serial(message);
254 prv = &jbus->waiters;
255 while ((jrw = *prv) != NULL && jrw->serial != serial)
258 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
261 /* retrieve the string value */
262 if (!dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &str, DBUS_TYPE_INVALID)) {
270 jrw->onresp_s(iserror ? -1 : status, str, jrw->data);
272 reply = json_tokener_parse(str);
273 status = reply ? 0 : -1;
274 jrw->onresp_j(iserror ? -1 : status, reply, jrw->data);
275 json_object_put(reply);
279 return DBUS_HANDLER_RESULT_HANDLED;
282 static DBusHandlerResult incoming_call(DBusConnection *connection, DBusMessage *message, struct jbus *jbus)
284 struct jservice *srv;
288 struct json_object *query;
290 /* search for the service */
291 if (!matchitf(jbus, message))
292 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
293 method = dbus_message_get_member(message);
295 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
296 srv = jbus->services;
297 while(srv != NULL && strcmp(method, srv->method))
300 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
302 /* handle the message */
303 jreq = malloc(sizeof * jreq);
305 return DBUS_HANDLER_RESULT_NEED_MEMORY;
306 jreq->request = dbus_message_ref(message);
307 jreq->connection = dbus_connection_ref(jbus->connection);
309 /* retrieve the string value */
310 if (!dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &str, DBUS_TYPE_INVALID))
311 return reply_invalid_request(jreq);
313 /* handling strings only */
314 srv->oncall_s(jreq, str);
317 /* handling json only */
318 query = json_tokener_parse(str);
320 return reply_invalid_request(jreq);
321 srv->oncall_j(jreq, query);
322 json_object_put(query);
324 return DBUS_HANDLER_RESULT_HANDLED;
327 static DBusHandlerResult incoming_signal(DBusConnection *connection, DBusMessage *message, struct jbus *jbus)
332 struct json_object *obj;
334 /* search for the service */
335 if (!matchitf(jbus, message))
336 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
337 name = dbus_message_get_member(message);
339 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
341 while(sig != NULL && strcmp(name, sig->name))
344 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
346 /* retrieve the string value */
347 if (dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &str, DBUS_TYPE_INVALID)) {
348 if (sig->onsignal_s) {
349 /* handling strings only */
350 sig->onsignal_s(str);
353 /* handling json only */
354 obj = json_tokener_parse(str);
356 sig->onsignal_j(obj);
357 json_object_put(obj);
361 return DBUS_HANDLER_RESULT_HANDLED;
364 static DBusHandlerResult incoming(DBusConnection *connection, DBusMessage *message, void *data)
366 switch(dbus_message_get_type(message)) {
367 case DBUS_MESSAGE_TYPE_METHOD_CALL:
368 return incoming_call(connection, message, (struct jbus*)data);
369 case DBUS_MESSAGE_TYPE_METHOD_RETURN:
370 return incoming_resp(connection, message, (struct jbus*)data, 0);
371 case DBUS_MESSAGE_TYPE_ERROR:
372 return incoming_resp(connection, message, (struct jbus*)data, 1);
373 case DBUS_MESSAGE_TYPE_SIGNAL:
374 return incoming_signal(connection, message, (struct jbus*)data);
376 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
379 /************************** MAIN FUNCTIONS *****************************************/
381 struct jbus *create_jbus(int session, const char *path)
386 /* create the context and connect */
387 jbus = calloc(1, sizeof * jbus);
393 jbus->path = strdup(path);
395 if (jbus->path == NULL) {
399 while(*path == '/') path++;
400 jbus->name = name = strdup(path);
411 while (name >= jbus->name && *name == '.')
419 jbus->connection = dbus_bus_get(session ? DBUS_BUS_SESSION : DBUS_BUS_SYSTEM, NULL);
420 if (jbus->connection == NULL) {
423 if (!dbus_connection_add_filter(jbus->connection, incoming, jbus, NULL)) {
435 void jbus_addref(struct jbus *jbus)
440 void jbus_unref(struct jbus *jbus)
442 struct jservice *srv;
443 if (!--jbus->refcount) {
444 dbus_connection_unref(jbus->connection);
445 while((srv = jbus->services) != NULL) {
446 jbus->services = srv->next;
456 int jbus_reply_error_s(struct jreq *jreq, const char *error)
459 DBusMessage *message;
461 message = dbus_message_new_error(jreq->request, DBUS_ERROR_FAILED, error);
465 if (dbus_connection_send(jreq->connection, message, NULL))
467 dbus_message_unref(message);
473 int jbus_reply_error_j(struct jreq *jreq, struct json_object *reply)
475 const char *str = json_object_to_json_string(reply);
476 return str ? jbus_reply_error_s(jreq, str) : reply_out_of_memory(jreq);
479 int jbus_reply_s(struct jreq *jreq, const char *reply)
482 DBusMessage *message;
484 message = dbus_message_new_method_return(jreq->request);
486 return reply_out_of_memory(jreq);
488 if (!dbus_message_append_args(message, DBUS_TYPE_STRING, &reply, DBUS_TYPE_INVALID)) {
489 dbus_message_unref(message);
490 return reply_out_of_memory(jreq);
493 if (dbus_connection_send(jreq->connection, message, NULL))
495 dbus_message_unref(message);
500 int jbus_reply_j(struct jreq *jreq, struct json_object *reply)
502 const char *str = json_object_to_json_string(reply);
503 return str ? jbus_reply_s(jreq, str) : reply_out_of_memory(jreq);
506 int jbus_send_signal_s(struct jbus *jbus, const char *name, const char *content)
509 DBusMessage *message;
511 message = dbus_message_new_signal(jbus->path, jbus->name, name);
515 if (!dbus_message_append_args(message, DBUS_TYPE_STRING, &content, DBUS_TYPE_INVALID)) {
516 dbus_message_unref(message);
520 if (dbus_connection_send(jbus->connection, message, NULL))
522 dbus_message_unref(message);
530 int jbus_send_signal_j(struct jbus *jbus, const char *name, struct json_object *content)
532 const char *str = json_object_to_json_string(content);
537 return jbus_send_signal_s(jbus, name, str);
540 int jbus_add_service_s(struct jbus *jbus, const char *method, void (*oncall)(struct jreq *, const char *))
542 return add_service(jbus, method, oncall, NULL);
545 int jbus_add_service_j(struct jbus *jbus, const char *method, void (*oncall)(struct jreq *, struct json_object *))
547 return add_service(jbus, method, NULL, oncall);
550 int jbus_start_serving(struct jbus *jbus)
552 int status = dbus_bus_request_name(jbus->connection, jbus->name, DBUS_NAME_FLAG_DO_NOT_QUEUE, NULL);
554 case DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER:
555 case DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER:
557 case DBUS_REQUEST_NAME_REPLY_EXISTS:
558 case DBUS_REQUEST_NAME_REPLY_IN_QUEUE:
565 int jbus_read_write_dispatch(struct jbus *jbus, int toms)
567 if (dbus_connection_read_write_dispatch(jbus->connection, toms));
573 int jbus_call_ss(struct jbus *jbus, const char *method, const char *query, void (*onresp)(int, const char*, void*), void *data)
575 return call(jbus, method, query, onresp, NULL, data);
578 int jbus_call_sj(struct jbus *jbus, const char *method, const char *query, void (*onresp)(int, struct json_object*, void*), void *data)
580 return call(jbus, method, query, NULL, onresp, data);
583 int jbus_call_js(struct jbus *jbus, const char *method, struct json_object *query, void (*onresp)(int, const char*, void*), void *data)
585 const char *str = json_object_to_json_string(query);
590 return call(jbus, method, str, onresp, NULL, data);
593 int jbus_call_jj(struct jbus *jbus, const char *method, struct json_object *query, void (*onresp)(int, struct json_object*, void*), void *data)
595 const char *str = json_object_to_json_string(query);
600 return call(jbus, method, str, NULL, onresp, data);
603 char *jbus_call_ss_sync(struct jbus *jbus, const char *method, const char *query)
605 struct respsync synchro;
606 synchro.value = NULL;
607 synchro.replied = jbus_call_ss(jbus, method, query, sync_of_replies, &synchro);
608 while (!synchro.replied && !jbus_read_write_dispatch(jbus, -1));
609 return synchro.value;
612 struct json_object *jbus_call_sj_sync(struct jbus *jbus, const char *method, const char *query)
614 struct json_object *obj;
615 char *str = jbus_call_ss_sync(jbus, method, query);
619 obj = json_tokener_parse(str);
625 char *jbus_call_js_sync(struct jbus *jbus, const char *method, struct json_object *query)
627 const char *str = json_object_to_json_string(query);
632 return jbus_call_ss_sync(jbus, method, str);
635 struct json_object *jbus_call_jj_sync(struct jbus *jbus, const char *method, struct json_object *query)
637 const char *str = json_object_to_json_string(query);
642 return jbus_call_sj_sync(jbus, method, str);
645 int jbus_on_signal_s(struct jbus *jbus, const char *name, void (*onsig)(const char *))
647 return add_signal(jbus, name, onsig, NULL);
650 int jbus_on_signal_j(struct jbus *jbus, const char *name, void (*onsig)(struct json_object *))
652 return add_signal(jbus, name, NULL, onsig);
655 /************************** FEW LITTLE TESTS *****************************************/
661 void ping(struct jreq *jreq, struct json_object *request)
663 printf("ping(%s) -> %s\n",json_object_to_json_string(request),json_object_to_json_string(request));
664 jbus_reply_j(jreq, request);
665 json_object_put(request);
667 void incr(struct jreq *jreq, struct json_object *request)
669 static int counter = 0;
670 struct json_object *res = json_object_new_int(++counter);
671 printf("incr(%s) -> %s\n",json_object_to_json_string(request),json_object_to_json_string(res));
672 jbus_reply_j(jreq, res);
673 jbus_send_signal_j(jbus, "incremented", res);
674 json_object_put(res);
675 json_object_put(request);
680 jbus = create_jbus(1, "/bzh/iot/jdbus");
681 s1 = jbus_add_service_j(jbus, "ping", ping);
682 s2 = jbus_add_service_j(jbus, "incr", incr);
683 s3 = jbus_start_serving(jbus);
684 printf("started %d %d %d\n", s1, s2, s3);
685 while (!jbus_read_write_dispatch (jbus, -1))
693 void onresp(int status, struct json_object *response, void *data)
695 printf("resp: %d, %s, %s\n",status,(char*)data,json_object_to_json_string(response));
696 json_object_put(response);
698 void signaled(const char *data)
700 printf("signaled with {%s}\n", data);
705 jbus = create_jbus(1, "/bzh/iot/jdbus");
706 jbus_on_signal_s(jbus, "incremented", signaled);
708 jbus_call_sj(jbus, "ping", "{\"toto\":[1,2,3,4,true,\"toto\"]}", onresp, "ping");
709 jbus_call_sj(jbus, "incr", "{\"doit\":\"for-me\"}", onresp, "incr");
710 jbus_read_write_dispatch (jbus, 1);
712 printf("[[[%s]]]\n", jbus_call_ss_sync(jbus, "ping", "\"formidable!\""));
713 while (!jbus_read_write_dispatch (jbus, -1))