d58def90ca1cecdf057a476146bb8e905e6322d1
[src/app-framework-main.git] / src / utils-jbus.c
1 /*
2  Copyright 2015 IoT.bzh
3
4  author: José Bollo <jose.bollo@iot.bzh>
5
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
9
10      http://www.apache.org/licenses/LICENSE-2.0
11
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.
17 */
18 #define _GNU_SOURCE
19
20 #include <stdlib.h>
21 #include <stdio.h>
22 #include <errno.h>
23 #include <string.h>
24
25 #include <json.h>
26 #include <dbus/dbus.h>
27
28 #include "utils-jbus.h"
29
30 struct jreq;
31 struct jservice;
32 struct jbus;
33
34 /* structure for handled requests */
35 struct jreq {
36         DBusConnection *connection;
37         DBusMessage *request;
38 };
39
40 /* structure for recorded services */
41 struct jservice {
42         struct jservice *next;
43         char *method;
44         void (*oncall_s)(struct jreq *, const char *);
45         void (*oncall_j)(struct jreq *, struct json_object *);
46 };
47
48 /* structure for signal handlers */
49 struct jsignal {
50         struct jsignal *next;
51         char *name;
52         void (*onsignal_s)(const char *);
53         void (*onsignal_j)(struct json_object *);
54 };
55
56 /* structure for recording asynchronous requests */
57 struct jrespw {
58         struct jrespw *next;
59         dbus_uint32_t serial;
60         void *data;
61         void (*onresp_s)(int, const char*, void *);
62         void (*onresp_j)(int, struct json_object*, void *);
63 };
64
65 /* structure for synchronous requests */
66 struct respsync {
67         int replied;
68         char *value;
69 };
70
71 /* structure for handling either client or server jbus on dbus */
72 struct jbus {
73         int refcount;
74         struct jservice *services;
75         DBusConnection *connection;
76         struct jsignal *signals;
77         struct jrespw *waiters;
78         char *path;
79         char *name;
80 };
81
82 /*********************** STATIC COMMON METHODS *****************/
83
84 static inline void free_jreq(struct jreq *jreq)
85 {
86         dbus_message_unref(jreq->request);
87         dbus_connection_unref(jreq->connection);
88         free(jreq);
89 }
90
91 static inline int reply_out_of_memory(struct jreq *jreq)
92 {
93         static const char out_of_memory[] = "out of memory";
94         jbus_reply_error_s(jreq, out_of_memory);
95         errno = ENOMEM;
96         return -1;
97 }
98
99 static inline int reply_invalid_request(struct jreq *jreq)
100 {
101         static const char invalid_request[] = "invalid request";
102         jbus_reply_error_s(jreq, invalid_request);
103         return DBUS_HANDLER_RESULT_HANDLED;
104 }
105
106 static int matchitf(struct jbus *jbus, DBusMessage *message)
107 {
108         const char *itf = dbus_message_get_interface(message);
109         return itf != NULL && !strcmp(itf, jbus->name);
110 }
111
112 static int add_service(
113                 struct jbus *jbus,
114                 const char *method,
115                 void (*oncall_s)(struct jreq*, const char*),
116                 void (*oncall_j)(struct jreq*, struct json_object*)
117 )
118 {
119         struct jservice *srv;
120
121         /* allocation */
122         srv = malloc(sizeof * srv);
123         if (srv == NULL) {
124                 errno = ENOMEM;
125                 goto error;
126         }
127         srv->method = strdup(method);
128         if (!srv->method) {
129                 errno = ENOMEM;
130                 goto error2;
131         }
132
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;
138
139         return 0;
140
141 error2:
142         free(srv);
143 error:
144         return -1;
145 }
146
147 static int add_signal(
148         struct jbus *jbus,
149         const char *name,
150         void (*onsignal_s)(const char*),
151         void (*onsignal_j)(struct json_object*)
152 )
153 {
154         char *rule;
155         struct jsignal *sig;
156
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))
160                         return -1;
161                 dbus_bus_add_match(jbus->connection, rule, NULL);
162                 free(rule);
163         }
164
165         /* allocation */
166         sig = malloc(sizeof * sig);
167         if (sig == NULL)
168                 goto error;
169         sig->name = strdup(name);
170         if (!sig->name)
171                 goto error2;
172
173         /* record the signal */
174         sig->onsignal_s = onsignal_s;
175         sig->onsignal_j = onsignal_j;
176         sig->next = jbus->signals;
177         jbus->signals = sig;
178
179         return 0;
180
181 error2:
182         free(sig);
183 error:
184         errno = ENOMEM;
185         return -1;
186 }
187
188 static int call(
189         struct jbus *jbus,
190         const char *method,
191         const char *query,
192         void (*onresp_s)(int status, const char *response, void *data),
193         void (*onresp_j)(int status, struct json_object *response, void *data),
194         void *data
195 )
196 {
197         DBusMessage *msg;
198         struct jrespw *resp;
199
200         resp = malloc(sizeof * resp);
201         if (resp == NULL) {
202                 errno = ENOMEM;
203                 goto error;
204         }
205
206         msg = dbus_message_new_method_call(jbus->name, jbus->path, jbus->name, method);
207         if (msg == NULL) {
208                 errno = ENOMEM;
209                 goto error2;
210         }
211
212         if (!dbus_message_append_args(msg, DBUS_TYPE_STRING, &query, DBUS_TYPE_INVALID)) {
213                 errno = ENOMEM;
214                 goto error3;
215         }
216
217         if (!dbus_connection_send(jbus->connection, msg, &resp->serial)) {
218                 goto error3;
219         }
220
221         dbus_message_unref(msg);
222         resp->data = data;
223         resp->onresp_s = onresp_s;
224         resp->onresp_j = onresp_j;
225         resp->next = jbus->waiters;
226         jbus->waiters = resp;
227         return 0;
228
229 error3:
230         dbus_message_unref(msg);
231 error2:
232         free(resp);
233 error:
234         return -1;
235 }
236
237 static void sync_of_replies(int status, const char *value, void *data)
238 {
239         struct respsync *s = data;
240         s->value = status ? NULL : strdup(value ? value : "");
241         s->replied = 1;
242 }
243
244 static DBusHandlerResult incoming_resp(DBusConnection *connection, DBusMessage *message, struct jbus *jbus, int iserror)
245 {
246         int status;
247         const char *str;
248         struct jrespw *jrw, **prv;
249         struct json_object *reply;
250         dbus_uint32_t serial;
251
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)
256                 prv = &jrw->next;
257         if (jrw == NULL)
258                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
259         *prv = jrw->next;
260
261         /* retrieve the string value */
262         if (!dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &str, DBUS_TYPE_INVALID)) {
263                 status = -1;
264                 str = NULL;
265                 reply = NULL;
266         }
267
268         /* treat it */
269         if (jrw->onresp_s)
270                 jrw->onresp_s(iserror ? -1 : status, str, jrw->data);
271         else {
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);
276         }
277
278         free(jrw);
279         return DBUS_HANDLER_RESULT_HANDLED;
280 }
281
282 static DBusHandlerResult incoming_call(DBusConnection *connection, DBusMessage *message, struct jbus *jbus)
283 {
284         struct jservice *srv;
285         struct jreq *jreq;
286         const char *str;
287         const char *method;
288         struct json_object *query;
289
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);
294         if (method == NULL)
295                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
296         srv = jbus->services;
297         while(srv != NULL && strcmp(method, srv->method))
298                 srv = srv->next;
299         if (srv == NULL)
300                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
301
302         /* handle the message */
303         jreq = malloc(sizeof * jreq);
304         if (jreq == NULL)
305                 return DBUS_HANDLER_RESULT_NEED_MEMORY;
306         jreq->request = dbus_message_ref(message);
307         jreq->connection = dbus_connection_ref(jbus->connection);
308         
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);
312         if (srv->oncall_s) {
313                 /* handling strings only */
314                 srv->oncall_s(jreq, str);
315         }
316         else {
317                 /* handling json only */
318                 query = json_tokener_parse(str);
319                 if (query == NULL)
320                         return reply_invalid_request(jreq);
321                 srv->oncall_j(jreq, query);
322                 json_object_put(query);
323         }
324         return DBUS_HANDLER_RESULT_HANDLED;
325 }
326
327 static DBusHandlerResult incoming_signal(DBusConnection *connection, DBusMessage *message, struct jbus *jbus)
328 {
329         struct jsignal *sig;
330         const char *str;
331         const char *name;
332         struct json_object *obj;
333
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);
338         if (name == NULL)
339                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
340         sig = jbus->signals;
341         while(sig != NULL && strcmp(name, sig->name))
342                 sig = sig->next;
343         if (sig == NULL)
344                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
345
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);
351                 }
352                 else {
353                         /* handling json only */
354                         obj = json_tokener_parse(str);
355                         if (obj != NULL) {
356                                 sig->onsignal_j(obj);
357                                 json_object_put(obj);
358                         }
359                 }
360         }
361         return DBUS_HANDLER_RESULT_HANDLED;
362 }
363
364 static DBusHandlerResult incoming(DBusConnection *connection, DBusMessage *message, void *data)
365 {
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);
375         }
376         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
377 }
378
379 /************************** MAIN FUNCTIONS *****************************************/
380
381 struct jbus *create_jbus(int session, const char *path)
382 {
383         struct jbus *jbus;
384         char *name;
385
386         /* create the context and connect */
387         jbus = calloc(1, sizeof * jbus);
388         if (jbus == NULL) {
389                 errno = ENOMEM;
390                 goto error;
391         }
392         jbus->refcount = 1;
393         jbus->path = strdup(path);
394         jbus->name = NULL;
395         if (jbus->path == NULL) {
396                 errno = ENOMEM;
397                 goto error2;
398         }
399         while(*path == '/') path++;
400         jbus->name = name = strdup(path);
401         if (name == NULL) {
402                 errno = ENOMEM;
403                 goto error2;
404         }
405         while(*name) {
406                 if (*name == '/')
407                         *name = '.';
408                 name++;
409         }
410         name--;
411         while (name >= jbus->name && *name == '.')
412                 *name-- = 0;
413         if (!*jbus->name) {
414                 errno = EINVAL;
415                 goto error2;
416         }
417
418         /* connect */
419         jbus->connection = dbus_bus_get(session ? DBUS_BUS_SESSION : DBUS_BUS_SYSTEM, NULL);
420         if (jbus->connection == NULL) {
421                 goto error2;
422         }
423         if (!dbus_connection_add_filter(jbus->connection, incoming, jbus, NULL)) {
424                 goto error2;
425         }
426
427         return jbus;
428
429 error2:
430         jbus_unref(jbus);
431 error:
432         return NULL;
433 }
434
435 void jbus_addref(struct jbus *jbus)
436 {
437         jbus->refcount++;
438 }
439
440 void jbus_unref(struct jbus *jbus)
441 {
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;
447                         free(srv->method);
448                         free(srv);
449                 }
450                 free(jbus->name);
451                 free(jbus->path);
452                 free(jbus);
453         }
454 }
455
456 int jbus_reply_error_s(struct jreq *jreq, const char *error)
457 {
458         int rc = -1;
459         DBusMessage *message;
460
461         message = dbus_message_new_error(jreq->request, DBUS_ERROR_FAILED, error);
462         if (message == NULL)
463                 errno = ENOMEM;
464         else {
465                 if (dbus_connection_send(jreq->connection, message, NULL))
466                         rc = 0;
467                 dbus_message_unref(message);
468         }
469         free_jreq(jreq);
470         return rc;
471 }
472
473 int jbus_reply_error_j(struct jreq *jreq, struct json_object *reply)
474 {
475         const char *str = json_object_to_json_string(reply);
476         return str ? jbus_reply_error_s(jreq, str) : reply_out_of_memory(jreq);
477 }
478
479 int jbus_reply_s(struct jreq *jreq, const char *reply)
480 {
481         int rc = -1;
482         DBusMessage *message;
483
484         message = dbus_message_new_method_return(jreq->request);
485         if (message == NULL)
486                 return reply_out_of_memory(jreq);
487
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);
491         }
492
493         if (dbus_connection_send(jreq->connection, message, NULL))
494                 rc = 0;
495         dbus_message_unref(message);
496         free_jreq(jreq);
497         return rc;
498 }
499
500 int jbus_reply_j(struct jreq *jreq, struct json_object *reply)
501 {
502         const char *str = json_object_to_json_string(reply);
503         return str ? jbus_reply_s(jreq, str) : reply_out_of_memory(jreq);
504 }
505
506 int jbus_send_signal_s(struct jbus *jbus, const char *name, const char *content)
507 {
508         int rc = -1;
509         DBusMessage *message;
510
511         message = dbus_message_new_signal(jbus->path, jbus->name, name);
512         if (message == NULL)
513                 goto error;
514
515         if (!dbus_message_append_args(message, DBUS_TYPE_STRING, &content, DBUS_TYPE_INVALID)) {
516                 dbus_message_unref(message);
517                 goto error;
518         }
519
520         if (dbus_connection_send(jbus->connection, message, NULL))
521                 rc = 0;
522         dbus_message_unref(message);
523         return rc;
524
525 error:
526         errno = ENOMEM;
527         return -1;
528 }
529
530 int jbus_send_signal_j(struct jbus *jbus, const char *name, struct json_object *content)
531 {
532         const char *str = json_object_to_json_string(content);
533         if (str == NULL) {
534                 errno = ENOMEM;
535                 return -1;
536         }
537         return jbus_send_signal_s(jbus, name, str);
538 }
539
540 int jbus_add_service_s(struct jbus *jbus, const char *method, void (*oncall)(struct jreq *, const char *))
541 {
542         return add_service(jbus, method, oncall, NULL);
543 }
544
545 int jbus_add_service_j(struct jbus *jbus, const char *method, void (*oncall)(struct jreq *, struct json_object *))
546 {
547         return add_service(jbus, method, NULL, oncall);
548 }
549
550 int jbus_start_serving(struct jbus *jbus)
551 {
552         int status = dbus_bus_request_name(jbus->connection, jbus->name, DBUS_NAME_FLAG_DO_NOT_QUEUE, NULL);
553         switch (status) {
554         case DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER:
555         case DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER:
556                 return 0;
557         case DBUS_REQUEST_NAME_REPLY_EXISTS:
558         case DBUS_REQUEST_NAME_REPLY_IN_QUEUE:
559         default:
560                 errno = EADDRINUSE;
561                 return -1;
562         }
563 }
564
565 int jbus_read_write_dispatch(struct jbus *jbus, int toms)
566 {
567         if (dbus_connection_read_write_dispatch(jbus->connection, toms));
568                 return 0;
569         errno = EPIPE;
570         return -1;
571 }
572
573 int jbus_call_ss(struct jbus *jbus, const char *method, const char *query, void (*onresp)(int, const char*, void*), void *data)
574 {
575         return call(jbus, method, query, onresp, NULL, data);
576 }
577
578 int jbus_call_sj(struct jbus *jbus, const char *method, const char *query, void (*onresp)(int, struct json_object*, void*), void *data)
579 {
580         return call(jbus, method, query, NULL, onresp, data);
581 }
582
583 int jbus_call_js(struct jbus *jbus, const char *method, struct json_object *query, void (*onresp)(int, const char*, void*), void *data)
584 {
585         const char *str = json_object_to_json_string(query);
586         if (str == NULL) {
587                 errno = ENOMEM;
588                 return -1;
589         }
590         return call(jbus, method, str, onresp, NULL, data);
591 }
592
593 int jbus_call_jj(struct jbus *jbus, const char *method, struct json_object *query, void (*onresp)(int, struct json_object*, void*), void *data)
594 {
595         const char *str = json_object_to_json_string(query);
596         if (str == NULL) {
597                 errno = ENOMEM;
598                 return -1;
599         }
600         return call(jbus, method, str, NULL, onresp, data);
601 }
602
603 char *jbus_call_ss_sync(struct jbus *jbus, const char *method, const char *query)
604 {
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;
610 }
611
612 struct json_object *jbus_call_sj_sync(struct jbus *jbus, const char *method, const char *query)
613 {
614         struct json_object *obj;
615         char *str = jbus_call_ss_sync(jbus, method, query);
616         if (str == NULL)
617                 obj = NULL;
618         else {
619                 obj = json_tokener_parse(str);
620                 free(str);
621         }
622         return obj;
623 }
624
625 char *jbus_call_js_sync(struct jbus *jbus, const char *method, struct json_object *query)
626 {
627         const char *str = json_object_to_json_string(query);
628         if (str == NULL) {
629                 errno = ENOMEM;
630                 return NULL;
631         }
632         return jbus_call_ss_sync(jbus, method, str);
633 }
634
635 struct json_object *jbus_call_jj_sync(struct jbus *jbus, const char *method, struct json_object *query)
636 {
637         const char *str = json_object_to_json_string(query);
638         if (str == NULL) {
639                 errno = ENOMEM;
640                 return NULL;
641         }
642         return jbus_call_sj_sync(jbus, method, str);
643 }
644
645 int jbus_on_signal_s(struct jbus *jbus, const char *name, void (*onsig)(const char *))
646 {
647         return add_signal(jbus, name, onsig, NULL);
648 }
649
650 int jbus_on_signal_j(struct jbus *jbus, const char *name, void (*onsig)(struct json_object *))
651 {
652         return add_signal(jbus, name, NULL, onsig);
653 }
654
655 /************************** FEW LITTLE TESTS *****************************************/
656
657 #ifdef SERVER
658 #include <stdio.h>
659 #include <unistd.h>
660 struct jbus *jbus;
661 void ping(struct jreq *jreq, struct json_object *request)
662 {
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);       
666 }
667 void incr(struct jreq *jreq, struct json_object *request)
668 {
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);
676 }
677 int main()
678 {
679         int s1, s2, s3;
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))
686                 ;
687 }
688 #endif
689 #ifdef CLIENT
690 #include <stdio.h>
691 #include <unistd.h>
692 struct jbus *jbus;
693 void onresp(int status, struct json_object *response, void *data)
694 {
695         printf("resp: %d, %s, %s\n",status,(char*)data,json_object_to_json_string(response));
696         json_object_put(response);
697 }
698 void signaled(const char *data)
699 {
700         printf("signaled with {%s}\n", data);
701 }
702 int main()
703 {
704         int i = 10;
705         jbus = create_jbus(1, "/bzh/iot/jdbus");
706         jbus_on_signal_s(jbus, "incremented", signaled);
707         while(i--) {
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);
711         }
712         printf("[[[%s]]]\n", jbus_call_ss_sync(jbus, "ping", "\"formidable!\""));
713         while (!jbus_read_write_dispatch (jbus, -1))
714                 ;
715 }
716 #endif
717
718
719
720
721
722
723
724