7e65723a2b2142690277fbbb0dfc94156dbe84d4
[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         }
276
277         free(jrw);
278         return DBUS_HANDLER_RESULT_HANDLED;
279 }
280
281 static DBusHandlerResult incoming_call(DBusConnection *connection, DBusMessage *message, struct jbus *jbus)
282 {
283         struct jservice *srv;
284         struct jreq *jreq;
285         const char *str;
286         const char *method;
287         struct json_object *query;
288
289         /* search for the service */
290         if (!matchitf(jbus, message))
291                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
292         method = dbus_message_get_member(message);
293         if (method == NULL)
294                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
295         srv = jbus->services;
296         while(srv != NULL && strcmp(method, srv->method))
297                 srv = srv->next;
298         if (srv == NULL)
299                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
300
301         /* handle the message */
302         jreq = malloc(sizeof * jreq);
303         if (jreq == NULL)
304                 return DBUS_HANDLER_RESULT_NEED_MEMORY;
305         jreq->request = dbus_message_ref(message);
306         jreq->connection = dbus_connection_ref(jbus->connection);
307         
308         /* retrieve the string value */
309         if (!dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &str, DBUS_TYPE_INVALID))
310                 return reply_invalid_request(jreq);
311         if (srv->oncall_s) {
312                 /* handling strings only */
313                 srv->oncall_s(jreq, str);
314         }
315         else {
316                 /* handling json only */
317                 query = json_tokener_parse(str);
318                 if (query == NULL)
319                         return reply_invalid_request(jreq);
320                 srv->oncall_j(jreq, query);
321         }
322         return DBUS_HANDLER_RESULT_HANDLED;
323 }
324
325 static DBusHandlerResult incoming_signal(DBusConnection *connection, DBusMessage *message, struct jbus *jbus)
326 {
327         struct jsignal *sig;
328         const char *str;
329         const char *name;
330         struct json_object *obj;
331
332         /* search for the service */
333         if (!matchitf(jbus, message))
334                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
335         name = dbus_message_get_member(message);
336         if (name == NULL)
337                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
338         sig = jbus->signals;
339         while(sig != NULL && strcmp(name, sig->name))
340                 sig = sig->next;
341         if (sig == NULL)
342                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
343
344         /* retrieve the string value */
345         if (dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &str, DBUS_TYPE_INVALID)) {
346                 if (sig->onsignal_s) {
347                         /* handling strings only */
348                         sig->onsignal_s(str);
349                 }
350                 else {
351                         /* handling json only */
352                         obj = json_tokener_parse(str);
353                         if (obj != NULL)
354                                 sig->onsignal_j(obj);
355                 }
356         }
357         return DBUS_HANDLER_RESULT_HANDLED;
358 }
359
360 static DBusHandlerResult incoming(DBusConnection *connection, DBusMessage *message, void *data)
361 {
362         switch(dbus_message_get_type(message)) {
363         case DBUS_MESSAGE_TYPE_METHOD_CALL:
364                 return incoming_call(connection, message, (struct jbus*)data);
365         case DBUS_MESSAGE_TYPE_METHOD_RETURN:
366                 return incoming_resp(connection, message, (struct jbus*)data, 0);
367         case DBUS_MESSAGE_TYPE_ERROR:
368                 return incoming_resp(connection, message, (struct jbus*)data, 1);
369         case DBUS_MESSAGE_TYPE_SIGNAL:
370                 return incoming_signal(connection, message, (struct jbus*)data);
371         }
372         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
373 }
374
375 /************************** MAIN FUNCTIONS *****************************************/
376
377 struct jbus *create_jbus(int session, const char *path)
378 {
379         struct jbus *jbus;
380         char *name;
381
382         /* create the context and connect */
383         jbus = calloc(1, sizeof * jbus);
384         if (jbus == NULL) {
385                 errno = ENOMEM;
386                 goto error;
387         }
388         jbus->refcount = 1;
389         jbus->path = strdup(path);
390         jbus->name = NULL;
391         if (jbus->path == NULL) {
392                 errno = ENOMEM;
393                 goto error2;
394         }
395         while(*path == '/') path++;
396         jbus->name = name = strdup(path);
397         if (name == NULL) {
398                 errno = ENOMEM;
399                 goto error2;
400         }
401         while(*name) {
402                 if (*name == '/')
403                         *name = '.';
404                 name++;
405         }
406         name--;
407         while (name >= jbus->name && *name == '.')
408                 *name-- = 0;
409         if (!*jbus->name) {
410                 errno = EINVAL;
411                 goto error2;
412         }
413
414         /* connect */
415         jbus->connection = dbus_bus_get(session ? DBUS_BUS_SESSION : DBUS_BUS_SYSTEM, NULL);
416         if (jbus->connection == NULL) {
417                 goto error2;
418         }
419         if (!dbus_connection_add_filter(jbus->connection, incoming, jbus, NULL)) {
420                 goto error2;
421         }
422
423         return jbus;
424
425 error2:
426         jbus_unref(jbus);
427 error:
428         return NULL;
429 }
430
431 void jbus_addref(struct jbus *jbus)
432 {
433         jbus->refcount++;
434 }
435
436 void jbus_unref(struct jbus *jbus)
437 {
438         struct jservice *srv;
439         if (!--jbus->refcount) {
440                 dbus_connection_unref(jbus->connection);
441                 while((srv = jbus->services) != NULL) {
442                         jbus->services = srv->next;
443                         free(srv->method);
444                         free(srv);
445                 }
446                 free(jbus->name);
447                 free(jbus->path);
448                 free(jbus);
449         }
450 }
451
452 int jbus_reply_error_s(struct jreq *jreq, const char *error)
453 {
454         int rc = -1;
455         DBusMessage *message;
456
457         message = dbus_message_new_error(jreq->request, DBUS_ERROR_FAILED, error);
458         if (message == NULL)
459                 errno = ENOMEM;
460         else {
461                 if (dbus_connection_send(jreq->connection, message, NULL))
462                         rc = 0;
463                 dbus_message_unref(message);
464         }
465         free_jreq(jreq);
466         return rc;
467 }
468
469 int jbus_reply_error_j(struct jreq *jreq, struct json_object *reply)
470 {
471         const char *str = json_object_to_json_string(reply);
472         return str ? jbus_reply_error_s(jreq, str) : reply_out_of_memory(jreq);
473 }
474
475 int jbus_reply_s(struct jreq *jreq, const char *reply)
476 {
477         int rc = -1;
478         DBusMessage *message;
479
480         message = dbus_message_new_method_return(jreq->request);
481         if (message == NULL)
482                 return reply_out_of_memory(jreq);
483
484         if (!dbus_message_append_args(message, DBUS_TYPE_STRING, &reply, DBUS_TYPE_INVALID)) {
485                 dbus_message_unref(message);
486                 return reply_out_of_memory(jreq);
487         }
488
489         if (dbus_connection_send(jreq->connection, message, NULL))
490                 rc = 0;
491         dbus_message_unref(message);
492         free_jreq(jreq);
493         return rc;
494 }
495
496 int jbus_reply_j(struct jreq *jreq, struct json_object *reply)
497 {
498         const char *str = json_object_to_json_string(reply);
499         return str ? jbus_reply_s(jreq, str) : reply_out_of_memory(jreq);
500 }
501
502 int jbus_send_signal_s(struct jbus *jbus, const char *name, const char *content)
503 {
504         int rc = -1;
505         DBusMessage *message;
506
507         message = dbus_message_new_signal(jbus->path, jbus->name, name);
508         if (message == NULL)
509                 goto error;
510
511         if (!dbus_message_append_args(message, DBUS_TYPE_STRING, &content, DBUS_TYPE_INVALID)) {
512                 dbus_message_unref(message);
513                 goto error;
514         }
515
516         if (dbus_connection_send(jbus->connection, message, NULL))
517                 rc = 0;
518         dbus_message_unref(message);
519         return rc;
520
521 error:
522         errno = ENOMEM;
523         return -1;
524 }
525
526 int jbus_send_signal_j(struct jbus *jbus, const char *name, struct json_object *content)
527 {
528         const char *str = json_object_to_json_string(content);
529         if (str == NULL) {
530                 errno = ENOMEM;
531                 return -1;
532         }
533         return jbus_send_signal_s(jbus, name, str);
534 }
535
536 int jbus_add_service_s(struct jbus *jbus, const char *method, void (*oncall)(struct jreq *, const char *))
537 {
538         return add_service(jbus, method, oncall, NULL);
539 }
540
541 int jbus_add_service_j(struct jbus *jbus, const char *method, void (*oncall)(struct jreq *, struct json_object *))
542 {
543         return add_service(jbus, method, NULL, oncall);
544 }
545
546 int jbus_start_serving(struct jbus *jbus)
547 {
548         int status = dbus_bus_request_name(jbus->connection, jbus->name, DBUS_NAME_FLAG_DO_NOT_QUEUE, NULL);
549         switch (status) {
550         case DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER:
551         case DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER:
552                 return 0;
553         case DBUS_REQUEST_NAME_REPLY_EXISTS:
554         case DBUS_REQUEST_NAME_REPLY_IN_QUEUE:
555         default:
556                 errno = EADDRINUSE;
557                 return -1;
558         }
559 }
560
561 int jbus_read_write_dispatch(struct jbus *jbus, int toms)
562 {
563         if (dbus_connection_read_write_dispatch(jbus->connection, toms));
564                 return 0;
565         errno = EPIPE;
566         return -1;
567 }
568
569 int jbus_call_ss(struct jbus *jbus, const char *method, const char *query, void (*onresp)(int, const char*, void*), void *data)
570 {
571         return call(jbus, method, query, onresp, NULL, data);
572 }
573
574 int jbus_call_sj(struct jbus *jbus, const char *method, const char *query, void (*onresp)(int, struct json_object*, void*), void *data)
575 {
576         return call(jbus, method, query, NULL, onresp, data);
577 }
578
579 int jbus_call_js(struct jbus *jbus, const char *method, struct json_object *query, void (*onresp)(int, const char*, void*), void *data)
580 {
581         const char *str = json_object_to_json_string(query);
582         if (str == NULL) {
583                 errno = ENOMEM;
584                 return -1;
585         }
586         return call(jbus, method, str, onresp, NULL, data);
587 }
588
589 int jbus_call_jj(struct jbus *jbus, const char *method, struct json_object *query, void (*onresp)(int, struct json_object*, void*), void *data)
590 {
591         const char *str = json_object_to_json_string(query);
592         if (str == NULL) {
593                 errno = ENOMEM;
594                 return -1;
595         }
596         return call(jbus, method, str, NULL, onresp, data);
597 }
598
599 char *jbus_call_ss_sync(struct jbus *jbus, const char *method, const char *query)
600 {
601         struct respsync synchro;
602         synchro.value = NULL;
603         synchro.replied = jbus_call_ss(jbus, method, query, sync_of_replies, &synchro);
604         while (!synchro.replied && !jbus_read_write_dispatch(jbus, -1));
605         return synchro.value;
606 }
607
608 struct json_object *jbus_call_sj_sync(struct jbus *jbus, const char *method, const char *query)
609 {
610         const char *str = jbus_call_ss_sync(jbus, method, query);
611         return str ? json_tokener_parse(str) : NULL;
612 }
613
614
615 char *jbus_call_js_sync(struct jbus *jbus, const char *method, struct json_object *query)
616 {
617         const char *str = json_object_to_json_string(query);
618         if (str == NULL) {
619                 errno = ENOMEM;
620                 return NULL;
621         }
622         return jbus_call_ss_sync(jbus, method, str);
623 }
624
625 struct json_object *jbus_call_jj_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_sj_sync(jbus, method, str);
633 }
634
635 int jbus_on_signal_s(struct jbus *jbus, const char *name, void (*onsig)(const char *))
636 {
637         return add_signal(jbus, name, onsig, NULL);
638 }
639
640 int jbus_on_signal_j(struct jbus *jbus, const char *name, void (*onsig)(struct json_object *))
641 {
642         return add_signal(jbus, name, NULL, onsig);
643 }
644
645 /************************** FEW LITTLE TESTS *****************************************/
646
647 #ifdef SERVER
648 #include <stdio.h>
649 #include <unistd.h>
650 struct jbus *jbus;
651 void ping(struct jreq *jreq, struct json_object *request)
652 {
653 printf("ping(%s) -> %s\n",json_object_to_json_string(request),json_object_to_json_string(request));
654         jbus_reply_j(jreq, request);
655         json_object_put(request);       
656 }
657 void incr(struct jreq *jreq, struct json_object *request)
658 {
659         static int counter = 0;
660         struct json_object *res = json_object_new_int(++counter);
661 printf("incr(%s) -> %s\n",json_object_to_json_string(request),json_object_to_json_string(res));
662         jbus_reply_j(jreq, res);
663 jbus_send_signal_j(jbus, "incremented", res);
664         json_object_put(res);
665         json_object_put(request);
666 }
667 int main()
668 {
669         int s1, s2, s3;
670         jbus = create_jbus(1, "/bzh/iot/jdbus");
671         s1 = jbus_add_service_j(jbus, "ping", ping);
672         s2 = jbus_add_service_j(jbus, "incr", incr);
673         s3 = jbus_start_serving(jbus);
674         printf("started %d %d %d\n", s1, s2, s3);
675         while (!jbus_read_write_dispatch (jbus, -1))
676                 ;
677 }
678 #endif
679 #ifdef CLIENT
680 #include <stdio.h>
681 #include <unistd.h>
682 struct jbus *jbus;
683 void onresp(int status, struct json_object *response, void *data)
684 {
685         printf("resp: %d, %s, %s\n",status,(char*)data,json_object_to_json_string(response));
686         json_object_put(response);
687 }
688 void signaled(const char *data)
689 {
690         printf("signaled with {%s}\n", data);
691 }
692 int main()
693 {
694         int i = 10;
695         jbus = create_jbus(1, "/bzh/iot/jdbus");
696         jbus_on_signal_s(jbus, "incremented", signaled);
697         while(i--) {
698                 jbus_call_sj(jbus, "ping", "{\"toto\":[1,2,3,4,true,\"toto\"]}", onresp, "ping");
699                 jbus_call_sj(jbus, "incr", "{\"doit\":\"for-me\"}", onresp, "incr");
700                 jbus_read_write_dispatch (jbus, 1);
701         }
702         printf("[[[%s]]]\n", jbus_call_ss_sync(jbus, "ping", "\"formidable!\""));
703         while (!jbus_read_write_dispatch (jbus, -1))
704                 ;
705 }
706 #endif
707
708
709
710
711
712
713
714