2 * Copyright (C) 2015-2020 "IoT.bzh"
3 * Author: José Bollo <jose.bollo@iot.bzh>
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
24 #include <json-c/json.h>
26 #define AFB_BINDING_VERSION 0
27 #include <afb/afb-binding.h>
29 #include "afb-calls.h"
31 #include "afb-export.h"
33 #include "afb-msg-json.h"
34 #include "afb-session.h"
36 #include "afb-error-text.h"
41 #define CALLFLAGS (afb_req_x2_subcall_api_session|afb_req_x2_subcall_catch_events)
42 #define LEGACY_SUBCALLFLAGS (afb_req_x2_subcall_pass_events|afb_req_x2_subcall_on_behalf)
45 /************************************************************************/
54 #define mode_sync ((struct modes){ .hooked=0, .sync=1, .legacy=0 })
55 #define mode_async ((struct modes){ .hooked=0, .sync=0, .legacy=0 })
56 #define mode_legacy_sync ((struct modes){ .hooked=0, .sync=1, .legacy=1 })
57 #define mode_legacy_async ((struct modes){ .hooked=0, .sync=0, .legacy=1 })
60 #define mode_hooked_sync ((struct modes){ .hooked=1, .sync=1, .legacy=0 })
61 #define mode_hooked_async ((struct modes){ .hooked=1, .sync=0, .legacy=0 })
62 #define mode_hooked_legacy_sync ((struct modes){ .hooked=1, .sync=1, .legacy=1 })
63 #define mode_hooked_legacy_async ((struct modes){ .hooked=1, .sync=0, .legacy=1 })
69 void (*legacy_v1)(void*, int, struct json_object*);
70 void (*legacy_v2)(void*, int, struct json_object*, struct afb_req_x1);
71 void (*legacy_v3)(void*, int, struct json_object*, struct afb_req_x2*);
72 void (*x3)(void*, struct json_object*, const char*, const char *, struct afb_req_x2*);
75 void (*legacy_v12)(void*, int, struct json_object*);
76 void (*legacy_v3)(void*, int, struct json_object*, struct afb_api_x3*);
77 void (*x3)(void*, struct json_object*, const char*, const char*, struct afb_api_x3*);
85 struct afb_export *export;
93 struct jobloop *jobloop;
96 struct json_object **object;
101 union callback callback;
104 void (*final)(void*, struct json_object*, const char*, const char*, union callback, struct afb_export*,struct afb_xreq*);
105 void (*legacy_final)(void*, int, struct json_object*, union callback, struct afb_export*,struct afb_xreq*);
111 /******************************************************************************/
113 static int store_reply(
114 struct json_object *iobject, const char *ierror, const char *iinfo,
115 struct json_object **sobject, char **serror, char **sinfo)
120 else if (!(*serror = strdup(ierror))) {
121 ERROR("can't report error %s", ierror);
122 json_object_put(iobject);
131 json_object_put(iobject);
136 else if (!(*sinfo = strdup(iinfo)))
137 ERROR("can't report info %s", iinfo);
143 /******************************************************************************/
145 static void sync_leave(struct callreq *callreq)
147 struct jobloop *jobloop = __atomic_exchange_n(&callreq->jobloop, NULL, __ATOMIC_RELAXED);
152 static void sync_enter(int signum, void *closure, struct jobloop *jobloop)
154 struct callreq *callreq = closure;
156 callreq->jobloop = jobloop;
157 afb_export_process_xreq(callreq->export, &callreq->xreq);
159 afb_xreq_reply(&callreq->xreq, NULL, afb_error_text_internal_error, NULL);
163 /******************************************************************************/
165 static void callreq_destroy_cb(struct afb_xreq *xreq)
167 struct callreq *callreq = CONTAINER_OF_XREQ(struct callreq, xreq);
169 afb_context_disconnect(&callreq->xreq.context);
170 json_object_put(callreq->xreq.json);
174 static void callreq_reply_cb(struct afb_xreq *xreq, struct json_object *object, const char *error, const char *info)
176 struct callreq *callreq = CONTAINER_OF_XREQ(struct callreq, xreq);
179 /* centralized hooking */
180 if (callreq->mode.hooked) {
181 if (callreq->mode.sync) {
182 if (callreq->xreq.caller)
183 afb_hook_xreq_subcallsync_result(callreq->xreq.caller, -!!error, object, error, info);
185 afb_hook_api_callsync_result(callreq->export, -!!error, object, error, info);
187 if (callreq->xreq.caller)
188 afb_hook_xreq_subcall_result(callreq->xreq.caller, object, error, info);
190 afb_hook_api_call_result(callreq->export, object, error, info);
195 /* true report of the result */
196 if (callreq->mode.sync) {
197 callreq->returned = 1;
198 if (callreq->mode.legacy) {
199 callreq->status = -!!error;
201 *callreq->object = afb_msg_json_reply(object, error, info, NULL);
203 json_object_put(object);
205 callreq->status = store_reply(object, error, info,
206 callreq->object, callreq->error, callreq->info);
210 if (callreq->mode.legacy) {
211 object = afb_msg_json_reply(object, error, info, NULL);
212 callreq->legacy_final(callreq->closure, -!!error, object, callreq->callback, callreq->export, callreq->xreq.caller);
214 callreq->final(callreq->closure, object, error, info, callreq->callback, callreq->export, callreq->xreq.caller);
216 json_object_put(object);
220 static int callreq_subscribe_cb(struct afb_xreq *xreq, struct afb_event_x2 *event)
223 struct callreq *callreq = CONTAINER_OF_XREQ(struct callreq, xreq);
225 if (callreq->flags & afb_req_x2_subcall_pass_events)
226 rc = afb_xreq_subscribe(callreq->xreq.caller, event);
227 if (callreq->flags & afb_req_x2_subcall_catch_events) {
228 rc2 = afb_export_subscribe(callreq->export, event);
235 static int callreq_unsubscribe_cb(struct afb_xreq *xreq, struct afb_event_x2 *event)
238 struct callreq *callreq = CONTAINER_OF_XREQ(struct callreq, xreq);
240 if (callreq->flags & afb_req_x2_subcall_pass_events)
241 rc = afb_xreq_unsubscribe(callreq->xreq.caller, event);
242 if (callreq->flags & afb_req_x2_subcall_catch_events) {
243 rc2 = afb_export_unsubscribe(callreq->export, event);
250 /******************************************************************************/
252 const struct afb_xreq_query_itf afb_calls_xreq_itf = {
253 .unref = callreq_destroy_cb,
254 .reply = callreq_reply_cb,
255 .subscribe = callreq_subscribe_cb,
256 .unsubscribe = callreq_unsubscribe_cb
259 /******************************************************************************/
261 static struct callreq *callreq_create(
262 struct afb_export *export,
263 struct afb_xreq *caller,
266 struct json_object *args,
270 struct callreq *callreq;
271 size_t lenapi, lenverb;
274 lenapi = 1 + strlen(api);
275 lenverb = 1 + strlen(verb);
276 callreq = malloc(lenapi + lenverb + sizeof *callreq);
278 ERROR("out of memory");
279 json_object_put(args);
282 afb_xreq_init(&callreq->xreq, &afb_calls_xreq_itf);
283 api2 = (char*)&callreq[1];
284 callreq->xreq.request.called_api = memcpy(api2, api, lenapi);;
285 verb2 = &api2[lenapi];
286 callreq->xreq.request.called_verb = memcpy(verb2, verb, lenverb);
287 callreq->xreq.json = args;
288 callreq->mode = mode;
290 afb_export_context_init(export, &callreq->xreq.context);
292 if (flags & afb_req_x2_subcall_api_session)
293 afb_export_context_init(export, &callreq->xreq.context);
295 afb_context_subinit(&callreq->xreq.context, &caller->context);
296 if (flags & afb_req_x2_subcall_on_behalf)
297 afb_context_on_behalf_other_context(&callreq->xreq.context, &caller->context);
298 callreq->xreq.caller = caller;
299 afb_xreq_unhooked_addref(caller);
300 export = afb_export_from_api_x3(caller->request.api);
302 callreq->export = export;
303 callreq->flags = flags;
308 /******************************************************************************/
311 struct afb_export *export,
312 struct afb_xreq *caller,
315 struct json_object *args,
317 struct json_object **object,
322 struct callreq *callreq;
325 /* allocates the request */
326 callreq = callreq_create(export, caller, api, verb, args, flags, mode);
330 /* initializes the request */
331 callreq->jobloop = NULL;
332 callreq->returned = 0;
334 callreq->object = object;
335 callreq->error = error;
336 callreq->info = info;
338 afb_xreq_unhooked_addref(&callreq->xreq); /* avoid early callreq destruction */
340 rc = jobs_enter(NULL, 0, sync_enter, callreq);
341 if (rc >= 0 && callreq->returned) {
342 rc = callreq->status;
343 afb_xreq_unhooked_unref(&callreq->xreq);
347 afb_xreq_unhooked_unref(&callreq->xreq);
349 return store_reply(NULL, afb_error_text_internal_error, NULL, object, error, info);
352 /******************************************************************************/
354 static void do_async(
355 struct afb_export *export,
356 struct afb_xreq *caller,
359 struct json_object *args,
363 void (*final)(void*, struct json_object*, const char*, const char*, union callback, struct afb_export*,struct afb_xreq*),
366 struct callreq *callreq;
368 callreq = callreq_create(export, caller, api, verb, args, flags, mode);
371 final(closure, NULL, afb_error_text_internal_error, NULL, (union callback){ .any = callback }, export, caller);
373 callreq->callback.any = callback;
374 callreq->closure = closure;
375 callreq->final = final;
377 afb_export_process_xreq(callreq->export, &callreq->xreq);
381 /******************************************************************************/
383 static void final_call(
385 struct json_object *object,
388 union callback callback,
389 struct afb_export *export,
390 struct afb_xreq *caller)
392 if (callback.call.x3)
393 callback.call.x3(closure, object, error, info, afb_export_to_api_x3(export));
396 static void final_subcall(
398 struct json_object *object,
401 union callback callback,
402 struct afb_export *export,
403 struct afb_xreq *caller)
405 if (callback.subcall.x3)
406 callback.subcall.x3(closure, object, error, info, xreq_to_req_x2(caller));
409 /******************************************************************************/
412 struct afb_export *export,
415 struct json_object *args,
416 void (*callback)(void*, struct json_object*, const char *error, const char *info, struct afb_api_x3*),
419 do_async(export, NULL, api, verb, args, CALLFLAGS, callback, closure, final_call, mode_async);
422 int afb_calls_call_sync(
423 struct afb_export *export,
426 struct json_object *args,
427 struct json_object **object,
431 return do_sync(export, NULL, api, verb, args, CALLFLAGS, object, error, info, mode_sync);
434 void afb_calls_subcall(
435 struct afb_xreq *xreq,
438 struct json_object *args,
440 void (*callback)(void *closure, struct json_object *object, const char *error, const char * info, struct afb_req_x2 *req),
443 do_async(NULL, xreq, api, verb, args, flags, callback, closure, final_subcall, mode_async);
446 int afb_calls_subcall_sync(
447 struct afb_xreq *xreq,
450 struct json_object *args,
452 struct json_object **object,
456 return do_sync(NULL, xreq, api, verb, args, flags, object, error, info, mode_sync);
460 void afb_calls_hooked_call(
461 struct afb_export *export,
464 struct json_object *args,
465 void (*callback)(void*, struct json_object*, const char *error, const char *info, struct afb_api_x3*),
468 afb_hook_api_call(export, api, verb, args);
469 do_async(export, NULL, api, verb, args, CALLFLAGS, callback, closure, final_call, mode_hooked_async);
472 int afb_calls_hooked_call_sync(
473 struct afb_export *export,
476 struct json_object *args,
477 struct json_object **object,
481 afb_hook_api_callsync(export, api, verb, args);
482 return do_sync(export, NULL, api, verb, args, CALLFLAGS, object, error, info, mode_hooked_sync);
485 void afb_calls_hooked_subcall(
486 struct afb_xreq *xreq,
489 struct json_object *args,
491 void (*callback)(void *closure, struct json_object *object, const char *error, const char * info, struct afb_req_x2 *req),
494 afb_hook_xreq_subcall(xreq, api, verb, args, flags);
495 do_async(NULL, xreq, api, verb, args, flags, callback, closure, final_subcall, mode_hooked_async);
498 int afb_calls_hooked_subcall_sync(
499 struct afb_xreq *xreq,
502 struct json_object *args,
504 struct json_object **object,
508 afb_hook_xreq_subcallsync(xreq, api, verb, args, flags);
509 return do_sync(NULL, xreq, api, verb, args, flags, object, error, info, mode_hooked_sync);
513 /******************************************************************************/
514 /******************************************************************************/
515 /******************************************************************************/
516 /******************************************************************************/
517 /******************************************************************************/
518 /******************************************************************************/
519 /******************************************************************************/
520 /******************************************************************************/
522 static int do_legacy_sync(
523 struct afb_export *export,
524 struct afb_xreq *caller,
527 struct json_object *args,
529 struct json_object **object,
532 struct callreq *callreq;
535 /* allocates the request */
536 callreq = callreq_create(export, caller, api, verb, args, flags, mode);
540 /* initializes the request */
541 callreq->jobloop = NULL;
542 callreq->returned = 0;
544 callreq->object = object;
546 afb_xreq_unhooked_addref(&callreq->xreq); /* avoid early callreq destruction */
548 rc = jobs_enter(NULL, 0, sync_enter, callreq);
549 if (rc >= 0 && callreq->returned) {
550 rc = callreq->status;
551 afb_xreq_unhooked_unref(&callreq->xreq);
555 afb_xreq_unhooked_unref(&callreq->xreq);
558 *object = afb_msg_json_reply(NULL, afb_error_text_internal_error, NULL, NULL);
562 /******************************************************************************/
564 static void do_legacy_async(
565 struct afb_export *export,
566 struct afb_xreq *caller,
569 struct json_object *args,
573 void (*final)(void*, int, struct json_object*, union callback, struct afb_export*,struct afb_xreq*),
576 struct callreq *callreq;
577 struct json_object *ie;
579 callreq = callreq_create(export, caller, api, verb, args, flags, mode);
582 ie = afb_msg_json_reply(NULL, afb_error_text_internal_error, NULL, NULL);
583 final(closure, -1, ie, (union callback){ .any = callback }, export, caller);
586 callreq->callback.any = callback;
587 callreq->closure = closure;
588 callreq->legacy_final = final;
590 afb_export_process_xreq(callreq->export, &callreq->xreq);
594 /******************************************************************************/
596 static void final_legacy_call_v12(
599 struct json_object *object,
600 union callback callback,
601 struct afb_export *export,
602 struct afb_xreq *caller)
604 if (callback.call.legacy_v12)
605 callback.call.legacy_v12(closure, status, object);
608 static void final_legacy_call_v3(
611 struct json_object *object,
612 union callback callback,
613 struct afb_export *export,
614 struct afb_xreq *caller)
616 if (callback.call.legacy_v3)
617 callback.call.legacy_v3(closure, status, object, afb_export_to_api_x3(export));
620 /******************************************************************************/
622 void afb_calls_legacy_call_v12(
623 struct afb_export *export,
626 struct json_object *args,
627 void (*callback)(void*, int, struct json_object*),
630 do_legacy_async(export, NULL, api, verb, args, CALLFLAGS, callback, closure, final_legacy_call_v12, mode_legacy_async);
633 void afb_calls_legacy_call_v3(
634 struct afb_export *export,
637 struct json_object *args,
638 void (*callback)(void*, int, struct json_object*, struct afb_api_x3 *),
641 do_legacy_async(export, NULL, api, verb, args, CALLFLAGS, callback, closure, final_legacy_call_v3, mode_legacy_async);
644 int afb_calls_legacy_call_sync(
645 struct afb_export *export,
648 struct json_object *args,
649 struct json_object **result)
651 return do_legacy_sync(export, NULL, api, verb, args, CALLFLAGS, result, mode_legacy_sync);
655 void afb_calls_legacy_hooked_call_v12(
656 struct afb_export *export,
659 struct json_object *args,
660 void (*callback)(void*, int, struct json_object*),
663 afb_hook_api_call(export, api, verb, args);
664 do_legacy_async(export, NULL, api, verb, args, CALLFLAGS, callback, closure, final_legacy_call_v12, mode_hooked_legacy_async);
667 void afb_calls_legacy_hooked_call_v3(
668 struct afb_export *export,
671 struct json_object *args,
672 void (*callback)(void*, int, struct json_object*, struct afb_api_x3 *),
675 afb_hook_api_call(export, api, verb, args);
676 do_legacy_async(export, NULL, api, verb, args, CALLFLAGS, callback, closure, final_legacy_call_v3, mode_hooked_legacy_async);
679 int afb_calls_legacy_hooked_call_sync(
680 struct afb_export *export,
683 struct json_object *args,
684 struct json_object **result)
687 struct json_object *object;
689 afb_hook_api_callsync(export, api, verb, args);
690 rc = do_legacy_sync(export, NULL, api, verb, args, CALLFLAGS, &object, mode_hooked_legacy_sync);
694 json_object_put(object);
699 /******************************************************************************/
701 static void final_legacy_subcall_v1(
704 struct json_object *object,
705 union callback callback,
706 struct afb_export *export,
707 struct afb_xreq *caller)
709 if (callback.subcall.legacy_v1)
710 callback.subcall.legacy_v1(closure, status, object);
713 static void final_legacy_subcall_v2(
716 struct json_object *object,
717 union callback callback,
718 struct afb_export *export,
719 struct afb_xreq *caller)
721 if (callback.subcall.legacy_v2)
722 callback.subcall.legacy_v2(closure, status, object, xreq_to_req_x1(caller));
725 static void final_legacy_subcall_v3(
728 struct json_object *object,
729 union callback callback,
730 struct afb_export *export,
731 struct afb_xreq *caller)
733 if (callback.subcall.legacy_v3)
734 callback.subcall.legacy_v3(closure, status, object, xreq_to_req_x2(caller));
737 /******************************************************************************/
739 void afb_calls_legacy_subcall_v1(
740 struct afb_xreq *caller,
743 struct json_object *args,
744 void (*callback)(void*, int, struct json_object*),
747 do_legacy_async(NULL, caller, api, verb, args, LEGACY_SUBCALLFLAGS, callback, closure, final_legacy_subcall_v1, mode_legacy_async);
750 void afb_calls_legacy_subcall_v2(
751 struct afb_xreq *caller,
754 struct json_object *args,
755 void (*callback)(void*, int, struct json_object*, struct afb_req_x1),
758 do_legacy_async(NULL, caller, api, verb, args, LEGACY_SUBCALLFLAGS, callback, closure, final_legacy_subcall_v2, mode_legacy_async);
761 void afb_calls_legacy_subcall_v3(
762 struct afb_xreq *caller,
765 struct json_object *args,
766 void (*callback)(void*, int, struct json_object*, struct afb_req_x2 *),
769 do_legacy_async(NULL, caller, api, verb, args, LEGACY_SUBCALLFLAGS, callback, closure, final_legacy_subcall_v3, mode_legacy_async);
772 int afb_calls_legacy_subcall_sync(
773 struct afb_xreq *caller,
776 struct json_object *args,
777 struct json_object **result)
779 return do_legacy_sync(NULL, caller, api, verb, args, LEGACY_SUBCALLFLAGS, result, mode_legacy_sync);
783 void afb_calls_legacy_hooked_subcall_v1(
784 struct afb_xreq *caller,
787 struct json_object *args,
788 void (*callback)(void*, int, struct json_object*),
791 afb_hook_xreq_subcall(caller, api, verb, args, LEGACY_SUBCALLFLAGS);
792 do_legacy_async(NULL, caller, api, verb, args, LEGACY_SUBCALLFLAGS, callback, closure, final_legacy_subcall_v1, mode_hooked_legacy_async);
795 void afb_calls_legacy_hooked_subcall_v2(
796 struct afb_xreq *caller,
799 struct json_object *args,
800 void (*callback)(void*, int, struct json_object*, struct afb_req_x1),
803 afb_hook_xreq_subcall(caller, api, verb, args, LEGACY_SUBCALLFLAGS);
804 do_legacy_async(NULL, caller, api, verb, args, LEGACY_SUBCALLFLAGS, callback, closure, final_legacy_subcall_v2, mode_hooked_legacy_async);
807 void afb_calls_legacy_hooked_subcall_v3(
808 struct afb_xreq *caller,
811 struct json_object *args,
812 void (*callback)(void*, int, struct json_object*, struct afb_req_x2 *),
815 afb_hook_xreq_subcall(caller, api, verb, args, LEGACY_SUBCALLFLAGS);
816 do_legacy_async(NULL, caller, api, verb, args, LEGACY_SUBCALLFLAGS, callback, closure, final_legacy_subcall_v3, mode_hooked_legacy_async);
819 int afb_calls_legacy_hooked_subcall_sync(
820 struct afb_xreq *caller,
823 struct json_object *args,
824 struct json_object **result)
826 afb_hook_xreq_subcallsync(caller, api, verb, args, LEGACY_SUBCALLFLAGS);
827 return do_legacy_sync(NULL, caller, api, verb, args, LEGACY_SUBCALLFLAGS, result, mode_hooked_legacy_sync);