2 * Copyright (C) 2016, 2017, 2018 "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 })
58 #define mode_hooked_sync ((struct modes){ .hooked=1, .sync=1, .legacy=0 })
59 #define mode_hooked_async ((struct modes){ .hooked=1, .sync=0, .legacy=0 })
60 #define mode_hooked_legacy_sync ((struct modes){ .hooked=1, .sync=1, .legacy=1 })
61 #define mode_hooked_legacy_async ((struct modes){ .hooked=1, .sync=0, .legacy=1 })
66 void (*legacy_v1)(void*, int, struct json_object*);
67 void (*legacy_v2)(void*, int, struct json_object*, struct afb_req_x1);
68 void (*legacy_v3)(void*, int, struct json_object*, struct afb_req_x2*);
69 void (*x3)(void*, struct json_object*, const char*, const char *, struct afb_req_x2*);
72 void (*legacy_v12)(void*, int, struct json_object*);
73 void (*legacy_v3)(void*, int, struct json_object*, struct afb_api_x3*);
74 void (*x3)(void*, struct json_object*, const char*, const char*, struct afb_api_x3*);
82 struct afb_export *export;
90 struct jobloop *jobloop;
93 struct json_object **object;
98 union callback callback;
101 void (*final)(void*, struct json_object*, const char*, const char*, union callback, struct afb_export*,struct afb_xreq*);
102 void (*legacy_final)(void*, int, struct json_object*, union callback, struct afb_export*,struct afb_xreq*);
108 /******************************************************************************/
110 static int store_reply(
111 struct json_object *iobject, const char *ierror, const char *iinfo,
112 struct json_object **sobject, char **serror, char **sinfo)
117 else if (!(*serror = strdup(ierror))) {
118 ERROR("can't report error %s", ierror);
119 json_object_put(iobject);
128 json_object_put(iobject);
133 else if (!(*sinfo = strdup(iinfo)))
134 ERROR("can't report info %s", iinfo);
140 /******************************************************************************/
142 static void sync_leave(struct callreq *callreq)
144 struct jobloop *jobloop = __atomic_exchange_n(&callreq->jobloop, NULL, __ATOMIC_RELAXED);
149 static void sync_enter(int signum, void *closure, struct jobloop *jobloop)
151 struct callreq *callreq = closure;
153 callreq->jobloop = jobloop;
154 afb_export_process_xreq(callreq->export, &callreq->xreq);
156 afb_xreq_reply(&callreq->xreq, NULL, "internal-error", NULL);
160 /******************************************************************************/
162 static void callreq_destroy_cb(struct afb_xreq *xreq)
164 struct callreq *callreq = CONTAINER_OF_XREQ(struct callreq, xreq);
166 afb_context_disconnect(&callreq->xreq.context);
167 json_object_put(callreq->xreq.json);
168 afb_cred_unref(callreq->xreq.cred);
172 static void callreq_reply_cb(struct afb_xreq *xreq, struct json_object *object, const char *error, const char *info)
174 struct callreq *callreq = CONTAINER_OF_XREQ(struct callreq, xreq);
176 /* centralized hooking */
177 if (callreq->mode.hooked) {
178 if (callreq->mode.sync) {
179 if (callreq->xreq.caller)
180 afb_hook_xreq_subcallsync_result(callreq->xreq.caller, -!!error, object, error, info);
182 afb_hook_api_callsync_result(callreq->export, -!!error, object, error, info);
184 if (callreq->xreq.caller)
185 afb_hook_xreq_subcall_result(callreq->xreq.caller, object, error, info);
187 afb_hook_api_call_result(callreq->export, object, error, info);
191 /* true report of the result */
192 if (callreq->mode.sync) {
193 callreq->returned = 1;
194 if (callreq->mode.legacy) {
195 callreq->status = -!!error;
197 *callreq->object = afb_msg_json_reply(object, error, info, NULL);
199 json_object_put(object);
201 callreq->status = store_reply(object, error, info,
202 callreq->object, callreq->error, callreq->info);
206 if (callreq->mode.legacy) {
207 object = afb_msg_json_reply(object, error, info, NULL);
208 callreq->legacy_final(callreq->closure, -!!error, object, callreq->callback, callreq->export, callreq->xreq.caller);
210 callreq->final(callreq->closure, object, error, info, callreq->callback, callreq->export, callreq->xreq.caller);
212 json_object_put(object);
216 static int callreq_subscribe_cb(struct afb_xreq *xreq, struct afb_event_x2 *event)
219 struct callreq *callreq = CONTAINER_OF_XREQ(struct callreq, xreq);
221 if (callreq->flags & afb_req_x2_subcall_pass_events)
222 rc = afb_xreq_subscribe(callreq->xreq.caller, event);
223 if (callreq->flags & afb_req_x2_subcall_catch_events) {
224 rc2 = afb_export_subscribe(callreq->export, event);
231 static int callreq_unsubscribe_cb(struct afb_xreq *xreq, struct afb_event_x2 *event)
234 struct callreq *callreq = CONTAINER_OF_XREQ(struct callreq, xreq);
236 if (callreq->flags & afb_req_x2_subcall_pass_events)
237 rc = afb_xreq_unsubscribe(callreq->xreq.caller, event);
238 if (callreq->flags & afb_req_x2_subcall_catch_events) {
239 rc2 = afb_export_unsubscribe(callreq->export, event);
246 /******************************************************************************/
248 const struct afb_xreq_query_itf afb_calls_xreq_itf = {
249 .unref = callreq_destroy_cb,
250 .reply = callreq_reply_cb,
251 .subscribe = callreq_subscribe_cb,
252 .unsubscribe = callreq_unsubscribe_cb
255 /******************************************************************************/
257 static struct callreq *callreq_create(
258 struct afb_export *export,
259 struct afb_xreq *caller,
262 struct json_object *args,
266 struct callreq *callreq;
267 size_t lenapi, lenverb;
270 lenapi = 1 + strlen(api);
271 lenverb = 1 + strlen(verb);
272 callreq = malloc(lenapi + lenverb + sizeof *callreq);
274 ERROR("out of memory");
275 json_object_put(args);
278 afb_xreq_init(&callreq->xreq, &afb_calls_xreq_itf);
279 callreq->xreq.context.validated = 1;
280 api2 = (char*)&callreq[1];
281 callreq->xreq.request.called_api = memcpy(api2, api, lenapi);;
282 verb2 = &api2[lenapi];
283 callreq->xreq.request.called_verb = memcpy(verb2, verb, lenverb);
284 callreq->xreq.json = args;
285 callreq->mode = mode;
287 export = afb_export_from_api_x3(caller->request.api);
288 if (flags & afb_req_x2_subcall_on_behalf)
289 callreq->xreq.cred = afb_cred_addref(caller->cred);
290 callreq->xreq.caller = caller;
291 afb_xreq_unhooked_addref(caller);
293 if (caller && (flags & afb_req_x2_subcall_api_session))
294 afb_context_subinit(&callreq->xreq.context, &caller->context);
296 afb_export_context_init(export, &callreq->xreq.context);
297 callreq->export = export;
298 callreq->flags = flags;
303 /******************************************************************************/
306 struct afb_export *export,
307 struct afb_xreq *caller,
310 struct json_object *args,
312 struct json_object **object,
317 struct callreq *callreq;
320 /* allocates the request */
321 callreq = callreq_create(export, caller, api, verb, args, flags, mode);
325 /* initializes the request */
326 callreq->jobloop = NULL;
327 callreq->returned = 0;
329 callreq->object = object;
330 callreq->error = error;
331 callreq->info = info;
333 afb_xreq_unhooked_addref(&callreq->xreq); /* avoid early callreq destruction */
335 rc = jobs_enter(NULL, 0, sync_enter, callreq);
336 if (rc >= 0 && callreq->returned) {
337 rc = callreq->status;
338 afb_xreq_unhooked_unref(&callreq->xreq);
342 afb_xreq_unhooked_unref(&callreq->xreq);
344 return store_reply(NULL, "internal-error", NULL, object, info, error);
347 /******************************************************************************/
349 static void do_async(
350 struct afb_export *export,
351 struct afb_xreq *caller,
354 struct json_object *args,
358 void (*final)(void*, struct json_object*, const char*, const char*, union callback, struct afb_export*,struct afb_xreq*),
361 struct callreq *callreq;
363 callreq = callreq_create(export, caller, api, verb, args, flags, mode);
366 final(closure, NULL, "internal-error", NULL, (union callback){ .any = callback }, export, caller);
368 callreq->callback.any = callback;
369 callreq->closure = closure;
370 callreq->final = final;
372 afb_export_process_xreq(callreq->export, &callreq->xreq);
376 /******************************************************************************/
378 static void final_call(
380 struct json_object *object,
383 union callback callback,
384 struct afb_export *export,
385 struct afb_xreq *caller)
387 if (callback.call.x3)
388 callback.call.x3(closure, object, error, info, afb_export_to_api_x3(export));
391 static void final_subcall(
393 struct json_object *object,
396 union callback callback,
397 struct afb_export *export,
398 struct afb_xreq *caller)
400 if (callback.subcall.x3)
401 callback.subcall.x3(closure, object, error, info, xreq_to_req_x2(caller));
404 /******************************************************************************/
407 struct afb_export *export,
410 struct json_object *args,
411 void (*callback)(void*, struct json_object*, const char *error, const char *info, struct afb_api_x3*),
414 do_async(export, NULL, api, verb, args, CALLFLAGS, callback, closure, final_call, mode_async);
417 void afb_calls_hooked_call(
418 struct afb_export *export,
421 struct json_object *args,
422 void (*callback)(void*, struct json_object*, const char *error, const char *info, struct afb_api_x3*),
425 afb_hook_api_call(export, api, verb, args);
426 do_async(export, NULL, api, verb, args, CALLFLAGS, callback, closure, final_call, mode_hooked_async);
429 int afb_calls_call_sync(
430 struct afb_export *export,
433 struct json_object *args,
434 struct json_object **object,
438 return do_sync(export, NULL, api, verb, args, CALLFLAGS, object, error, info, mode_sync);
441 int afb_calls_hooked_call_sync(
442 struct afb_export *export,
445 struct json_object *args,
446 struct json_object **object,
450 afb_hook_api_callsync(export, api, verb, args);
451 return do_sync(export, NULL, api, verb, args, CALLFLAGS, object, error, info, mode_hooked_sync);
454 void afb_calls_subcall(
455 struct afb_xreq *xreq,
458 struct json_object *args,
460 void (*callback)(void *closure, struct json_object *object, const char *error, const char * info, struct afb_req_x2 *req),
463 do_async(NULL, xreq, api, verb, args, flags, callback, closure, final_subcall, mode_async);
466 void afb_calls_hooked_subcall(
467 struct afb_xreq *xreq,
470 struct json_object *args,
472 void (*callback)(void *closure, struct json_object *object, const char *error, const char * info, struct afb_req_x2 *req),
475 afb_hook_xreq_subcall(xreq, api, verb, args, flags);
476 do_async(NULL, xreq, api, verb, args, flags, callback, closure, final_subcall, mode_hooked_async);
479 int afb_calls_subcall_sync(
480 struct afb_xreq *xreq,
483 struct json_object *args,
485 struct json_object **object,
489 return do_sync(NULL, xreq, api, verb, args, flags, object, error, info, mode_sync);
492 int afb_calls_hooked_subcall_sync(
493 struct afb_xreq *xreq,
496 struct json_object *args,
498 struct json_object **object,
502 afb_hook_xreq_subcallsync(xreq, api, verb, args, flags);
503 return do_sync(NULL, xreq, api, verb, args, flags, object, error, info, mode_hooked_sync);
506 /******************************************************************************/
507 /******************************************************************************/
508 /******************************************************************************/
509 /******************************************************************************/
510 /******************************************************************************/
511 /******************************************************************************/
512 /******************************************************************************/
513 /******************************************************************************/
515 static int do_legacy_sync(
516 struct afb_export *export,
517 struct afb_xreq *caller,
520 struct json_object *args,
522 struct json_object **object,
525 struct callreq *callreq;
528 /* allocates the request */
529 callreq = callreq_create(export, caller, api, verb, args, flags, mode);
533 /* initializes the request */
534 callreq->jobloop = NULL;
535 callreq->returned = 0;
537 callreq->object = object;
539 afb_xreq_unhooked_addref(&callreq->xreq); /* avoid early callreq destruction */
541 rc = jobs_enter(NULL, 0, sync_enter, callreq);
542 if (rc >= 0 && callreq->returned) {
543 rc = callreq->status;
544 afb_xreq_unhooked_unref(&callreq->xreq);
548 afb_xreq_unhooked_unref(&callreq->xreq);
551 *object = afb_msg_json_internal_error();
555 /******************************************************************************/
557 static void do_legacy_async(
558 struct afb_export *export,
559 struct afb_xreq *caller,
562 struct json_object *args,
566 void (*final)(void*, int, struct json_object*, union callback, struct afb_export*,struct afb_xreq*),
569 struct callreq *callreq;
570 struct json_object *ie;
572 callreq = callreq_create(export, caller, api, verb, args, flags, mode);
575 ie = afb_msg_json_internal_error();
576 final(closure, -1, ie, (union callback){ .any = callback }, export, caller);
579 callreq->callback.any = callback;
580 callreq->closure = closure;
581 callreq->legacy_final = final;
583 afb_export_process_xreq(callreq->export, &callreq->xreq);
587 /******************************************************************************/
589 static void final_legacy_call_v12(
592 struct json_object *object,
593 union callback callback,
594 struct afb_export *export,
595 struct afb_xreq *caller)
597 if (callback.call.legacy_v12)
598 callback.call.legacy_v12(closure, status, object);
601 static void final_legacy_call_v3(
604 struct json_object *object,
605 union callback callback,
606 struct afb_export *export,
607 struct afb_xreq *caller)
609 if (callback.call.legacy_v3)
610 callback.call.legacy_v3(closure, status, object, afb_export_to_api_x3(export));
613 /******************************************************************************/
615 void afb_calls_legacy_call_v12(
616 struct afb_export *export,
619 struct json_object *args,
620 void (*callback)(void*, int, struct json_object*),
623 do_legacy_async(export, NULL, api, verb, args, CALLFLAGS, callback, closure, final_legacy_call_v12, mode_legacy_async);
626 void afb_calls_legacy_hooked_call_v12(
627 struct afb_export *export,
630 struct json_object *args,
631 void (*callback)(void*, int, struct json_object*),
634 afb_hook_api_call(export, api, verb, args);
635 do_legacy_async(export, NULL, api, verb, args, CALLFLAGS, callback, closure, final_legacy_call_v12, mode_hooked_legacy_async);
638 void afb_calls_legacy_call_v3(
639 struct afb_export *export,
642 struct json_object *args,
643 void (*callback)(void*, int, struct json_object*, struct afb_api_x3 *),
646 do_legacy_async(export, NULL, api, verb, args, CALLFLAGS, callback, closure, final_legacy_call_v3, mode_legacy_async);
649 void afb_calls_legacy_hooked_call_v3(
650 struct afb_export *export,
653 struct json_object *args,
654 void (*callback)(void*, int, struct json_object*, struct afb_api_x3 *),
657 afb_hook_api_call(export, api, verb, args);
658 do_legacy_async(export, NULL, api, verb, args, CALLFLAGS, callback, closure, final_legacy_call_v3, mode_hooked_legacy_async);
661 int afb_calls_legacy_call_sync(
662 struct afb_export *export,
665 struct json_object *args,
666 struct json_object **result)
668 return do_legacy_sync(export, NULL, api, verb, args, CALLFLAGS, result, mode_legacy_sync);
671 int afb_calls_legacy_hooked_call_sync(
672 struct afb_export *export,
675 struct json_object *args,
676 struct json_object **result)
679 struct json_object *object;
681 afb_hook_api_callsync(export, api, verb, args);
682 rc = do_legacy_sync(export, NULL, api, verb, args, CALLFLAGS, &object, mode_hooked_legacy_sync);
686 json_object_put(object);
690 /******************************************************************************/
692 static void final_legacy_subcall_v1(
695 struct json_object *object,
696 union callback callback,
697 struct afb_export *export,
698 struct afb_xreq *caller)
700 if (callback.subcall.legacy_v1)
701 callback.subcall.legacy_v1(closure, status, object);
704 static void final_legacy_subcall_v2(
707 struct json_object *object,
708 union callback callback,
709 struct afb_export *export,
710 struct afb_xreq *caller)
712 if (callback.subcall.legacy_v2)
713 callback.subcall.legacy_v2(closure, status, object, xreq_to_req_x1(caller));
716 static void final_legacy_subcall_v3(
719 struct json_object *object,
720 union callback callback,
721 struct afb_export *export,
722 struct afb_xreq *caller)
724 if (callback.subcall.legacy_v3)
725 callback.subcall.legacy_v3(closure, status, object, xreq_to_req_x2(caller));
728 /******************************************************************************/
730 void afb_calls_legacy_subcall_v1(
731 struct afb_xreq *caller,
734 struct json_object *args,
735 void (*callback)(void*, int, struct json_object*),
738 do_legacy_async(NULL, caller, api, verb, args, LEGACY_SUBCALLFLAGS, callback, closure, final_legacy_subcall_v1, mode_legacy_async);
741 void afb_calls_legacy_hooked_subcall_v1(
742 struct afb_xreq *caller,
745 struct json_object *args,
746 void (*callback)(void*, int, struct json_object*),
749 afb_hook_xreq_subcall(caller, api, verb, args, LEGACY_SUBCALLFLAGS);
750 do_legacy_async(NULL, caller, api, verb, args, LEGACY_SUBCALLFLAGS, callback, closure, final_legacy_subcall_v1, mode_hooked_legacy_async);
753 void afb_calls_legacy_subcall_v2(
754 struct afb_xreq *caller,
757 struct json_object *args,
758 void (*callback)(void*, int, struct json_object*, struct afb_req_x1),
761 do_legacy_async(NULL, caller, api, verb, args, LEGACY_SUBCALLFLAGS, callback, closure, final_legacy_subcall_v2, mode_legacy_async);
764 void afb_calls_legacy_hooked_subcall_v2(
765 struct afb_xreq *caller,
768 struct json_object *args,
769 void (*callback)(void*, int, struct json_object*, struct afb_req_x1),
772 afb_hook_xreq_subcall(caller, api, verb, args, LEGACY_SUBCALLFLAGS);
773 do_legacy_async(NULL, caller, api, verb, args, LEGACY_SUBCALLFLAGS, callback, closure, final_legacy_subcall_v2, mode_hooked_legacy_async);
776 void afb_calls_legacy_subcall_v3(
777 struct afb_xreq *caller,
780 struct json_object *args,
781 void (*callback)(void*, int, struct json_object*, struct afb_req_x2 *),
784 do_legacy_async(NULL, caller, api, verb, args, LEGACY_SUBCALLFLAGS, callback, closure, final_legacy_subcall_v3, mode_legacy_async);
787 void afb_calls_legacy_hooked_subcall_v3(
788 struct afb_xreq *caller,
791 struct json_object *args,
792 void (*callback)(void*, int, struct json_object*, struct afb_req_x2 *),
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_v3, mode_hooked_legacy_async);
799 int afb_calls_legacy_subcall_sync(
800 struct afb_xreq *caller,
803 struct json_object *args,
804 struct json_object **result)
806 return do_legacy_sync(NULL, caller, api, verb, args, LEGACY_SUBCALLFLAGS, result, mode_legacy_sync);
809 int afb_calls_legacy_hooked_subcall_sync(
810 struct afb_xreq *caller,
813 struct json_object *args,
814 struct json_object **result)
816 afb_hook_xreq_subcallsync(caller, api, verb, args, LEGACY_SUBCALLFLAGS);
817 return do_legacy_sync(NULL, caller, api, verb, args, LEGACY_SUBCALLFLAGS, result, mode_hooked_legacy_sync);