Add backward compatibility and remarks
[src/app-framework-binder.git] / src / afb-xreq.c
1 /*
2  * Copyright (C) 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 #define AFB_BINDING_PRAGMA_NO_VERBOSE_MACRO
20
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <errno.h>
25
26 #include <json-c/json.h>
27 #include <afb/afb-binding-v1.h>
28 #include <afb/afb-binding-v2.h>
29
30 #include "afb-context.h"
31 #include "afb-xreq.h"
32 #include "afb-evt.h"
33 #include "afb-msg-json.h"
34 #include "afb-subcall.h"
35 #include "afb-hook.h"
36 #include "afb-api.h"
37 #include "afb-apiset.h"
38 #include "afb-auth.h"
39 #include "jobs.h"
40 #include "verbose.h"
41
42 /******************************************************************************/
43
44 static void vinfo(void *first, void *second, const char *fmt, va_list args, void (*fun)(void*,void*,const char*))
45 {
46         char *info;
47         if (fmt == NULL || vasprintf(&info, fmt, args) < 0)
48                 info = NULL;
49         fun(first, second, info);
50         free(info);
51 }
52
53 /******************************************************************************/
54
55 static struct json_object *xreq_json_cb(void *closure)
56 {
57         struct afb_xreq *xreq = closure;
58         return xreq->json ? : (xreq->json = xreq->queryitf->json(xreq));
59 }
60
61 static struct afb_arg xreq_get_cb(void *closure, const char *name)
62 {
63         struct afb_xreq *xreq = closure;
64         if (xreq->queryitf->get)
65                 return xreq->queryitf->get(xreq, name);
66         else
67                 return afb_msg_json_get_arg(xreq_json_cb(closure), name);
68 }
69
70 static void xreq_success_cb(void *closure, struct json_object *obj, const char *info)
71 {
72         struct afb_xreq *xreq = closure;
73
74         if (xreq->replied) {
75                 ERROR("reply called more than one time!!");
76                 json_object_put(obj);
77         } else {
78                 xreq->replied = 1;
79                 if (xreq->queryitf->success)
80                         xreq->queryitf->success(xreq, obj, info);
81                 else
82                         xreq->queryitf->reply(xreq, 0, afb_msg_json_reply_ok(info, obj, &xreq->context, NULL));
83         }
84 }
85
86 static void xreq_fail_cb(void *closure, const char *status, const char *info)
87 {
88         struct afb_xreq *xreq = closure;
89
90         if (xreq->replied) {
91                 ERROR("reply called more than one time!!");
92         } else {
93                 xreq->replied = 1;
94                 if (xreq->queryitf->fail)
95                         xreq->queryitf->fail(xreq, status, info);
96                 else
97                         xreq->queryitf->reply(xreq, 1, afb_msg_json_reply_error(status, info, &xreq->context, NULL));
98         }
99 }
100
101 static void xreq_vsuccess_cb(void *closure, struct json_object *obj, const char *fmt, va_list args)
102 {
103         vinfo(closure, obj, fmt, args, (void*)xreq_success_cb);
104 }
105
106 static void xreq_vfail_cb(void *closure, const char *status, const char *fmt, va_list args)
107 {
108         vinfo(closure, (void*)status, fmt, args, (void*)xreq_fail_cb);
109 }
110
111 static void *xreq_context_get_cb(void *closure)
112 {
113         struct afb_xreq *xreq = closure;
114         return afb_context_get(&xreq->context);
115 }
116
117 static void xreq_context_set_cb(void *closure, void *value, void (*free_value)(void*))
118 {
119         struct afb_xreq *xreq = closure;
120         afb_context_set(&xreq->context, value, free_value);
121 }
122
123 static void xreq_addref_cb(void *closure)
124 {
125         struct afb_xreq *xreq = closure;
126         __atomic_add_fetch(&xreq->refcount, 1, __ATOMIC_RELAXED);
127 }
128
129 static void xreq_unref_cb(void *closure)
130 {
131         struct afb_xreq *xreq = closure;
132         if (!__atomic_sub_fetch(&xreq->refcount, 1, __ATOMIC_RELAXED)) {
133                 xreq->queryitf->unref(xreq);
134         }
135 }
136
137 static void xreq_session_close_cb(void *closure)
138 {
139         struct afb_xreq *xreq = closure;
140         afb_context_close(&xreq->context);
141 }
142
143 static int xreq_session_set_LOA_cb(void *closure, unsigned level)
144 {
145         struct afb_xreq *xreq = closure;
146         return afb_context_change_loa(&xreq->context, level);
147 }
148
149 static int xreq_subscribe_cb(void *closure, struct afb_event event)
150 {
151         struct afb_xreq *xreq = closure;
152         return afb_xreq_subscribe(xreq, event);
153 }
154
155 int afb_xreq_subscribe(struct afb_xreq *xreq, struct afb_event event)
156 {
157         if (xreq->listener)
158                 return afb_evt_add_watch(xreq->listener, event);
159         if (xreq->queryitf->subscribe)
160                 return xreq->queryitf->subscribe(xreq, event);
161         ERROR("no event listener, subscription impossible");
162         errno = EINVAL;
163         return -1;
164 }
165
166 static int xreq_unsubscribe_cb(void *closure, struct afb_event event)
167 {
168         struct afb_xreq *xreq = closure;
169         return afb_xreq_unsubscribe(xreq, event);
170 }
171
172 int afb_xreq_unsubscribe(struct afb_xreq *xreq, struct afb_event event)
173 {
174         if (xreq->listener)
175                 return afb_evt_remove_watch(xreq->listener, event);
176         if (xreq->queryitf->unsubscribe)
177                 return xreq->queryitf->unsubscribe(xreq, event);
178         ERROR("no event listener, unsubscription impossible");
179         errno = EINVAL;
180         return -1;
181 }
182
183 static void xreq_subcall_cb(void *closure, const char *api, const char *verb, struct json_object *args, void (*callback)(void*, int, struct json_object*), void *cb_closure)
184 {
185         struct afb_xreq *xreq = closure;
186
187         if (xreq->queryitf->subcall) {
188                 xreq->queryitf->subcall(xreq, api, verb, args, callback, cb_closure);
189                 json_object_put(args);
190         } else {
191                 afb_subcall(xreq, api, verb, args, callback, cb_closure);
192         }
193 }
194
195 struct xreq_sync
196 {
197         struct afb_xreq *caller;
198         const char *api;
199         const char *verb;
200         struct json_object *args;
201         struct jobloop *jobloop;
202         struct json_object *result;
203         int iserror;
204 };
205
206 static void xreq_sync_leave(struct xreq_sync *sync)
207 {
208         struct jobloop *jobloop = sync->jobloop;
209         if (jobloop) {
210                 sync->jobloop = NULL;
211                 jobs_leave(jobloop);
212         }
213 }
214
215 static void xreq_sync_reply(void *closure, int iserror, struct json_object *obj)
216 {
217         struct xreq_sync *sync = closure;
218
219         sync->iserror = iserror;
220         sync->result = json_object_get(obj);
221         xreq_sync_leave(sync);
222 }
223
224 static void xreq_sync_enter(int signum, void *closure, struct jobloop *jobloop)
225 {
226         struct xreq_sync *sync = closure;
227
228         if (!signum) {
229                 sync->jobloop = jobloop;
230                 xreq_subcall_cb(sync->caller, sync->api, sync->verb, json_object_get(sync->args), xreq_sync_reply, sync);
231         } else {
232                 sync->iserror = 1;
233                 xreq_sync_leave(sync);
234         }
235 }
236
237 static int xreq_subcallsync_cb(void *closure, const char *api, const char *verb, struct json_object *args, struct json_object **result)
238 {
239         int rc;
240         struct xreq_sync sync;
241         struct afb_xreq *xreq = closure;
242
243         sync.caller = xreq;
244         sync.api = api;
245         sync.verb = verb;
246         sync.args = args;
247         sync.jobloop = NULL;
248         sync.result = NULL;
249         sync.iserror = 1;
250
251         rc = jobs_enter(NULL, 0, xreq_sync_enter, &sync);
252         json_object_put(args);
253         if (rc < 0 || sync.iserror) {
254                 *result = sync.result ? : afb_msg_json_internal_error();
255                 return 0;
256         }
257         *result = sync.result;
258         return 1;
259 }
260
261 static void xreq_vverbose_cb(void*closure, int level, const char *file, int line, const char *func, const char *fmt, va_list args)
262 {
263         /* TODO: improves the implementation. example: on condition make a list of log messages that will be returned */
264         vverbose(level, file, line, func, fmt, args);
265 }
266
267 /******************************************************************************/
268
269 static struct json_object *xreq_hooked_json_cb(void *closure)
270 {
271         struct json_object *r = xreq_json_cb(closure);
272         struct afb_xreq *xreq = closure;
273         return afb_hook_xreq_json(xreq, r);
274 }
275
276 static struct afb_arg xreq_hooked_get_cb(void *closure, const char *name)
277 {
278         struct afb_arg r = xreq_get_cb(closure, name);
279         struct afb_xreq *xreq = closure;
280         return afb_hook_xreq_get(xreq, name, r);
281 }
282
283 static void xreq_hooked_success_cb(void *closure, struct json_object *obj, const char *info)
284 {
285         struct afb_xreq *xreq = closure;
286         afb_hook_xreq_success(xreq, obj, info);
287         xreq_success_cb(closure, obj, info);
288 }
289
290 static void xreq_hooked_fail_cb(void *closure, const char *status, const char *info)
291 {
292         struct afb_xreq *xreq = closure;
293         afb_hook_xreq_fail(xreq, status, info);
294         xreq_fail_cb(closure, status, info);
295 }
296
297 static void xreq_hooked_vsuccess_cb(void *closure, struct json_object *obj, const char *fmt, va_list args)
298 {
299         vinfo(closure, obj, fmt, args, (void*)xreq_hooked_success_cb);
300 }
301
302 static void xreq_hooked_vfail_cb(void *closure, const char *status, const char *fmt, va_list args)
303 {
304         vinfo(closure, (void*)status, fmt, args, (void*)xreq_hooked_fail_cb);
305 }
306
307 static void *xreq_hooked_context_get_cb(void *closure)
308 {
309         void *r = xreq_context_get_cb(closure);
310         struct afb_xreq *xreq = closure;
311         return afb_hook_xreq_context_get(xreq, r);
312 }
313
314 static void xreq_hooked_context_set_cb(void *closure, void *value, void (*free_value)(void*))
315 {
316         struct afb_xreq *xreq = closure;
317         afb_hook_xreq_context_set(xreq, value, free_value);
318         xreq_context_set_cb(closure, value, free_value);
319 }
320
321 static void xreq_hooked_addref_cb(void *closure)
322 {
323         struct afb_xreq *xreq = closure;
324         afb_hook_xreq_addref(xreq);
325         xreq_addref_cb(closure);
326 }
327
328 static void xreq_hooked_unref_cb(void *closure)
329 {
330         struct afb_xreq *xreq = closure;
331         afb_hook_xreq_unref(xreq);
332         if (!__atomic_sub_fetch(&xreq->refcount, 1, __ATOMIC_RELAXED)) {
333                 afb_hook_xreq_end(xreq);
334                 xreq->queryitf->unref(xreq);
335         }
336 }
337
338 static void xreq_hooked_session_close_cb(void *closure)
339 {
340         struct afb_xreq *xreq = closure;
341         afb_hook_xreq_session_close(xreq);
342         xreq_session_close_cb(closure);
343 }
344
345 static int xreq_hooked_session_set_LOA_cb(void *closure, unsigned level)
346 {
347         int r = xreq_session_set_LOA_cb(closure, level);
348         struct afb_xreq *xreq = closure;
349         return afb_hook_xreq_session_set_LOA(xreq, level, r);
350 }
351
352 static int xreq_hooked_subscribe_cb(void *closure, struct afb_event event)
353 {
354         int r = xreq_subscribe_cb(closure, event);
355         struct afb_xreq *xreq = closure;
356         return afb_hook_xreq_subscribe(xreq, event, r);
357 }
358
359 static int xreq_hooked_unsubscribe_cb(void *closure, struct afb_event event)
360 {
361         int r = xreq_unsubscribe_cb(closure, event);
362         struct afb_xreq *xreq = closure;
363         return afb_hook_xreq_unsubscribe(xreq, event, r);
364 }
365
366 struct reply
367 {
368         struct afb_xreq *xreq;
369         void (*callback)(void*, int, struct json_object*);
370         void *closure;
371 };
372
373 static void xreq_hooked_subcall_reply_cb(void *closure, int iserror, struct json_object *result)
374 {
375         struct reply *reply = closure;
376         
377         afb_hook_xreq_subcall_result(reply->xreq, iserror, result);
378         reply->callback(reply->closure, iserror, result);
379         free(reply);
380 }
381
382 static void xreq_hooked_subcall_cb(void *closure, const char *api, const char *verb, struct json_object *args, void (*callback)(void*, int, struct json_object*), void *cb_closure)
383 {
384         struct reply *reply = malloc(sizeof *reply);
385         struct afb_xreq *xreq = closure;
386         afb_hook_xreq_subcall(xreq, api, verb, args);
387         if (reply) {
388                 reply->xreq = xreq;
389                 reply->callback = callback;
390                 reply->closure = cb_closure;
391                 xreq_subcall_cb(closure, api, verb, args, xreq_hooked_subcall_reply_cb, reply);
392         } else {
393                 ERROR("out of memory");
394                 xreq_subcall_cb(closure, api, verb, args, callback, cb_closure);
395         }
396 }
397
398 static int xreq_hooked_subcallsync_cb(void *closure, const char *api, const char *verb, struct json_object *args, struct json_object **result)
399 {
400         int r;
401         struct afb_xreq *xreq = closure;
402         afb_hook_xreq_subcallsync(xreq, api, verb, args);
403         r = xreq_subcallsync_cb(closure, api, verb, args, result);
404         return afb_hook_xreq_subcallsync_result(xreq, r, *result);
405 }
406
407 static void xreq_hooked_vverbose_cb(void*closure, int level, const char *file, int line, const char *func, const char *fmt, va_list args)
408 {
409         struct afb_xreq *xreq = closure;
410         va_list ap;
411         va_copy(ap, args);
412         xreq_vverbose_cb(closure, level, file, line, func, fmt, args);
413         afb_hook_xreq_vverbose(xreq, level, file, line, func, fmt, ap);
414         va_end(ap);
415 }
416
417 /******************************************************************************/
418
419 const struct afb_req_itf xreq_itf = {
420         .json = xreq_json_cb,
421         .get = xreq_get_cb,
422         .success = xreq_success_cb,
423         .fail = xreq_fail_cb,
424         .vsuccess = xreq_vsuccess_cb,
425         .vfail = xreq_vfail_cb,
426         .context_get = xreq_context_get_cb,
427         .context_set = xreq_context_set_cb,
428         .addref = xreq_addref_cb,
429         .unref = xreq_unref_cb,
430         .session_close = xreq_session_close_cb,
431         .session_set_LOA = xreq_session_set_LOA_cb,
432         .subscribe = xreq_subscribe_cb,
433         .unsubscribe = xreq_unsubscribe_cb,
434         .subcall = xreq_subcall_cb,
435         .subcallsync = xreq_subcallsync_cb,
436         .vverbose = xreq_vverbose_cb
437 };
438
439 const struct afb_req_itf xreq_hooked_itf = {
440         .json = xreq_hooked_json_cb,
441         .get = xreq_hooked_get_cb,
442         .success = xreq_hooked_success_cb,
443         .fail = xreq_hooked_fail_cb,
444         .vsuccess = xreq_hooked_vsuccess_cb,
445         .vfail = xreq_hooked_vfail_cb,
446         .context_get = xreq_hooked_context_get_cb,
447         .context_set = xreq_hooked_context_set_cb,
448         .addref = xreq_hooked_addref_cb,
449         .unref = xreq_hooked_unref_cb,
450         .session_close = xreq_hooked_session_close_cb,
451         .session_set_LOA = xreq_hooked_session_set_LOA_cb,
452         .subscribe = xreq_hooked_subscribe_cb,
453         .unsubscribe = xreq_hooked_unsubscribe_cb,
454         .subcall = xreq_hooked_subcall_cb,
455         .subcallsync = xreq_hooked_subcallsync_cb,
456         .vverbose = xreq_hooked_vverbose_cb
457 };
458
459 static inline struct afb_req to_req(struct afb_xreq *xreq)
460 {
461         return (struct afb_req){ .itf = xreq->hookflags ? &xreq_hooked_itf : &xreq_itf, .closure = xreq };
462 }
463
464 /******************************************************************************/
465
466 struct json_object *afb_xreq_json(struct afb_xreq *xreq)
467 {
468         return afb_req_json(to_req(xreq));
469 }
470
471 void afb_xreq_success(struct afb_xreq *xreq, struct json_object *obj, const char *info)
472 {
473         afb_req_success(to_req(xreq), obj, info);
474 }
475
476 void afb_xreq_success_f(struct afb_xreq *xreq, struct json_object *obj, const char *info, ...)
477 {
478         char *message;
479         va_list args;
480         va_start(args, info);
481         if (info == NULL || vasprintf(&message, info, args) < 0)
482                 message = NULL;
483         va_end(args);
484         afb_xreq_success(xreq, obj, message);
485         free(message);
486 }
487
488 void afb_xreq_fail(struct afb_xreq *xreq, const char *status, const char *info)
489 {
490         afb_req_fail(to_req(xreq), status, info);
491 }
492
493 void afb_xreq_fail_f(struct afb_xreq *xreq, const char *status, const char *info, ...)
494 {
495         char *message;
496         va_list args;
497         va_start(args, info);
498         if (info == NULL || vasprintf(&message, info, args) < 0)
499                 message = NULL;
500         va_end(args);
501         afb_xreq_fail(xreq, status, message);
502         free(message);
503 }
504
505 const char *afb_xreq_raw(struct afb_xreq *xreq, size_t *size)
506 {
507         struct json_object *obj = xreq_json_cb(xreq);
508         const char *result = json_object_to_json_string(obj);
509         if (size != NULL)
510                 *size = strlen(result);
511         return result;
512 }
513
514 void afb_xreq_addref(struct afb_xreq *xreq)
515 {
516         afb_req_addref(to_req(xreq));
517 }
518
519 void afb_xreq_unref(struct afb_xreq *xreq)
520 {
521         afb_req_unref(to_req(xreq));
522 }
523
524 void afb_xreq_unhooked_subcall(struct afb_xreq *xreq, const char *api, const char *verb, struct json_object *args, void (*callback)(void*, int, struct json_object*), void *cb_closure)
525 {
526         xreq_subcall_cb(xreq, api, verb, args, callback, cb_closure);
527 }
528
529 void afb_xreq_subcall(struct afb_xreq *xreq, const char *api, const char *verb, struct json_object *args, void (*callback)(void*, int, struct json_object*), void *cb_closure)
530 {
531         afb_req_subcall(to_req(xreq), api, verb, args, callback, cb_closure);
532 }
533
534 int afb_xreq_unhooked_subcall_sync(struct afb_xreq *xreq, const char *api, const char *verb, struct json_object *args, struct json_object **result)
535 {
536         return xreq_subcallsync_cb(xreq, api, verb, args, result);
537 }
538
539 int afb_xreq_subcall_sync(struct afb_xreq *xreq, const char *api, const char *verb, struct json_object *args, struct json_object **result)
540 {
541         return afb_req_subcall_sync(to_req(xreq), api, verb, args, result);
542 }
543
544 static int xreq_session_check_apply_v1(struct afb_xreq *xreq, int sessionflags)
545 {
546         int loa;
547
548         if ((sessionflags & (AFB_SESSION_CLOSE_V1|AFB_SESSION_RENEW_V1|AFB_SESSION_CHECK_V1|AFB_SESSION_LOA_EQ_V1)) != 0) {
549                 if (!afb_context_check(&xreq->context)) {
550                         afb_context_close(&xreq->context);
551                         afb_xreq_fail_f(xreq, "denied", "invalid token's identity");
552                         errno = EINVAL;
553                         return -1;
554                 }
555         }
556
557         if ((sessionflags & AFB_SESSION_LOA_GE_V1) != 0) {
558                 loa = (sessionflags >> AFB_SESSION_LOA_SHIFT_V1) & AFB_SESSION_LOA_MASK_V1;
559                 if (!afb_context_check_loa(&xreq->context, loa)) {
560                         afb_xreq_fail_f(xreq, "denied", "invalid LOA");
561                         errno = EPERM;
562                         return -1;
563                 }
564         }
565
566         if ((sessionflags & AFB_SESSION_LOA_LE_V1) != 0) {
567                 loa = (sessionflags >> AFB_SESSION_LOA_SHIFT_V1) & AFB_SESSION_LOA_MASK_V1;
568                 if (afb_context_check_loa(&xreq->context, loa + 1)) {
569                         afb_xreq_fail_f(xreq, "denied", "invalid LOA");
570                         errno = EPERM;
571                         return -1;
572                 }
573         }
574
575         if ((sessionflags & AFB_SESSION_RENEW_V1) != 0) {
576                 afb_context_refresh(&xreq->context);
577         }
578         if ((sessionflags & AFB_SESSION_CLOSE_V1) != 0) {
579                 afb_context_change_loa(&xreq->context, 0);
580                 afb_context_close(&xreq->context);
581         }
582
583         return 0;
584 }
585
586 static int xreq_session_check_apply_v2(struct afb_xreq *xreq, uint32_t sessionflags, const struct afb_auth *auth)
587 {
588         int loa;
589
590         if (sessionflags != 0) {
591                 if (!afb_context_check(&xreq->context)) {
592                         afb_context_close(&xreq->context);
593                         afb_xreq_fail_f(xreq, "denied", "invalid token's identity");
594                         errno = EINVAL;
595                         return -1;
596                 }
597         }
598
599         loa = (int)(sessionflags & AFB_SESSION_LOA_MASK_V2);
600         if (loa && !afb_context_check_loa(&xreq->context, loa)) {
601                 afb_xreq_fail_f(xreq, "denied", "invalid LOA");
602                 errno = EPERM;
603                 return -1;
604         }
605
606         if (auth && !afb_auth_check(auth, xreq)) {
607                 afb_xreq_fail_f(xreq, "denied", "authorisation refused");
608                 errno = EPERM;
609                 return -1;
610         }
611
612         if ((sessionflags & AFB_SESSION_REFRESH_V2) != 0) {
613                 afb_context_refresh(&xreq->context);
614         }
615         if ((sessionflags & AFB_SESSION_CLOSE_V2) != 0) {
616                 afb_context_close(&xreq->context);
617         }
618
619         return 0;
620 }
621
622 void afb_xreq_call_verb_v1(struct afb_xreq *xreq, const struct afb_verb_desc_v1 *verb)
623 {
624         if (!verb)
625                 afb_xreq_fail_unknown_verb(xreq);
626         else
627                 if (!xreq_session_check_apply_v1(xreq, verb->session))
628                         verb->callback(to_req(xreq));
629 }
630
631 void afb_xreq_call_verb_v2(struct afb_xreq *xreq, const struct afb_verb_v2 *verb)
632 {
633         if (!verb)
634                 afb_xreq_fail_unknown_verb(xreq);
635         else
636                 if (!xreq_session_check_apply_v2(xreq, verb->session, verb->auth))
637                         verb->callback(to_req(xreq));
638 }
639
640 void afb_xreq_init(struct afb_xreq *xreq, const struct afb_xreq_query_itf *queryitf)
641 {
642         memset(xreq, 0, sizeof *xreq);
643         xreq->refcount = 1;
644         xreq->queryitf = queryitf;
645 }
646
647 void afb_xreq_fail_unknown_api(struct afb_xreq *xreq)
648 {
649         afb_xreq_fail_f(xreq, "unknown-api", "api %s not found (for verb %s)", xreq->api, xreq->verb);
650 }
651
652 void afb_xreq_fail_unknown_verb(struct afb_xreq *xreq)
653 {
654         afb_xreq_fail_f(xreq, "unknown-verb", "verb %s unknown within api %s", xreq->verb, xreq->api);
655 }
656
657 static void process_sync(struct afb_xreq *xreq)
658 {
659         struct afb_api api;
660
661         /* init hooking */
662         afb_hook_init_xreq(xreq);
663         if (xreq->hookflags)
664                 afb_hook_xreq_begin(xreq);
665
666         /* search the api */
667         if (afb_apiset_get(xreq->apiset, xreq->api, &api) < 0) {
668                 afb_xreq_fail_f(xreq, "unknown-api", "api %s not found", xreq->api);
669         } else {
670                 xreq->context.api_key = api.closure;
671                 api.itf->call(api.closure, xreq);
672         }
673 }
674
675 static void process_async(int signum, void *arg)
676 {
677         struct afb_xreq *xreq = arg;
678
679         if (signum != 0) {
680                 afb_xreq_fail_f(xreq, "aborted", "signal %s(%d) caught", strsignal(signum), signum);
681         } else {
682                 process_sync(xreq);
683         }
684         afb_xreq_unref(xreq);
685 }
686
687 void afb_xreq_process(struct afb_xreq *xreq, struct afb_apiset *apiset)
688 {
689         xreq->apiset = apiset;
690
691         afb_xreq_addref(xreq);
692         if (jobs_queue(NULL, afb_apiset_timeout_get(apiset), process_async, xreq) < 0) {
693                 /* TODO: allows or not to proccess it directly as when no threading? (see above) */
694                 ERROR("can't process job with threads: %m");
695                 afb_xreq_fail_f(xreq, "cancelled", "not able to create a job for the task");
696                 afb_xreq_unref(xreq);
697         }
698         afb_xreq_unref(xreq);
699 }
700