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