compact formatting of json
[src/app-framework-binder.git] / src / afb-api-dbus.c
1 /* 
2  * Copyright (C) 2015, 2016 "IoT.bzh"
3  * Author José Bollo <jose.bollo@iot.bzh>
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *   http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 #define _GNU_SOURCE
19 #define NO_PLUGIN_VERBOSE_MACRO
20
21 #include <stdlib.h>
22 #include <string.h>
23 #include <assert.h>
24 #include <errno.h>
25
26 #include <systemd/sd-bus.h>
27 #include <json-c/json.h>
28
29 #include <afb/afb-plugin.h>
30 #include <afb/afb-req-itf.h>
31
32 #include "afb-common.h"
33
34 #include "session.h"
35 #include "afb-apis.h"
36 #include "afb-api-so.h"
37 #include "afb-context.h"
38 #include "verbose.h"
39
40 static const char DEFAULT_PATH_PREFIX[] = "/org/agl/afb/api/";
41
42 /*
43  * The path given are of the form
44  *     system:/org/agl/afb/api/...
45  * or
46  *     user:/org/agl/afb/api/...
47  */
48 struct api_dbus
49 {
50         struct sd_bus *sdbus;   /* the bus */
51         struct sd_bus_slot *slot; /* the slot */
52         char *path;             /* path of the object for the API */
53         char *name;             /* name/interface of the object */
54         char *api;              /* api name of the interface */
55 };
56
57 #define RETOK   1
58 #define RETERR  2
59 #define RETRAW  3
60
61 /******************* common part **********************************/
62
63 /*
64  * create a structure api_dbus connected on either the system
65  * bus if 'system' is not null or on the user bus. The connection
66  * is established for either emiting/receiving on 'path' being of length
67  * 'pathlen'.
68  */
69 static struct api_dbus *make_api_dbus_3(int system, const char *path, size_t pathlen)
70 {
71         struct api_dbus *api;
72         struct sd_bus *sdbus;
73         char *ptr;
74
75         /* allocates the structure */
76         api = calloc(1, sizeof *api + 1 + pathlen + pathlen);
77         if (api == NULL) {
78                 errno = ENOMEM;
79                 goto error;
80         }
81
82         /* init the structure's strings */
83
84         /* path is copied after the struct */
85         api->path = (void*)(api+1);
86         strcpy(api->path, path);
87
88         /* api name is at the end of the path */
89         api->api = strrchr(api->path, '/');
90         if (api->api == NULL) {
91                 errno = EINVAL;
92                 goto error2;
93         }
94         api->api++;
95         if (!afb_apis_is_valid_api_name(api->api)) {
96                 errno = EINVAL;
97                 goto error2;
98         }
99
100         /* the name/interface is copied after the path */
101         api->name = &api->path[pathlen + 1];
102         strcpy(api->name, &path[1]);
103         ptr = strchr(api->name, '/');
104         while(ptr != NULL) {
105                 *ptr = '.';
106                 ptr = strchr(ptr, '/');
107         }
108
109         /* choose the bus */
110         sdbus = (system ? afb_common_get_system_bus : afb_common_get_user_bus)();
111         if (sdbus == NULL)
112                 goto error2;
113
114         api->sdbus = sdbus;
115         return api;
116
117 error2:
118         free(api);
119 error:
120         return NULL;
121 }
122
123 /*
124  * create a structure api_dbus connected on either the system
125  * bus if 'system' is not null or on the user bus. The connection
126  * is established for either emiting/receiving on 'path'.
127  * If 'path' is not absolute, it is prefixed with DEFAULT_PATH_PREFIX.
128  */
129 static struct api_dbus *make_api_dbus_2(int system, const char *path)
130 {
131         size_t len;
132         char *ptr;
133
134         /* check the length of the path */
135         len = strlen(path);
136         if (len == 0) {
137                 errno = EINVAL;
138                 return NULL;
139         }
140
141         /* if the path is absolute, creation now */
142         if (path[0] == '/')
143                 return make_api_dbus_3(system, path, len);
144
145         /* compute the path prefixed with DEFAULT_PATH_PREFIX */
146         assert(strlen(DEFAULT_PATH_PREFIX) > 0);
147         assert(DEFAULT_PATH_PREFIX[strlen(DEFAULT_PATH_PREFIX) - 1] == '/');
148         len += strlen(DEFAULT_PATH_PREFIX);
149         ptr = alloca(len + 1);
150         strcpy(stpcpy(ptr, DEFAULT_PATH_PREFIX), path);
151
152         /* creation for prefixed path */
153         return make_api_dbus_3(system, ptr, len);
154 }
155
156 /*
157  * create a structure api_dbus connected either emiting/receiving
158  * on 'path'.
159  * The path can be prefixed with "system:" or "user:" to select
160  * either the user or the system D-Bus. If none is set then user's
161  * bus is selected.
162  * If remaining 'path' is not absolute, it is prefixed with
163  * DEFAULT_PATH_PREFIX.
164  */
165 static struct api_dbus *make_api_dbus(const char *path)
166 {
167         const char *ptr;
168         size_t preflen;
169
170         /* retrieves the prefix "scheme-like" part */
171         ptr = strchr(path, ':');
172         if (ptr == NULL)
173                 return make_api_dbus_2(0, path);
174
175         /* check the prefix part */
176         preflen = (size_t)(ptr - path);
177         if (strncmp(path, "system", preflen) == 0)
178                 return make_api_dbus_2(1, ptr + 1);
179
180         if (strncmp(path, "user", preflen) == 0)
181                 return make_api_dbus_2(0, ptr + 1);
182
183         /* TODO: connect to a foreign D-Bus? */
184         errno = EINVAL;
185         return NULL;
186 }
187
188 static void destroy_api_dbus(struct api_dbus *api)
189 {
190         free(api);
191 }
192
193 /******************* client part **********************************/
194
195 /*
196  * structure for recording query data
197  */
198 struct dbus_memo {
199         struct afb_req req;             /* the request handle */
200         struct afb_context *context;    /* the context of the query */
201 };
202
203 /* allocates and init the memorizing data */
204 static struct dbus_memo *api_dbus_client_make_memo(struct afb_req req, struct afb_context *context)
205 {
206         struct dbus_memo *memo;
207
208         memo = malloc(sizeof *memo);
209         if (memo != NULL) {
210                 afb_req_addref(req);
211                 memo->req = req;
212                 memo->context = context;
213         }
214         return memo;
215 }
216
217 /* free and release the memorizing data */
218 static void api_dbus_client_free_memo(struct dbus_memo *memo)
219 {
220         afb_req_unref(memo->req);
221         free(memo);
222 }
223
224 /* callback when received answer */
225 static int api_dbus_client_on_reply(sd_bus_message *message, void *userdata, sd_bus_error *ret_error)
226 {
227         int rc;
228         struct dbus_memo *memo;
229         const char *first, *second;
230         uint8_t type;
231         uint32_t flags;
232
233         /* retrieve the recorded data */
234         memo = userdata;
235
236         /* get the answer */
237         rc = sd_bus_message_read(message, "yssu", &type, &first, &second, &flags);
238         if (rc < 0) {
239                 /* failing to have the answer */
240                 afb_req_fail(memo->req, "error", "dbus error");
241         } else {
242                 /* report the answer */
243                 memo->context->flags = (unsigned)flags;
244                 switch(type) {
245                 case RETOK:
246                         afb_req_success(memo->req, json_tokener_parse(first), second);
247                         break;
248                 case RETERR:
249                         afb_req_fail(memo->req, first, second);
250                         break;
251                 case RETRAW:
252                         afb_req_send(memo->req, first, strlen(first));
253                         break;
254                 default:
255                         afb_req_fail(memo->req, "error", "dbus link broken");
256                         break;
257                 }
258         }
259         api_dbus_client_free_memo(memo);
260         return 1;
261 }
262
263 /* on call, propagate it to the dbus service */
264 static void api_dbus_client_call(struct api_dbus *api, struct afb_req req, struct afb_context *context, const char *verb, size_t lenverb)
265 {
266         size_t size;
267         int rc;
268         char *method = strndupa(verb, lenverb);
269         struct dbus_memo *memo;
270
271         /* create the recording data */
272         memo = api_dbus_client_make_memo(req, context);
273         if (memo == NULL) {
274                 afb_req_fail(req, "error", "out of memory");
275                 return;
276         }
277
278         /* makes the call */
279         rc = sd_bus_call_method_async(api->sdbus, NULL,
280                 api->name, api->path, api->name, method,
281                 api_dbus_client_on_reply, memo,
282                 "ssu",
283                         afb_req_raw(req, &size),
284                         ctxClientGetUuid(context->session),
285                         (uint32_t)context->flags);
286
287         /* if there was an error report it directly */
288         if (rc < 0) {
289                 errno = -rc;
290                 afb_req_fail(req, "error", "dbus error");
291                 api_dbus_client_free_memo(memo);
292         }
293 }
294
295 /* receives events */
296 static int api_dbus_client_on_event(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
297 {
298         struct json_object *object;
299         const char *event, *data;
300         int rc = sd_bus_message_read(m, "ss", &event, &data);
301         if (rc < 0)
302                 ERROR("unreadable event");
303         else {
304                 object = json_tokener_parse(data);
305                 ctxClientEventSend(NULL, event, object);
306                 json_object_put(object);
307         }
308         return 1;
309 }
310
311 /* adds a afb-dbus-service client api */
312 int afb_api_dbus_add_client(const char *path)
313 {
314         int rc;
315         struct api_dbus *api;
316         struct afb_api afb_api;
317         char *match;
318
319         /* create the dbus client api */
320         api = make_api_dbus(path);
321         if (api == NULL)
322                 goto error;
323
324         /* connect to events */
325         rc = asprintf(&match, "type='signal',path='%s',interface='%s',member='event'", api->path, api->name);
326         if (rc < 0) {
327                 errno = ENOMEM;
328                 ERROR("out of memory");
329                 goto error;
330         }
331         rc = sd_bus_add_match(api->sdbus, &api->slot, match, api_dbus_client_on_event, api);
332         free(match);
333         if (rc < 0) {
334                 errno = -rc;
335                 ERROR("can't add dbus object %s for %s", api->path, api->name);
336                 goto error;
337         }
338
339         /* record it as an API */
340         afb_api.closure = api;
341         afb_api.call = (void*)api_dbus_client_call;
342         if (afb_apis_add(api->api, afb_api) < 0)
343                 goto error2;
344
345         return 0;
346
347 error2:
348         destroy_api_dbus(api);
349 error:
350         return -1;
351 }
352
353 /******************* dbus request part for server *****************/
354
355 /*
356  * structure for a dbus request
357  */
358 struct dbus_req {
359         struct afb_context context;     /* the context, should be THE FIRST */
360         sd_bus_message *message;        /* the incoming request message */
361         const char *request;            /* the readen request as string */
362         struct json_object *json;       /* the readen request as object */
363         int refcount;                   /* reference count of the request */
364 };
365
366 /* increment the reference count of the request */
367 static void dbus_req_addref(struct dbus_req *dreq)
368 {
369         dreq->refcount++;
370 }
371
372 /* decrement the reference count of the request and free/release it on falling to null */
373 static void dbus_req_unref(struct dbus_req *dreq)
374 {
375         if (dreq == NULL || --dreq->refcount)
376                 return;
377
378         afb_context_disconnect(&dreq->context);
379         json_object_put(dreq->json);
380         sd_bus_message_unref(dreq->message);
381         free(dreq);
382 }
383
384 /* get the object of the request */
385 static struct json_object *dbus_req_json(struct dbus_req *dreq)
386 {
387         if (dreq->json == NULL) {
388                 dreq->json = json_tokener_parse(dreq->request);
389                 if (dreq->json == NULL) {
390                         /* lazy error detection of json request. Is it to improve? */
391                         dreq->json = json_object_new_string(dreq->request);
392                 }
393         }
394         return dreq->json;
395 }
396
397 /* get the argument of the request of 'name' */
398 static struct afb_arg dbus_req_get(struct dbus_req *dreq, const char *name)
399 {
400         struct afb_arg arg;
401         struct json_object *value, *root;
402
403         root = dbus_req_json(dreq);
404         if (root != NULL && json_object_object_get_ex(root, name, &value)) {
405                 arg.name = name;
406                 arg.value = json_object_get_string(value);
407         } else {
408                 arg.name = NULL;
409                 arg.value = NULL;
410         }
411         arg.path = NULL;
412         return arg;
413 }
414
415 static void dbus_req_reply(struct dbus_req *dreq, uint8_t type, const char *first, const char *second)
416 {
417         int rc;
418         rc = sd_bus_reply_method_return(dreq->message,
419                         "yssu", type, first, second, (uint32_t)dreq->context.flags);
420         if (rc < 0)
421                 ERROR("sending the reply failed");
422 }
423
424 static void dbus_req_success(struct dbus_req *dreq, struct json_object *obj, const char *info)
425 {
426         dbus_req_reply(dreq, RETOK, json_object_to_json_string_ext(obj, JSON_C_TO_STRING_PLAIN), info);
427 }
428
429 static void dbus_req_fail(struct dbus_req *dreq, const char *status, const char *info)
430 {
431         dbus_req_reply(dreq, RETERR, status, info);
432 }
433
434 static const char *dbus_req_raw(struct dbus_req *dreq, size_t *size)
435 {
436         if (size != NULL)
437                 *size = strlen(dreq->request);
438         return dreq->request;
439 }
440
441 static void dbus_req_send(struct dbus_req *dreq, const char *buffer, size_t size)
442 {
443         /* TODO: how to put sized buffer as strings? things aren't clear here!!! */
444         dbus_req_reply(dreq, RETRAW, buffer, "");
445 }
446
447 struct afb_req_itf dbus_req_itf = {
448         .json = (void*)dbus_req_json,
449         .get = (void*)dbus_req_get,
450         .success = (void*)dbus_req_success,
451         .fail = (void*)dbus_req_fail,
452         .raw = (void*)dbus_req_raw,
453         .send = (void*)dbus_req_send,
454         .context_get = (void*)afb_context_get,
455         .context_set = (void*)afb_context_set,
456         .addref = (void*)dbus_req_addref,
457         .unref = (void*)dbus_req_unref
458 };
459
460 /******************* server part **********************************/
461
462 /* called when the object for the service is called */
463 static int api_dbus_server_on_object_called(sd_bus_message *message, void *userdata, sd_bus_error *ret_error)
464 {
465         int rc;
466         const char *method;
467         const char *uuid;
468         struct dbus_req *dreq;
469         struct api_dbus *api = userdata;
470         struct afb_req areq;
471         uint32_t flags;
472
473         /* check the interface */
474         if (strcmp(sd_bus_message_get_interface(message), api->name) != 0)
475                 return 0;
476
477         /* get the method */
478         method = sd_bus_message_get_member(message);
479
480         /* create the request */
481         dreq = calloc(1 , sizeof *dreq);
482         if (dreq == NULL) {
483                 sd_bus_reply_method_errorf(message, SD_BUS_ERROR_NO_MEMORY, "out of memory");
484                 return 1;
485         }
486
487         /* get the data */
488         rc = sd_bus_message_read(message, "ssu", &dreq->request, &uuid, &flags);
489         if (rc < 0) {
490                 sd_bus_reply_method_errorf(message, SD_BUS_ERROR_INVALID_SIGNATURE, "invalid signature");
491                 free(dreq);
492                 return 1;
493         }
494
495         /* connect to the context */
496         if (afb_context_connect(&dreq->context, uuid, NULL) < 0) {
497                 sd_bus_reply_method_errorf(message, SD_BUS_ERROR_NO_MEMORY, "out of memory");
498                 free(dreq);
499                 return 1;
500         }
501
502         /* fulfill the request and emit it */
503         dreq->context.flags = flags;
504         dreq->message = sd_bus_message_ref(message);
505         dreq->json = NULL;
506         dreq->refcount = 1;
507         areq.itf = &dbus_req_itf;
508         areq.closure = dreq;
509         afb_apis_call_(areq, &dreq->context, api->api, method);
510         dbus_req_unref(dreq);
511         return 1;
512 }
513
514 static void afb_api_dbus_server_send_event(struct api_dbus *api, const char *event, struct json_object *object)
515 {
516         int rc;
517
518         rc = sd_bus_emit_signal(api->sdbus, api->path, api->name,
519                         "event", "ss", event, json_object_to_json_string_ext(object, JSON_C_TO_STRING_PLAIN));
520         if (rc < 0)
521                 ERROR("error while emiting event %s", event);
522         json_object_put(object);
523 }
524
525 static int afb_api_dbus_server_expects_event(struct api_dbus *api, const char *event)
526 {
527         size_t len = strlen(api->api);
528         if (strncasecmp(event, api->api, len) != 0)
529                 return 0;
530         return event[len] == '.';
531 }
532
533 static struct afb_event_listener_itf evitf = {
534         .send = (void*)afb_api_dbus_server_send_event,
535         .expects = (void*)afb_api_dbus_server_expects_event
536 };
537
538 /* create the service */
539 int afb_api_dbus_add_server(const char *path)
540 {
541         int rc;
542         struct api_dbus *api;
543
544         /* get the dbus api object connected */
545         api = make_api_dbus(path);
546         if (api == NULL)
547                 goto error;
548
549         /* request the service object name */
550         rc = sd_bus_request_name(api->sdbus, api->name, 0);
551         if (rc < 0) {
552                 errno = -rc;
553                 ERROR("can't register name %s", api->name);
554                 goto error2;
555         }
556
557         /* connect the service to the dbus object */
558         rc = sd_bus_add_object(api->sdbus, &api->slot, api->path, api_dbus_server_on_object_called, api);
559         if (rc < 0) {
560                 errno = -rc;
561                 ERROR("can't add dbus object %s for %s", api->path, api->name);
562                 goto error3;
563         }
564         INFO("afb service over dbus installed, name %s, path %s", api->name, api->path);
565
566         ctxClientEventListenerAdd(NULL, (struct afb_event_listener){ .itf = &evitf, .closure = api });
567
568         return 0;
569 error3:
570         sd_bus_release_name(api->sdbus, api->name);
571 error2:
572         destroy_api_dbus(api);
573 error:
574         return -1;
575 }
576
577