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 const char _internal_error_[] = "internal-error";
112 /******************************************************************************/
114 static int store_reply(
115 struct json_object *iobject, const char *ierror, const char *iinfo,
116 struct json_object **sobject, char **serror, char **sinfo)
121 else if (!(*serror = strdup(ierror))) {
122 ERROR("can't report error %s", ierror);
123 json_object_put(iobject);
132 json_object_put(iobject);
137 else if (!(*sinfo = strdup(iinfo)))
138 ERROR("can't report info %s", iinfo);
144 /******************************************************************************/
146 static void sync_leave(struct callreq *callreq)
148 struct jobloop *jobloop = __atomic_exchange_n(&callreq->jobloop, NULL, __ATOMIC_RELAXED);
153 static void sync_enter(int signum, void *closure, struct jobloop *jobloop)
155 struct callreq *callreq = closure;
157 callreq->jobloop = jobloop;
158 afb_export_process_xreq(callreq->export, &callreq->xreq);
160 afb_xreq_reply(&callreq->xreq, NULL, _internal_error_, NULL);
164 /******************************************************************************/
166 static void callreq_destroy_cb(struct afb_xreq *xreq)
168 struct callreq *callreq = CONTAINER_OF_XREQ(struct callreq, xreq);
170 afb_context_disconnect(&callreq->xreq.context);
171 json_object_put(callreq->xreq.json);
172 afb_cred_unref(callreq->xreq.cred);
176 static void callreq_reply_cb(struct afb_xreq *xreq, struct json_object *object, const char *error, const char *info)
178 struct callreq *callreq = CONTAINER_OF_XREQ(struct callreq, xreq);
180 /* centralized hooking */
181 if (callreq->mode.hooked) {
182 if (callreq->mode.sync) {
183 if (callreq->xreq.caller)
184 afb_hook_xreq_subcallsync_result(callreq->xreq.caller, -!!error, object, error, info);
186 afb_hook_api_callsync_result(callreq->export, -!!error, object, error, info);
188 if (callreq->xreq.caller)
189 afb_hook_xreq_subcall_result(callreq->xreq.caller, object, error, info);
191 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 callreq->xreq.context.validated = 1;
284 api2 = (char*)&callreq[1];
285 callreq->xreq.request.called_api = memcpy(api2, api, lenapi);;
286 verb2 = &api2[lenapi];
287 callreq->xreq.request.called_verb = memcpy(verb2, verb, lenverb);
288 callreq->xreq.json = args;
289 callreq->mode = mode;
291 export = afb_export_from_api_x3(caller->request.api);
292 if (flags & afb_req_x2_subcall_on_behalf)
293 callreq->xreq.cred = afb_cred_addref(caller->cred);
294 callreq->xreq.caller = caller;
295 afb_xreq_unhooked_addref(caller);
297 if (caller && (flags & afb_req_x2_subcall_api_session))
298 afb_context_subinit(&callreq->xreq.context, &caller->context);
300 afb_export_context_init(export, &callreq->xreq.context);
301 callreq->export = export;
302 callreq->flags = flags;
307 /******************************************************************************/
310 struct afb_export *export,
311 struct afb_xreq *caller,
314 struct json_object *args,
316 struct json_object **object,
321 struct callreq *callreq;
324 /* allocates the request */
325 callreq = callreq_create(export, caller, api, verb, args, flags, mode);
329 /* initializes the request */
330 callreq->jobloop = NULL;
331 callreq->returned = 0;
333 callreq->object = object;
334 callreq->error = error;
335 callreq->info = info;
337 afb_xreq_unhooked_addref(&callreq->xreq); /* avoid early callreq destruction */
339 rc = jobs_enter(NULL, 0, sync_enter, callreq);
340 if (rc >= 0 && callreq->returned) {
341 rc = callreq->status;
342 afb_xreq_unhooked_unref(&callreq->xreq);
346 afb_xreq_unhooked_unref(&callreq->xreq);
348 return store_reply(NULL, _internal_error_, NULL, object, error, info);
351 /******************************************************************************/
353 static void do_async(
354 struct afb_export *export,
355 struct afb_xreq *caller,
358 struct json_object *args,
362 void (*final)(void*, struct json_object*, const char*, const char*, union callback, struct afb_export*,struct afb_xreq*),
365 struct callreq *callreq;
367 callreq = callreq_create(export, caller, api, verb, args, flags, mode);
370 final(closure, NULL, _internal_error_, NULL, (union callback){ .any = callback }, export, caller);
372 callreq->callback.any = callback;
373 callreq->closure = closure;
374 callreq->final = final;
376 afb_export_process_xreq(callreq->export, &callreq->xreq);
380 /******************************************************************************/
382 static void final_call(
384 struct json_object *object,
387 union callback callback,
388 struct afb_export *export,
389 struct afb_xreq *caller)
391 if (callback.call.x3)
392 callback.call.x3(closure, object, error, info, afb_export_to_api_x3(export));
395 static void final_subcall(
397 struct json_object *object,
400 union callback callback,
401 struct afb_export *export,
402 struct afb_xreq *caller)
404 if (callback.subcall.x3)
405 callback.subcall.x3(closure, object, error, info, xreq_to_req_x2(caller));
408 /******************************************************************************/
411 struct afb_export *export,
414 struct json_object *args,
415 void (*callback)(void*, struct json_object*, const char *error, const char *info, struct afb_api_x3*),
418 do_async(export, NULL, api, verb, args, CALLFLAGS, callback, closure, final_call, mode_async);
421 void afb_calls_hooked_call(
422 struct afb_export *export,
425 struct json_object *args,
426 void (*callback)(void*, struct json_object*, const char *error, const char *info, struct afb_api_x3*),
429 afb_hook_api_call(export, api, verb, args);
430 do_async(export, NULL, api, verb, args, CALLFLAGS, callback, closure, final_call, mode_hooked_async);
433 int afb_calls_call_sync(
434 struct afb_export *export,
437 struct json_object *args,
438 struct json_object **object,
442 return do_sync(export, NULL, api, verb, args, CALLFLAGS, object, error, info, mode_sync);
445 int afb_calls_hooked_call_sync(
446 struct afb_export *export,
449 struct json_object *args,
450 struct json_object **object,
454 afb_hook_api_callsync(export, api, verb, args);
455 return do_sync(export, NULL, api, verb, args, CALLFLAGS, object, error, info, mode_hooked_sync);
458 void afb_calls_subcall(
459 struct afb_xreq *xreq,
462 struct json_object *args,
464 void (*callback)(void *closure, struct json_object *object, const char *error, const char * info, struct afb_req_x2 *req),
467 do_async(NULL, xreq, api, verb, args, flags, callback, closure, final_subcall, mode_async);
470 void afb_calls_hooked_subcall(
471 struct afb_xreq *xreq,
474 struct json_object *args,
476 void (*callback)(void *closure, struct json_object *object, const char *error, const char * info, struct afb_req_x2 *req),
479 afb_hook_xreq_subcall(xreq, api, verb, args, flags);
480 do_async(NULL, xreq, api, verb, args, flags, callback, closure, final_subcall, mode_hooked_async);
483 int afb_calls_subcall_sync(
484 struct afb_xreq *xreq,
487 struct json_object *args,
489 struct json_object **object,
493 return do_sync(NULL, xreq, api, verb, args, flags, object, error, info, mode_sync);
496 int afb_calls_hooked_subcall_sync(
497 struct afb_xreq *xreq,
500 struct json_object *args,
502 struct json_object **object,
506 afb_hook_xreq_subcallsync(xreq, api, verb, args, flags);
507 return do_sync(NULL, xreq, api, verb, args, flags, object, error, info, mode_hooked_sync);
510 /******************************************************************************/
511 /******************************************************************************/
512 /******************************************************************************/
513 /******************************************************************************/
514 /******************************************************************************/
515 /******************************************************************************/
516 /******************************************************************************/
517 /******************************************************************************/
519 static int do_legacy_sync(
520 struct afb_export *export,
521 struct afb_xreq *caller,
524 struct json_object *args,
526 struct json_object **object,
529 struct callreq *callreq;
532 /* allocates the request */
533 callreq = callreq_create(export, caller, api, verb, args, flags, mode);
537 /* initializes the request */
538 callreq->jobloop = NULL;
539 callreq->returned = 0;
541 callreq->object = object;
543 afb_xreq_unhooked_addref(&callreq->xreq); /* avoid early callreq destruction */
545 rc = jobs_enter(NULL, 0, sync_enter, callreq);
546 if (rc >= 0 && callreq->returned) {
547 rc = callreq->status;
548 afb_xreq_unhooked_unref(&callreq->xreq);
552 afb_xreq_unhooked_unref(&callreq->xreq);
555 *object = afb_msg_json_reply(NULL, _internal_error_, NULL, NULL);
559 /******************************************************************************/
561 static void do_legacy_async(
562 struct afb_export *export,
563 struct afb_xreq *caller,
566 struct json_object *args,
570 void (*final)(void*, int, struct json_object*, union callback, struct afb_export*,struct afb_xreq*),
573 struct callreq *callreq;
574 struct json_object *ie;
576 callreq = callreq_create(export, caller, api, verb, args, flags, mode);
579 ie = afb_msg_json_reply(NULL, _internal_error_, NULL, NULL);
580 final(closure, -1, ie, (union callback){ .any = callback }, export, caller);
583 callreq->callback.any = callback;
584 callreq->closure = closure;
585 callreq->legacy_final = final;
587 afb_export_process_xreq(callreq->export, &callreq->xreq);
591 /******************************************************************************/
593 static void final_legacy_call_v12(
596 struct json_object *object,
597 union callback callback,
598 struct afb_export *export,
599 struct afb_xreq *caller)
601 if (callback.call.legacy_v12)
602 callback.call.legacy_v12(closure, status, object);
605 static void final_legacy_call_v3(
608 struct json_object *object,
609 union callback callback,
610 struct afb_export *export,
611 struct afb_xreq *caller)
613 if (callback.call.legacy_v3)
614 callback.call.legacy_v3(closure, status, object, afb_export_to_api_x3(export));
617 /******************************************************************************/
619 void afb_calls_legacy_call_v12(
620 struct afb_export *export,
623 struct json_object *args,
624 void (*callback)(void*, int, struct json_object*),
627 do_legacy_async(export, NULL, api, verb, args, CALLFLAGS, callback, closure, final_legacy_call_v12, mode_legacy_async);
630 void afb_calls_legacy_hooked_call_v12(
631 struct afb_export *export,
634 struct json_object *args,
635 void (*callback)(void*, int, struct json_object*),
638 afb_hook_api_call(export, api, verb, args);
639 do_legacy_async(export, NULL, api, verb, args, CALLFLAGS, callback, closure, final_legacy_call_v12, mode_hooked_legacy_async);
642 void afb_calls_legacy_call_v3(
643 struct afb_export *export,
646 struct json_object *args,
647 void (*callback)(void*, int, struct json_object*, struct afb_api_x3 *),
650 do_legacy_async(export, NULL, api, verb, args, CALLFLAGS, callback, closure, final_legacy_call_v3, mode_legacy_async);
653 void afb_calls_legacy_hooked_call_v3(
654 struct afb_export *export,
657 struct json_object *args,
658 void (*callback)(void*, int, struct json_object*, struct afb_api_x3 *),
661 afb_hook_api_call(export, api, verb, args);
662 do_legacy_async(export, NULL, api, verb, args, CALLFLAGS, callback, closure, final_legacy_call_v3, mode_hooked_legacy_async);
665 int afb_calls_legacy_call_sync(
666 struct afb_export *export,
669 struct json_object *args,
670 struct json_object **result)
672 return do_legacy_sync(export, NULL, api, verb, args, CALLFLAGS, result, mode_legacy_sync);
675 int afb_calls_legacy_hooked_call_sync(
676 struct afb_export *export,
679 struct json_object *args,
680 struct json_object **result)
683 struct json_object *object;
685 afb_hook_api_callsync(export, api, verb, args);
686 rc = do_legacy_sync(export, NULL, api, verb, args, CALLFLAGS, &object, mode_hooked_legacy_sync);
690 json_object_put(object);
694 /******************************************************************************/
696 static void final_legacy_subcall_v1(
699 struct json_object *object,
700 union callback callback,
701 struct afb_export *export,
702 struct afb_xreq *caller)
704 if (callback.subcall.legacy_v1)
705 callback.subcall.legacy_v1(closure, status, object);
708 static void final_legacy_subcall_v2(
711 struct json_object *object,
712 union callback callback,
713 struct afb_export *export,
714 struct afb_xreq *caller)
716 if (callback.subcall.legacy_v2)
717 callback.subcall.legacy_v2(closure, status, object, xreq_to_req_x1(caller));
720 static void final_legacy_subcall_v3(
723 struct json_object *object,
724 union callback callback,
725 struct afb_export *export,
726 struct afb_xreq *caller)
728 if (callback.subcall.legacy_v3)
729 callback.subcall.legacy_v3(closure, status, object, xreq_to_req_x2(caller));
732 /******************************************************************************/
734 void afb_calls_legacy_subcall_v1(
735 struct afb_xreq *caller,
738 struct json_object *args,
739 void (*callback)(void*, int, struct json_object*),
742 do_legacy_async(NULL, caller, api, verb, args, LEGACY_SUBCALLFLAGS, callback, closure, final_legacy_subcall_v1, mode_legacy_async);
745 void afb_calls_legacy_hooked_subcall_v1(
746 struct afb_xreq *caller,
749 struct json_object *args,
750 void (*callback)(void*, int, struct json_object*),
753 afb_hook_xreq_subcall(caller, api, verb, args, LEGACY_SUBCALLFLAGS);
754 do_legacy_async(NULL, caller, api, verb, args, LEGACY_SUBCALLFLAGS, callback, closure, final_legacy_subcall_v1, mode_hooked_legacy_async);
757 void afb_calls_legacy_subcall_v2(
758 struct afb_xreq *caller,
761 struct json_object *args,
762 void (*callback)(void*, int, struct json_object*, struct afb_req_x1),
765 do_legacy_async(NULL, caller, api, verb, args, LEGACY_SUBCALLFLAGS, callback, closure, final_legacy_subcall_v2, mode_legacy_async);
768 void afb_calls_legacy_hooked_subcall_v2(
769 struct afb_xreq *caller,
772 struct json_object *args,
773 void (*callback)(void*, int, struct json_object*, struct afb_req_x1),
776 afb_hook_xreq_subcall(caller, api, verb, args, LEGACY_SUBCALLFLAGS);
777 do_legacy_async(NULL, caller, api, verb, args, LEGACY_SUBCALLFLAGS, callback, closure, final_legacy_subcall_v2, mode_hooked_legacy_async);
780 void afb_calls_legacy_subcall_v3(
781 struct afb_xreq *caller,
784 struct json_object *args,
785 void (*callback)(void*, int, struct json_object*, struct afb_req_x2 *),
788 do_legacy_async(NULL, caller, api, verb, args, LEGACY_SUBCALLFLAGS, callback, closure, final_legacy_subcall_v3, mode_legacy_async);
791 void afb_calls_legacy_hooked_subcall_v3(
792 struct afb_xreq *caller,
795 struct json_object *args,
796 void (*callback)(void*, int, struct json_object*, struct afb_req_x2 *),
799 afb_hook_xreq_subcall(caller, api, verb, args, LEGACY_SUBCALLFLAGS);
800 do_legacy_async(NULL, caller, api, verb, args, LEGACY_SUBCALLFLAGS, callback, closure, final_legacy_subcall_v3, mode_hooked_legacy_async);
803 int afb_calls_legacy_subcall_sync(
804 struct afb_xreq *caller,
807 struct json_object *args,
808 struct json_object **result)
810 return do_legacy_sync(NULL, caller, api, verb, args, LEGACY_SUBCALLFLAGS, result, mode_legacy_sync);
813 int afb_calls_legacy_hooked_subcall_sync(
814 struct afb_xreq *caller,
817 struct json_object *args,
818 struct json_object **result)
820 afb_hook_xreq_subcallsync(caller, api, verb, args, LEGACY_SUBCALLFLAGS);
821 return do_legacy_sync(NULL, caller, api, verb, args, LEGACY_SUBCALLFLAGS, result, mode_hooked_legacy_sync);