7d9f8dcb2c16c1abd5e6110e844ca30218780323
[src/app-framework-binder.git] / src / afb-svc.c
1 /*
2  * Copyright (C) 2016, 2017 "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
20 #include <stdlib.h>
21 #include <string.h>
22 #include <errno.h>
23
24 #include <json-c/json.h>
25
26 #include <afb/afb-binding-v1.h>
27 #include <afb/afb-binding-v2.h>
28
29 #include "afb-session.h"
30 #include "afb-context.h"
31 #include "afb-evt.h"
32 #include "afb-msg-json.h"
33 #include "afb-svc.h"
34 #include "afb-xreq.h"
35 #include "afb-cred.h"
36 #include "afb-apiset.h"
37 #include "afb-hook.h"
38 #include "jobs.h"
39 #include "verbose.h"
40
41
42 #define HOOK(x,...) if((svc)->hookflags & afb_hook_flag_svc_##x) afb_hook_svc_##x(__VA_ARGS__)
43
44 /*
45  * Structure for requests initiated by the service
46  */
47 struct svc_req
48 {
49         struct afb_xreq xreq;
50
51         struct afb_svc *svc;
52
53         /* the args */
54         void (*callback)(void*, int, struct json_object*);
55         void *closure;
56
57         /* sync */
58         struct jobloop *jobloop;
59         struct json_object *result;
60         int status;
61         int async;
62 };
63
64 /* functions for services */
65 static void svc_on_event(void *closure, const char *event, int eventid, struct json_object *object);
66 static void svc_call(void *closure, const char *api, const char *verb, struct json_object *args,
67                                 void (*callback)(void*, int, struct json_object*), void *cbclosure);
68 static int svc_call_sync(void *closure, const char *api, const char *verb, struct json_object *args,
69                                 struct json_object **result);
70
71 /* the interface for services */
72 static const struct afb_service_itf service_itf = {
73         .call = svc_call,
74         .call_sync = svc_call_sync
75 };
76
77 /* the interface for events */
78 static const struct afb_evt_itf evt_itf = {
79         .broadcast = svc_on_event,
80         .push = svc_on_event
81 };
82
83 /* functions for requests of services */
84 static void svcreq_destroy(struct afb_xreq *xreq);
85 static void svcreq_reply(struct afb_xreq *xreq, int status, json_object *obj);
86
87 /* interface for requests of services */
88 const struct afb_xreq_query_itf afb_svc_xreq_itf = {
89         .unref = svcreq_destroy,
90         .reply = svcreq_reply
91 };
92
93 /* the common session for services sharing their session */
94 static struct afb_session *common_session;
95
96 static inline struct afb_service to_afb_service(struct afb_svc *svc)
97 {
98         return (struct afb_service){ .itf = &service_itf, .closure = svc };
99 }
100
101 /*
102  * Frees a service
103  */
104 static void svc_free(struct afb_svc *svc)
105 {
106         if (svc->listener != NULL)
107                 afb_evt_listener_unref(svc->listener);
108         if (svc->session)
109                 afb_session_unref(svc->session);
110         afb_apiset_unref(svc->apiset);
111         free(svc);
112 }
113
114 /*
115  * Allocates a new service
116  */
117 static struct afb_svc *afb_svc_alloc(
118                         const char *api,
119                         struct afb_apiset *apiset,
120                         int share_session
121 )
122 {
123         struct afb_svc *svc;
124
125         /* allocates the svc handler */
126         svc = calloc(1, sizeof * svc);
127         if (svc == NULL) {
128                 errno = ENOMEM;
129                 return NULL;
130         }
131
132         /* instanciate the apiset */
133         svc->api = api;
134         svc->apiset = afb_apiset_addref(apiset);
135
136         /* instanciate the session */
137         if (share_session) {
138                 /* session shared with other svcs */
139                 if (common_session == NULL) {
140                         common_session = afb_session_create (NULL, 0);
141                         if (common_session == NULL)
142                                 goto error;
143                 }
144                 svc->session = afb_session_addref(common_session);
145         } else {
146                 /* session dedicated to the svc */
147                 svc->session = afb_session_create (NULL, 0);
148                 if (svc->session == NULL)
149                         goto error;
150         }
151
152         svc->hookflags = afb_hook_flags_svc(svc->api);
153         return svc;
154
155 error:
156         svc_free(svc);
157         return NULL;
158 }
159
160 /*
161  * Creates a new service
162  */
163 struct afb_svc *afb_svc_create_v1(
164                         const char *api,
165                         struct afb_apiset *apiset,
166                         int share_session,
167                         int (*start)(struct afb_service service),
168                         void (*on_event)(const char *event, struct json_object *object)
169 )
170 {
171         int rc;
172         struct afb_svc *svc;
173
174         /* allocates the svc handler */
175         svc = afb_svc_alloc(api, apiset, share_session);
176         if (svc == NULL)
177                 goto error;
178
179         /* initialises the listener if needed */
180         if (on_event) {
181                 svc->on_event = on_event;
182                 svc->listener = afb_evt_listener_create(&evt_itf, svc);
183                 if (svc->listener == NULL)
184                         goto error;
185         }
186
187         /* initialises the svc now */
188         if (start) {
189                 HOOK(start_before, svc);
190                 rc = start(to_afb_service(svc));
191                 HOOK(start_after, svc, rc);
192                 if (rc < 0)
193                         goto error;
194         }
195
196         return svc;
197
198 error:
199         svc_free(svc);
200         return NULL;
201 }
202
203 /*
204  * Creates a new service
205  */
206 struct afb_svc *afb_svc_create_v2(
207                         const char *api,
208                         struct afb_apiset *apiset,
209                         int share_session,
210                         int (*start)(),
211                         void (*on_event)(const char *event, struct json_object *object),
212                         struct afb_binding_data_v2 *data
213 )
214 {
215         int rc;
216         struct afb_svc *svc;
217
218         /* allocates the svc handler */
219         svc = afb_svc_alloc(api, apiset, share_session);
220         if (svc == NULL)
221                 goto error;
222         data->service = to_afb_service(svc);
223
224         /* initialises the listener if needed */
225         if (on_event) {
226                 svc->on_event = on_event;
227                 svc->listener = afb_evt_listener_create(&evt_itf, svc);
228                 if (svc->listener == NULL)
229                         goto error;
230         }
231
232         /* starts the svc if needed */
233         if (start) {
234                 HOOK(start_before, svc);
235                 rc = start();
236                 HOOK(start_after, svc, rc);
237                 if (rc < 0)
238                         goto error;
239         }
240
241         return svc;
242
243 error:
244         svc_free(svc);
245         return NULL;
246 }
247
248 void afb_svc_update_hook(struct afb_svc *svc)
249 {
250         svc->hookflags = afb_hook_flags_svc(svc->api);
251 }
252
253 /*
254  * Propagates the event to the service
255  */
256 static void svc_on_event(void *closure, const char *event, int eventid, struct json_object *object)
257 {
258         struct afb_svc *svc = closure;
259
260         HOOK(on_event_before, svc, event, eventid, object);
261         svc->on_event(event, object);
262         HOOK(on_event_after, svc, event, eventid, object);
263         json_object_put(object);
264 }
265
266 /*
267  * create an svc_req
268  */
269 static struct svc_req *svcreq_create(struct afb_svc *svc, const char *api, const char *verb, struct json_object *args)
270 {
271         struct svc_req *svcreq;
272         size_t lenapi, lenverb;
273         char *copy;
274
275         /* allocates the request */
276         lenapi = 1 + strlen(api);
277         lenverb = 1 + strlen(verb);
278         svcreq = malloc(lenapi + lenverb + sizeof *svcreq);
279         if (svcreq != NULL) {
280                 /* initialises the request */
281                 afb_xreq_init(&svcreq->xreq, &afb_svc_xreq_itf);
282                 afb_context_init(&svcreq->xreq.context, svc->session, NULL);
283                 svcreq->xreq.context.validated = 1;
284                 copy = (char*)&svcreq[1];
285                 memcpy(copy, api, lenapi);
286                 svcreq->xreq.api = copy;
287                 copy = &copy[lenapi];
288                 memcpy(copy, verb, lenverb);
289                 svcreq->xreq.verb = copy;
290                 svcreq->xreq.listener = svc->listener;
291                 svcreq->xreq.json = args;
292                 svcreq->svc = svc;
293         }
294         return svcreq;
295 }
296
297 /*
298  * destroys the svc_req
299  */
300 static void svcreq_destroy(struct afb_xreq *xreq)
301 {
302         struct svc_req *svcreq = CONTAINER_OF_XREQ(struct svc_req, xreq);
303
304         afb_context_disconnect(&svcreq->xreq.context);
305         json_object_put(svcreq->xreq.json);
306         afb_cred_unref(svcreq->xreq.cred);
307         free(svcreq);
308 }
309
310 static void svcreq_sync_leave(struct svc_req *svcreq)
311 {
312         struct jobloop *jobloop = svcreq->jobloop;
313
314         if (jobloop) {
315                 svcreq->jobloop = NULL;
316                 jobs_leave(jobloop);
317         }
318 }
319
320 static void svcreq_reply(struct afb_xreq *xreq, int status, json_object *obj)
321 {
322         struct svc_req *svcreq = CONTAINER_OF_XREQ(struct svc_req, xreq);
323         if (svcreq->async) {
324                 struct afb_svc *svc = svcreq->svc;
325                 if (svcreq->callback)
326                         svcreq->callback(svcreq->closure, status, obj);
327                 HOOK(call_result, svc, status, obj);
328                 json_object_put(obj);
329         } else {
330                 svcreq->status = status;
331                 svcreq->result = obj;
332                 svcreq_sync_leave(svcreq);
333         }
334 }
335
336 static void svcreq_sync_enter(int signum, void *closure, struct jobloop *jobloop)
337 {
338         struct svc_req *svcreq = closure;
339
340         if (!signum) {
341                 svcreq->jobloop = jobloop;
342                 afb_xreq_process(&svcreq->xreq, svcreq->svc->apiset);
343         } else {
344                 svcreq->result = afb_msg_json_internal_error();
345                 svcreq->status = -1;
346                 svcreq_sync_leave(svcreq);
347         }
348 }
349
350 /*
351  * Initiates a call for the service
352  */
353 static void svc_call(void *closure, const char *api, const char *verb, struct json_object *args, void (*callback)(void*, int, struct json_object*), void *cbclosure)
354 {
355         struct afb_svc *svc = closure;
356         struct svc_req *svcreq;
357         struct json_object *ierr;
358
359         HOOK(call, svc, api, verb, args);
360
361         /* allocates the request */
362         svcreq = svcreq_create(svc, api, verb, args);
363         if (svcreq == NULL) {
364                 ERROR("out of memory");
365                 json_object_put(args);
366                 ierr = afb_msg_json_internal_error();
367                 if (callback)
368                         callback(cbclosure, -1, ierr);
369                 HOOK(call_result, svc, -1, ierr);
370                 json_object_put(ierr);
371                 return;
372         }
373
374         /* initialises the request */
375         svcreq->jobloop = NULL;
376         svcreq->callback = callback;
377         svcreq->closure = cbclosure;
378         svcreq->async = 1;
379
380         /* terminates and frees ressources if needed */
381         afb_xreq_process(&svcreq->xreq, svc->apiset);
382 }
383
384 static int svc_call_sync(void *closure, const char *api, const char *verb, struct json_object *args,
385                                 struct json_object **result)
386 {
387         struct afb_svc *svc = closure;
388         struct svc_req *svcreq;
389         struct json_object *resu;
390         int rc;
391
392         HOOK(callsync, svc, api, verb, args);
393
394         /* allocates the request */
395         svcreq = svcreq_create(svc, api, verb, args);
396         if (svcreq == NULL) {
397                 ERROR("out of memory");
398                 errno = ENOMEM;
399                 json_object_put(args);
400                 resu = afb_msg_json_internal_error();
401                 rc = -1;
402         } else {
403                 /* initialises the request */
404                 svcreq->jobloop = NULL;
405                 svcreq->callback = NULL;
406                 svcreq->result = NULL;
407                 svcreq->status = 0;
408                 svcreq->async = 0;
409                 afb_xreq_addref(&svcreq->xreq);
410                 rc = jobs_enter(NULL, 0, svcreq_sync_enter, svcreq);
411                 if (rc >= 0)
412                         rc = svcreq->status;
413                 resu = (rc >= 0 || svcreq->result) ? svcreq->result : afb_msg_json_internal_error();
414                 afb_xreq_unref(&svcreq->xreq);
415         }
416         HOOK(callsync_result, svc, rc, resu);
417         if (result)
418                 *result = resu;
419         else
420                 json_object_put(resu);
421         return rc;
422 }
423