2 * Copyright (C) 2016-2019 "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"
32 #include "afb-export.h"
34 #include "afb-msg-json.h"
35 #include "afb-session.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 const char _internal_error_[] = "internal-error";
115 /******************************************************************************/
117 static int store_reply(
118 struct json_object *iobject, const char *ierror, const char *iinfo,
119 struct json_object **sobject, char **serror, char **sinfo)
124 else if (!(*serror = strdup(ierror))) {
125 ERROR("can't report error %s", ierror);
126 json_object_put(iobject);
135 json_object_put(iobject);
140 else if (!(*sinfo = strdup(iinfo)))
141 ERROR("can't report info %s", iinfo);
147 /******************************************************************************/
149 static void sync_leave(struct callreq *callreq)
151 struct jobloop *jobloop = __atomic_exchange_n(&callreq->jobloop, NULL, __ATOMIC_RELAXED);
156 static void sync_enter(int signum, void *closure, struct jobloop *jobloop)
158 struct callreq *callreq = closure;
160 callreq->jobloop = jobloop;
161 afb_export_process_xreq(callreq->export, &callreq->xreq);
163 afb_xreq_reply(&callreq->xreq, NULL, _internal_error_, NULL);
167 /******************************************************************************/
169 static void callreq_destroy_cb(struct afb_xreq *xreq)
171 struct callreq *callreq = CONTAINER_OF_XREQ(struct callreq, xreq);
173 afb_context_disconnect(&callreq->xreq.context);
174 json_object_put(callreq->xreq.json);
175 afb_cred_unref(callreq->xreq.cred);
179 static void callreq_reply_cb(struct afb_xreq *xreq, struct json_object *object, const char *error, const char *info)
181 struct callreq *callreq = CONTAINER_OF_XREQ(struct callreq, xreq);
184 /* centralized hooking */
185 if (callreq->mode.hooked) {
186 if (callreq->mode.sync) {
187 if (callreq->xreq.caller)
188 afb_hook_xreq_subcallsync_result(callreq->xreq.caller, -!!error, object, error, info);
190 afb_hook_api_callsync_result(callreq->export, -!!error, object, error, info);
192 if (callreq->xreq.caller)
193 afb_hook_xreq_subcall_result(callreq->xreq.caller, object, error, info);
195 afb_hook_api_call_result(callreq->export, object, error, info);
200 /* true report of the result */
201 if (callreq->mode.sync) {
202 callreq->returned = 1;
203 if (callreq->mode.legacy) {
204 callreq->status = -!!error;
206 *callreq->object = afb_msg_json_reply(object, error, info, NULL);
208 json_object_put(object);
210 callreq->status = store_reply(object, error, info,
211 callreq->object, callreq->error, callreq->info);
215 if (callreq->mode.legacy) {
216 object = afb_msg_json_reply(object, error, info, NULL);
217 callreq->legacy_final(callreq->closure, -!!error, object, callreq->callback, callreq->export, callreq->xreq.caller);
219 callreq->final(callreq->closure, object, error, info, callreq->callback, callreq->export, callreq->xreq.caller);
221 json_object_put(object);
225 static int callreq_subscribe_cb(struct afb_xreq *xreq, struct afb_event_x2 *event)
228 struct callreq *callreq = CONTAINER_OF_XREQ(struct callreq, xreq);
230 if (callreq->flags & afb_req_x2_subcall_pass_events)
231 rc = afb_xreq_subscribe(callreq->xreq.caller, event);
232 if (callreq->flags & afb_req_x2_subcall_catch_events) {
233 rc2 = afb_export_subscribe(callreq->export, event);
240 static int callreq_unsubscribe_cb(struct afb_xreq *xreq, struct afb_event_x2 *event)
243 struct callreq *callreq = CONTAINER_OF_XREQ(struct callreq, xreq);
245 if (callreq->flags & afb_req_x2_subcall_pass_events)
246 rc = afb_xreq_unsubscribe(callreq->xreq.caller, event);
247 if (callreq->flags & afb_req_x2_subcall_catch_events) {
248 rc2 = afb_export_unsubscribe(callreq->export, event);
255 /******************************************************************************/
257 const struct afb_xreq_query_itf afb_calls_xreq_itf = {
258 .unref = callreq_destroy_cb,
259 .reply = callreq_reply_cb,
260 .subscribe = callreq_subscribe_cb,
261 .unsubscribe = callreq_unsubscribe_cb
264 /******************************************************************************/
266 static struct callreq *callreq_create(
267 struct afb_export *export,
268 struct afb_xreq *caller,
271 struct json_object *args,
275 struct callreq *callreq;
276 size_t lenapi, lenverb;
279 lenapi = 1 + strlen(api);
280 lenverb = 1 + strlen(verb);
281 callreq = malloc(lenapi + lenverb + sizeof *callreq);
283 ERROR("out of memory");
284 json_object_put(args);
287 afb_xreq_init(&callreq->xreq, &afb_calls_xreq_itf);
288 callreq->xreq.context.validated = 1;
289 api2 = (char*)&callreq[1];
290 callreq->xreq.request.called_api = memcpy(api2, api, lenapi);;
291 verb2 = &api2[lenapi];
292 callreq->xreq.request.called_verb = memcpy(verb2, verb, lenverb);
293 callreq->xreq.json = args;
294 callreq->mode = mode;
296 export = afb_export_from_api_x3(caller->request.api);
297 if (flags & afb_req_x2_subcall_on_behalf)
298 callreq->xreq.cred = afb_cred_addref(caller->cred);
299 callreq->xreq.caller = caller;
300 afb_xreq_unhooked_addref(caller);
302 if (caller && (flags & afb_req_x2_subcall_api_session))
303 afb_context_subinit(&callreq->xreq.context, &caller->context);
305 afb_export_context_init(export, &callreq->xreq.context);
306 callreq->export = export;
307 callreq->flags = flags;
312 /******************************************************************************/
315 struct afb_export *export,
316 struct afb_xreq *caller,
319 struct json_object *args,
321 struct json_object **object,
326 struct callreq *callreq;
329 /* allocates the request */
330 callreq = callreq_create(export, caller, api, verb, args, flags, mode);
334 /* initializes the request */
335 callreq->jobloop = NULL;
336 callreq->returned = 0;
338 callreq->object = object;
339 callreq->error = error;
340 callreq->info = info;
342 afb_xreq_unhooked_addref(&callreq->xreq); /* avoid early callreq destruction */
344 rc = jobs_enter(NULL, 0, sync_enter, callreq);
345 if (rc >= 0 && callreq->returned) {
346 rc = callreq->status;
347 afb_xreq_unhooked_unref(&callreq->xreq);
351 afb_xreq_unhooked_unref(&callreq->xreq);
353 return store_reply(NULL, _internal_error_, NULL, object, info, error);
356 /******************************************************************************/
358 static void do_async(
359 struct afb_export *export,
360 struct afb_xreq *caller,
363 struct json_object *args,
367 void (*final)(void*, struct json_object*, const char*, const char*, union callback, struct afb_export*,struct afb_xreq*),
370 struct callreq *callreq;
372 callreq = callreq_create(export, caller, api, verb, args, flags, mode);
375 final(closure, NULL, _internal_error_, NULL, (union callback){ .any = callback }, export, caller);
377 callreq->callback.any = callback;
378 callreq->closure = closure;
379 callreq->final = final;
381 afb_export_process_xreq(callreq->export, &callreq->xreq);
385 /******************************************************************************/
387 static void final_call(
389 struct json_object *object,
392 union callback callback,
393 struct afb_export *export,
394 struct afb_xreq *caller)
396 if (callback.call.x3)
397 callback.call.x3(closure, object, error, info, afb_export_to_api_x3(export));
400 static void final_subcall(
402 struct json_object *object,
405 union callback callback,
406 struct afb_export *export,
407 struct afb_xreq *caller)
409 if (callback.subcall.x3)
410 callback.subcall.x3(closure, object, error, info, xreq_to_req_x2(caller));
413 /******************************************************************************/
416 struct afb_export *export,
419 struct json_object *args,
420 void (*callback)(void*, struct json_object*, const char *error, const char *info, struct afb_api_x3*),
423 do_async(export, NULL, api, verb, args, CALLFLAGS, callback, closure, final_call, mode_async);
426 int afb_calls_call_sync(
427 struct afb_export *export,
430 struct json_object *args,
431 struct json_object **object,
435 return do_sync(export, NULL, api, verb, args, CALLFLAGS, object, error, info, mode_sync);
438 void afb_calls_subcall(
439 struct afb_xreq *xreq,
442 struct json_object *args,
444 void (*callback)(void *closure, struct json_object *object, const char *error, const char * info, struct afb_req_x2 *req),
447 do_async(NULL, xreq, api, verb, args, flags, callback, closure, final_subcall, mode_async);
450 int afb_calls_subcall_sync(
451 struct afb_xreq *xreq,
454 struct json_object *args,
456 struct json_object **object,
460 return do_sync(NULL, xreq, api, verb, args, flags, object, error, info, mode_sync);
464 void afb_calls_hooked_call(
465 struct afb_export *export,
468 struct json_object *args,
469 void (*callback)(void*, struct json_object*, const char *error, const char *info, struct afb_api_x3*),
472 afb_hook_api_call(export, api, verb, args);
473 do_async(export, NULL, api, verb, args, CALLFLAGS, callback, closure, final_call, mode_hooked_async);
476 int afb_calls_hooked_call_sync(
477 struct afb_export *export,
480 struct json_object *args,
481 struct json_object **object,
485 afb_hook_api_callsync(export, api, verb, args);
486 return do_sync(export, NULL, api, verb, args, CALLFLAGS, object, error, info, mode_hooked_sync);
489 void afb_calls_hooked_subcall(
490 struct afb_xreq *xreq,
493 struct json_object *args,
495 void (*callback)(void *closure, struct json_object *object, const char *error, const char * info, struct afb_req_x2 *req),
498 afb_hook_xreq_subcall(xreq, api, verb, args, flags);
499 do_async(NULL, xreq, api, verb, args, flags, callback, closure, final_subcall, mode_hooked_async);
502 int afb_calls_hooked_subcall_sync(
503 struct afb_xreq *xreq,
506 struct json_object *args,
508 struct json_object **object,
512 afb_hook_xreq_subcallsync(xreq, api, verb, args, flags);
513 return do_sync(NULL, xreq, api, verb, args, flags, object, error, info, mode_hooked_sync);
517 /******************************************************************************/
518 /******************************************************************************/
519 /******************************************************************************/
520 /******************************************************************************/
521 /******************************************************************************/
522 /******************************************************************************/
523 /******************************************************************************/
524 /******************************************************************************/
526 static int do_legacy_sync(
527 struct afb_export *export,
528 struct afb_xreq *caller,
531 struct json_object *args,
533 struct json_object **object,
536 struct callreq *callreq;
539 /* allocates the request */
540 callreq = callreq_create(export, caller, api, verb, args, flags, mode);
544 /* initializes the request */
545 callreq->jobloop = NULL;
546 callreq->returned = 0;
548 callreq->object = object;
550 afb_xreq_unhooked_addref(&callreq->xreq); /* avoid early callreq destruction */
552 rc = jobs_enter(NULL, 0, sync_enter, callreq);
553 if (rc >= 0 && callreq->returned) {
554 rc = callreq->status;
555 afb_xreq_unhooked_unref(&callreq->xreq);
559 afb_xreq_unhooked_unref(&callreq->xreq);
562 *object = afb_msg_json_reply(NULL, _internal_error_, NULL, NULL);
566 /******************************************************************************/
568 static void do_legacy_async(
569 struct afb_export *export,
570 struct afb_xreq *caller,
573 struct json_object *args,
577 void (*final)(void*, int, struct json_object*, union callback, struct afb_export*,struct afb_xreq*),
580 struct callreq *callreq;
581 struct json_object *ie;
583 callreq = callreq_create(export, caller, api, verb, args, flags, mode);
586 ie = afb_msg_json_reply(NULL, _internal_error_, NULL, NULL);
587 final(closure, -1, ie, (union callback){ .any = callback }, export, caller);
590 callreq->callback.any = callback;
591 callreq->closure = closure;
592 callreq->legacy_final = final;
594 afb_export_process_xreq(callreq->export, &callreq->xreq);
598 /******************************************************************************/
600 static void final_legacy_call_v12(
603 struct json_object *object,
604 union callback callback,
605 struct afb_export *export,
606 struct afb_xreq *caller)
608 if (callback.call.legacy_v12)
609 callback.call.legacy_v12(closure, status, object);
612 static void final_legacy_call_v3(
615 struct json_object *object,
616 union callback callback,
617 struct afb_export *export,
618 struct afb_xreq *caller)
620 if (callback.call.legacy_v3)
621 callback.call.legacy_v3(closure, status, object, afb_export_to_api_x3(export));
624 /******************************************************************************/
626 void afb_calls_legacy_call_v12(
627 struct afb_export *export,
630 struct json_object *args,
631 void (*callback)(void*, int, struct json_object*),
634 do_legacy_async(export, NULL, api, verb, args, CALLFLAGS, callback, closure, final_legacy_call_v12, mode_legacy_async);
637 void afb_calls_legacy_call_v3(
638 struct afb_export *export,
641 struct json_object *args,
642 void (*callback)(void*, int, struct json_object*, struct afb_api_x3 *),
645 do_legacy_async(export, NULL, api, verb, args, CALLFLAGS, callback, closure, final_legacy_call_v3, mode_legacy_async);
648 int afb_calls_legacy_call_sync(
649 struct afb_export *export,
652 struct json_object *args,
653 struct json_object **result)
655 return do_legacy_sync(export, NULL, api, verb, args, CALLFLAGS, result, mode_legacy_sync);
659 void afb_calls_legacy_hooked_call_v12(
660 struct afb_export *export,
663 struct json_object *args,
664 void (*callback)(void*, int, struct json_object*),
667 afb_hook_api_call(export, api, verb, args);
668 do_legacy_async(export, NULL, api, verb, args, CALLFLAGS, callback, closure, final_legacy_call_v12, mode_hooked_legacy_async);
671 void afb_calls_legacy_hooked_call_v3(
672 struct afb_export *export,
675 struct json_object *args,
676 void (*callback)(void*, int, struct json_object*, struct afb_api_x3 *),
679 afb_hook_api_call(export, api, verb, args);
680 do_legacy_async(export, NULL, api, verb, args, CALLFLAGS, callback, closure, final_legacy_call_v3, mode_hooked_legacy_async);
683 int afb_calls_legacy_hooked_call_sync(
684 struct afb_export *export,
687 struct json_object *args,
688 struct json_object **result)
691 struct json_object *object;
693 afb_hook_api_callsync(export, api, verb, args);
694 rc = do_legacy_sync(export, NULL, api, verb, args, CALLFLAGS, &object, mode_hooked_legacy_sync);
698 json_object_put(object);
703 /******************************************************************************/
705 static void final_legacy_subcall_v1(
708 struct json_object *object,
709 union callback callback,
710 struct afb_export *export,
711 struct afb_xreq *caller)
713 if (callback.subcall.legacy_v1)
714 callback.subcall.legacy_v1(closure, status, object);
717 static void final_legacy_subcall_v2(
720 struct json_object *object,
721 union callback callback,
722 struct afb_export *export,
723 struct afb_xreq *caller)
725 if (callback.subcall.legacy_v2)
726 callback.subcall.legacy_v2(closure, status, object, xreq_to_req_x1(caller));
729 static void final_legacy_subcall_v3(
732 struct json_object *object,
733 union callback callback,
734 struct afb_export *export,
735 struct afb_xreq *caller)
737 if (callback.subcall.legacy_v3)
738 callback.subcall.legacy_v3(closure, status, object, xreq_to_req_x2(caller));
741 /******************************************************************************/
743 void afb_calls_legacy_subcall_v1(
744 struct afb_xreq *caller,
747 struct json_object *args,
748 void (*callback)(void*, int, struct json_object*),
751 do_legacy_async(NULL, caller, api, verb, args, LEGACY_SUBCALLFLAGS, callback, closure, final_legacy_subcall_v1, mode_legacy_async);
754 void afb_calls_legacy_subcall_v2(
755 struct afb_xreq *caller,
758 struct json_object *args,
759 void (*callback)(void*, int, struct json_object*, struct afb_req_x1),
762 do_legacy_async(NULL, caller, api, verb, args, LEGACY_SUBCALLFLAGS, callback, closure, final_legacy_subcall_v2, mode_legacy_async);
765 void afb_calls_legacy_subcall_v3(
766 struct afb_xreq *caller,
769 struct json_object *args,
770 void (*callback)(void*, int, struct json_object*, struct afb_req_x2 *),
773 do_legacy_async(NULL, caller, api, verb, args, LEGACY_SUBCALLFLAGS, callback, closure, final_legacy_subcall_v3, mode_legacy_async);
776 int afb_calls_legacy_subcall_sync(
777 struct afb_xreq *caller,
780 struct json_object *args,
781 struct json_object **result)
783 return do_legacy_sync(NULL, caller, api, verb, args, LEGACY_SUBCALLFLAGS, result, mode_legacy_sync);
787 void afb_calls_legacy_hooked_subcall_v1(
788 struct afb_xreq *caller,
791 struct json_object *args,
792 void (*callback)(void*, int, struct json_object*),
795 afb_hook_xreq_subcall(caller, api, verb, args, LEGACY_SUBCALLFLAGS);
796 do_legacy_async(NULL, caller, api, verb, args, LEGACY_SUBCALLFLAGS, callback, closure, final_legacy_subcall_v1, mode_hooked_legacy_async);
799 void afb_calls_legacy_hooked_subcall_v2(
800 struct afb_xreq *caller,
803 struct json_object *args,
804 void (*callback)(void*, int, struct json_object*, struct afb_req_x1),
807 afb_hook_xreq_subcall(caller, api, verb, args, LEGACY_SUBCALLFLAGS);
808 do_legacy_async(NULL, caller, api, verb, args, LEGACY_SUBCALLFLAGS, callback, closure, final_legacy_subcall_v2, mode_hooked_legacy_async);
811 void afb_calls_legacy_hooked_subcall_v3(
812 struct afb_xreq *caller,
815 struct json_object *args,
816 void (*callback)(void*, int, struct json_object*, struct afb_req_x2 *),
819 afb_hook_xreq_subcall(caller, api, verb, args, LEGACY_SUBCALLFLAGS);
820 do_legacy_async(NULL, caller, api, verb, args, LEGACY_SUBCALLFLAGS, callback, closure, final_legacy_subcall_v3, mode_hooked_legacy_async);
823 int afb_calls_legacy_hooked_subcall_sync(
824 struct afb_xreq *caller,
827 struct json_object *args,
828 struct json_object **result)
830 afb_hook_xreq_subcallsync(caller, api, verb, args, LEGACY_SUBCALLFLAGS);
831 return do_legacy_sync(NULL, caller, api, verb, args, LEGACY_SUBCALLFLAGS, result, mode_hooked_legacy_sync);