X-Git-Url: https://gerrit.automotivelinux.org/gerrit/gitweb?a=blobdiff_plain;f=src%2Fafb-calls.c;fp=src%2Fafb-calls.c;h=73b89bf0656f6d8e9ecbe9779ab16e18222e7e16;hb=4521c1e7ae5371ab9d639adc617d17fb4e8ded0c;hp=0000000000000000000000000000000000000000;hpb=63682b4da9d3e892d1d0a671de860adc43068142;p=src%2Fapp-framework-binder.git diff --git a/src/afb-calls.c b/src/afb-calls.c new file mode 100644 index 00000000..73b89bf0 --- /dev/null +++ b/src/afb-calls.c @@ -0,0 +1,818 @@ +/* + * Copyright (C) 2016, 2017, 2018 "IoT.bzh" + * Author: José Bollo + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define _GNU_SOURCE + +#include +#include +#include + +#include + +#define AFB_BINDING_VERSION 0 +#include + +#include "afb-calls.h" +#include "afb-cred.h" +#include "afb-evt.h" +#include "afb-export.h" +#include "afb-hook.h" +#include "afb-msg-json.h" +#include "afb-session.h" +#include "afb-xreq.h" + +#include "jobs.h" +#include "verbose.h" + +#define CALLFLAGS (afb_req_x2_subcall_api_session|afb_req_x2_subcall_catch_events) +#define LEGACY_SUBCALLFLAGS (afb_req_x2_subcall_pass_events|afb_req_x2_subcall_on_behalf) + + +/************************************************************************/ + +struct modes +{ + unsigned hooked: 1; + unsigned sync: 1; + unsigned legacy: 1; +}; + +#define mode_sync ((struct modes){ .hooked=0, .sync=1, .legacy=0 }) +#define mode_async ((struct modes){ .hooked=0, .sync=0, .legacy=0 }) +#define mode_legacy_sync ((struct modes){ .hooked=0, .sync=1, .legacy=1 }) +#define mode_legacy_async ((struct modes){ .hooked=0, .sync=0, .legacy=1 }) +#define mode_hooked_sync ((struct modes){ .hooked=1, .sync=1, .legacy=0 }) +#define mode_hooked_async ((struct modes){ .hooked=1, .sync=0, .legacy=0 }) +#define mode_hooked_legacy_sync ((struct modes){ .hooked=1, .sync=1, .legacy=1 }) +#define mode_hooked_legacy_async ((struct modes){ .hooked=1, .sync=0, .legacy=1 }) + +union callback { + void *any; + union { + void (*legacy_v1)(void*, int, struct json_object*); + void (*legacy_v2)(void*, int, struct json_object*, struct afb_req_x1); + void (*legacy_v3)(void*, int, struct json_object*, struct afb_req_x2*); + void (*x3)(void*, struct json_object*, const char*, const char *, struct afb_req_x2*); + } subcall; + union { + void (*legacy_v12)(void*, int, struct json_object*); + void (*legacy_v3)(void*, int, struct json_object*, struct afb_api_x3*); + void (*x3)(void*, struct json_object*, const char*, const char*, struct afb_api_x3*); + } call; +}; + +struct callreq +{ + struct afb_xreq xreq; + + struct afb_export *export; + + struct modes mode; + + int flags; + + union { + struct { + struct jobloop *jobloop; + int returned; + int status; + struct json_object **object; + char **error; + char **info; + }; + struct { + union callback callback; + void *closure; + union { + void (*final)(void*, struct json_object*, const char*, const char*, union callback, struct afb_export*,struct afb_xreq*); + void (*legacy_final)(void*, int, struct json_object*, union callback, struct afb_export*,struct afb_xreq*); + }; + }; + }; +}; + +/******************************************************************************/ + +static int store_reply( + struct json_object *iobject, const char *ierror, const char *iinfo, + struct json_object **sobject, char **serror, char **sinfo) +{ + if (serror) { + if (!ierror) + *serror = NULL; + else if (!(*serror = strdup(ierror))) { + ERROR("can't report error %s", ierror); + json_object_put(iobject); + iobject = NULL; + iinfo = NULL; + } + } + + if (sobject) + *sobject = iobject; + else + json_object_put(iobject); + + if (sinfo) { + if (!iinfo) + *sinfo = NULL; + else if (!(*sinfo = strdup(iinfo))) + ERROR("can't report info %s", iinfo); + } + + return -!!ierror; +} + +/******************************************************************************/ + +static void sync_leave(struct callreq *callreq) +{ + struct jobloop *jobloop = __atomic_exchange_n(&callreq->jobloop, NULL, __ATOMIC_RELAXED); + if (jobloop) + jobs_leave(jobloop); +} + +static void sync_enter(int signum, void *closure, struct jobloop *jobloop) +{ + struct callreq *callreq = closure; + if (!signum) { + callreq->jobloop = jobloop; + afb_export_process_xreq(callreq->export, &callreq->xreq); + } else { + afb_xreq_reply(&callreq->xreq, NULL, "internal-error", NULL); + } +} + +/******************************************************************************/ + +static void callreq_destroy_cb(struct afb_xreq *xreq) +{ + struct callreq *callreq = CONTAINER_OF_XREQ(struct callreq, xreq); + + afb_context_disconnect(&callreq->xreq.context); + json_object_put(callreq->xreq.json); + afb_cred_unref(callreq->xreq.cred); + free(callreq); +} + +static void callreq_reply_cb(struct afb_xreq *xreq, struct json_object *object, const char *error, const char *info) +{ + struct callreq *callreq = CONTAINER_OF_XREQ(struct callreq, xreq); + + /* centralized hooking */ + if (callreq->mode.hooked) { + if (callreq->mode.sync) { + if (callreq->xreq.caller) + afb_hook_xreq_subcallsync_result(callreq->xreq.caller, -!!error, object, error, info); + else + afb_hook_api_callsync_result(callreq->export, -!!error, object, error, info); + } else { + if (callreq->xreq.caller) + afb_hook_xreq_subcall_result(callreq->xreq.caller, object, error, info); + else + afb_hook_api_call_result(callreq->export, object, error, info); + } + } + + /* true report of the result */ + if (callreq->mode.sync) { + callreq->returned = 1; + if (callreq->mode.legacy) { + callreq->status = -!!error; + if (callreq->object) + *callreq->object = afb_msg_json_reply(object, error, info, NULL); + else + json_object_put(object); + } else { + callreq->status = store_reply(object, error, info, + callreq->object, callreq->error, callreq->info); + } + sync_leave(callreq); + } else { + if (callreq->mode.legacy) { + object = afb_msg_json_reply(object, error, info, NULL); + callreq->legacy_final(callreq->closure, -!!error, object, callreq->callback, callreq->export, callreq->xreq.caller); + } else { + callreq->final(callreq->closure, object, error, info, callreq->callback, callreq->export, callreq->xreq.caller); + } + json_object_put(object); + } +} + +static int callreq_subscribe_cb(struct afb_xreq *xreq, struct afb_event_x2 *event) +{ + int rc = 0, rc2; + struct callreq *callreq = CONTAINER_OF_XREQ(struct callreq, xreq); + + if (callreq->flags & afb_req_x2_subcall_pass_events) + rc = afb_xreq_subscribe(callreq->xreq.caller, event); + if (callreq->flags & afb_req_x2_subcall_catch_events) { + rc2 = afb_export_subscribe(callreq->export, event); + if (rc2 < 0) + rc = rc2; + } + return rc; +} + +static int callreq_unsubscribe_cb(struct afb_xreq *xreq, struct afb_event_x2 *event) +{ + int rc = 0, rc2; + struct callreq *callreq = CONTAINER_OF_XREQ(struct callreq, xreq); + + if (callreq->flags & afb_req_x2_subcall_pass_events) + rc = afb_xreq_unsubscribe(callreq->xreq.caller, event); + if (callreq->flags & afb_req_x2_subcall_catch_events) { + rc2 = afb_export_unsubscribe(callreq->export, event); + if (rc2 < 0) + rc = rc2; + } + return rc; +} + +/******************************************************************************/ + +const struct afb_xreq_query_itf afb_calls_xreq_itf = { + .unref = callreq_destroy_cb, + .reply = callreq_reply_cb, + .subscribe = callreq_subscribe_cb, + .unsubscribe = callreq_unsubscribe_cb +}; + +/******************************************************************************/ + +static struct callreq *callreq_create( + struct afb_export *export, + struct afb_xreq *caller, + const char *api, + const char *verb, + struct json_object *args, + int flags, + struct modes mode) +{ + struct callreq *callreq; + size_t lenapi, lenverb; + char *api2, *verb2; + + lenapi = 1 + strlen(api); + lenverb = 1 + strlen(verb); + callreq = malloc(lenapi + lenverb + sizeof *callreq); + if (!callreq) { + ERROR("out of memory"); + json_object_put(args); + errno = ENOMEM; + } else { + afb_xreq_init(&callreq->xreq, &afb_calls_xreq_itf); + callreq->xreq.context.validated = 1; + api2 = (char*)&callreq[1]; + callreq->xreq.request.called_api = memcpy(api2, api, lenapi);; + verb2 = &api2[lenapi]; + callreq->xreq.request.called_verb = memcpy(verb2, verb, lenverb); + callreq->xreq.json = args; + callreq->mode = mode; + if (caller) { + export = afb_export_from_api_x3(caller->request.api); + if (flags & afb_req_x2_subcall_on_behalf) + callreq->xreq.cred = afb_cred_addref(caller->cred); + callreq->xreq.caller = caller; + afb_xreq_unhooked_addref(caller); + } + if (caller && (flags & afb_req_x2_subcall_api_session)) + afb_context_subinit(&callreq->xreq.context, &caller->context); + else + afb_export_context_init(export, &callreq->xreq.context); + callreq->export = export; + callreq->flags = flags; + } + return callreq; +} + +/******************************************************************************/ + +static int do_sync( + struct afb_export *export, + struct afb_xreq *caller, + const char *api, + const char *verb, + struct json_object *args, + int flags, + struct json_object **object, + char **error, + char **info, + struct modes mode) +{ + struct callreq *callreq; + int rc; + + /* allocates the request */ + callreq = callreq_create(export, caller, api, verb, args, flags, mode); + if (!callreq) + goto interr; + + /* initializes the request */ + callreq->jobloop = NULL; + callreq->returned = 0; + callreq->status = 0; + callreq->object = object; + callreq->error = error; + callreq->info = info; + + afb_xreq_unhooked_addref(&callreq->xreq); /* avoid early callreq destruction */ + + rc = jobs_enter(NULL, 0, sync_enter, callreq); + if (rc >= 0 && callreq->returned) { + rc = callreq->status; + afb_xreq_unhooked_unref(&callreq->xreq); + return rc; + } + + afb_xreq_unhooked_unref(&callreq->xreq); +interr: + return store_reply(NULL, "internal-error", NULL, object, info, error); +} + +/******************************************************************************/ + +static void do_async( + struct afb_export *export, + struct afb_xreq *caller, + const char *api, + const char *verb, + struct json_object *args, + int flags, + void *callback, + void *closure, + void (*final)(void*, struct json_object*, const char*, const char*, union callback, struct afb_export*,struct afb_xreq*), + struct modes mode) +{ + struct callreq *callreq; + + callreq = callreq_create(export, caller, api, verb, args, flags, mode); + + if (!callreq) + final(closure, NULL, "internal-error", NULL, (union callback){ .any = callback }, export, caller); + else { + callreq->callback.any = callback; + callreq->closure = closure; + callreq->final = final; + + afb_export_process_xreq(callreq->export, &callreq->xreq); + } +} + +/******************************************************************************/ + +static void final_call( + void *closure, + struct json_object *object, + const char *error, + const char *info, + union callback callback, + struct afb_export *export, + struct afb_xreq *caller) +{ + if (callback.call.x3) + callback.call.x3(closure, object, error, info, afb_export_to_api_x3(export)); +} + +static void final_subcall( + void *closure, + struct json_object *object, + const char *error, + const char *info, + union callback callback, + struct afb_export *export, + struct afb_xreq *caller) +{ + if (callback.subcall.x3) + callback.subcall.x3(closure, object, error, info, xreq_to_req_x2(caller)); +} + +/******************************************************************************/ + +void afb_calls_call( + struct afb_export *export, + const char *api, + const char *verb, + struct json_object *args, + void (*callback)(void*, struct json_object*, const char *error, const char *info, struct afb_api_x3*), + void *closure) +{ + do_async(export, NULL, api, verb, args, CALLFLAGS, callback, closure, final_call, mode_async); +} + +void afb_calls_hooked_call( + struct afb_export *export, + const char *api, + const char *verb, + struct json_object *args, + void (*callback)(void*, struct json_object*, const char *error, const char *info, struct afb_api_x3*), + void *closure) +{ + afb_hook_api_call(export, api, verb, args); + do_async(export, NULL, api, verb, args, CALLFLAGS, callback, closure, final_call, mode_hooked_async); +} + +int afb_calls_call_sync( + struct afb_export *export, + const char *api, + const char *verb, + struct json_object *args, + struct json_object **object, + char **error, + char **info) +{ + return do_sync(export, NULL, api, verb, args, CALLFLAGS, object, error, info, mode_sync); +} + +int afb_calls_hooked_call_sync( + struct afb_export *export, + const char *api, + const char *verb, + struct json_object *args, + struct json_object **object, + char **error, + char **info) +{ + afb_hook_api_callsync(export, api, verb, args); + return do_sync(export, NULL, api, verb, args, CALLFLAGS, object, error, info, mode_hooked_sync); +} + +void afb_calls_subcall( + struct afb_xreq *xreq, + const char *api, + const char *verb, + struct json_object *args, + int flags, + void (*callback)(void *closure, struct json_object *object, const char *error, const char * info, struct afb_req_x2 *req), + void *closure) +{ + do_async(NULL, xreq, api, verb, args, flags, callback, closure, final_subcall, mode_async); +} + +void afb_calls_hooked_subcall( + struct afb_xreq *xreq, + const char *api, + const char *verb, + struct json_object *args, + int flags, + void (*callback)(void *closure, struct json_object *object, const char *error, const char * info, struct afb_req_x2 *req), + void *closure) +{ + afb_hook_xreq_subcall(xreq, api, verb, args, flags); + do_async(NULL, xreq, api, verb, args, flags, callback, closure, final_subcall, mode_hooked_async); +} + +int afb_calls_subcall_sync( + struct afb_xreq *xreq, + const char *api, + const char *verb, + struct json_object *args, + int flags, + struct json_object **object, + char **error, + char **info) +{ + return do_sync(NULL, xreq, api, verb, args, flags, object, error, info, mode_sync); +} + +int afb_calls_hooked_subcall_sync( + struct afb_xreq *xreq, + const char *api, + const char *verb, + struct json_object *args, + int flags, + struct json_object **object, + char **error, + char **info) +{ + afb_hook_xreq_subcallsync(xreq, api, verb, args, flags); + return do_sync(NULL, xreq, api, verb, args, flags, object, error, info, mode_hooked_sync); +} + +/******************************************************************************/ +/******************************************************************************/ +/******************************************************************************/ +/******************************************************************************/ +/******************************************************************************/ +/******************************************************************************/ +/******************************************************************************/ +/******************************************************************************/ + +static int do_legacy_sync( + struct afb_export *export, + struct afb_xreq *caller, + const char *api, + const char *verb, + struct json_object *args, + int flags, + struct json_object **object, + struct modes mode) +{ + struct callreq *callreq; + int rc; + + /* allocates the request */ + callreq = callreq_create(export, caller, api, verb, args, flags, mode); + if (!callreq) + goto interr; + + /* initializes the request */ + callreq->jobloop = NULL; + callreq->returned = 0; + callreq->status = 0; + callreq->object = object; + + afb_xreq_unhooked_addref(&callreq->xreq); /* avoid early callreq destruction */ + + rc = jobs_enter(NULL, 0, sync_enter, callreq); + if (rc >= 0 && callreq->returned) { + rc = callreq->status; + afb_xreq_unhooked_unref(&callreq->xreq); + return rc; + } + + afb_xreq_unhooked_unref(&callreq->xreq); +interr: + if (object) + *object = afb_msg_json_internal_error(); + return -1; +} + +/******************************************************************************/ + +static void do_legacy_async( + struct afb_export *export, + struct afb_xreq *caller, + const char *api, + const char *verb, + struct json_object *args, + int flags, + void *callback, + void *closure, + void (*final)(void*, int, struct json_object*, union callback, struct afb_export*,struct afb_xreq*), + struct modes mode) +{ + struct callreq *callreq; + struct json_object *ie; + + callreq = callreq_create(export, caller, api, verb, args, flags, mode); + + if (!callreq) { + ie = afb_msg_json_internal_error(); + final(closure, -1, ie, (union callback){ .any = callback }, export, caller); + json_object_put(ie); + } else { + callreq->callback.any = callback; + callreq->closure = closure; + callreq->legacy_final = final; + + afb_export_process_xreq(callreq->export, &callreq->xreq); + } +} + +/******************************************************************************/ + +static void final_legacy_call_v12( + void *closure, + int status, + struct json_object *object, + union callback callback, + struct afb_export *export, + struct afb_xreq *caller) +{ + if (callback.call.legacy_v12) + callback.call.legacy_v12(closure, status, object); +} + +static void final_legacy_call_v3( + void *closure, + int status, + struct json_object *object, + union callback callback, + struct afb_export *export, + struct afb_xreq *caller) +{ + if (callback.call.legacy_v3) + callback.call.legacy_v3(closure, status, object, afb_export_to_api_x3(export)); +} + +/******************************************************************************/ + +void afb_calls_legacy_call_v12( + struct afb_export *export, + const char *api, + const char *verb, + struct json_object *args, + void (*callback)(void*, int, struct json_object*), + void *closure) +{ + do_legacy_async(export, NULL, api, verb, args, CALLFLAGS, callback, closure, final_legacy_call_v12, mode_legacy_async); +} + +void afb_calls_legacy_hooked_call_v12( + struct afb_export *export, + const char *api, + const char *verb, + struct json_object *args, + void (*callback)(void*, int, struct json_object*), + void *closure) +{ + afb_hook_api_call(export, api, verb, args); + do_legacy_async(export, NULL, api, verb, args, CALLFLAGS, callback, closure, final_legacy_call_v12, mode_hooked_legacy_async); +} + +void afb_calls_legacy_call_v3( + struct afb_export *export, + const char *api, + const char *verb, + struct json_object *args, + void (*callback)(void*, int, struct json_object*, struct afb_api_x3 *), + void *closure) +{ + do_legacy_async(export, NULL, api, verb, args, CALLFLAGS, callback, closure, final_legacy_call_v3, mode_legacy_async); +} + +void afb_calls_legacy_hooked_call_v3( + struct afb_export *export, + const char *api, + const char *verb, + struct json_object *args, + void (*callback)(void*, int, struct json_object*, struct afb_api_x3 *), + void *closure) +{ + afb_hook_api_call(export, api, verb, args); + do_legacy_async(export, NULL, api, verb, args, CALLFLAGS, callback, closure, final_legacy_call_v3, mode_hooked_legacy_async); +} + +int afb_calls_legacy_call_sync( + struct afb_export *export, + const char *api, + const char *verb, + struct json_object *args, + struct json_object **result) +{ + return do_legacy_sync(export, NULL, api, verb, args, CALLFLAGS, result, mode_legacy_sync); +} + +int afb_calls_legacy_hooked_call_sync( + struct afb_export *export, + const char *api, + const char *verb, + struct json_object *args, + struct json_object **result) +{ + int rc; + struct json_object *object; + + afb_hook_api_callsync(export, api, verb, args); + rc = do_legacy_sync(export, NULL, api, verb, args, CALLFLAGS, &object, mode_hooked_legacy_sync); + if (result) + *result = object; + else + json_object_put(object); + return rc; +} + +/******************************************************************************/ + +static void final_legacy_subcall_v1( + void *closure, + int status, + struct json_object *object, + union callback callback, + struct afb_export *export, + struct afb_xreq *caller) +{ + if (callback.subcall.legacy_v1) + callback.subcall.legacy_v1(closure, status, object); +} + +static void final_legacy_subcall_v2( + void *closure, + int status, + struct json_object *object, + union callback callback, + struct afb_export *export, + struct afb_xreq *caller) +{ + if (callback.subcall.legacy_v2) + callback.subcall.legacy_v2(closure, status, object, xreq_to_req_x1(caller)); +} + +static void final_legacy_subcall_v3( + void *closure, + int status, + struct json_object *object, + union callback callback, + struct afb_export *export, + struct afb_xreq *caller) +{ + if (callback.subcall.legacy_v3) + callback.subcall.legacy_v3(closure, status, object, xreq_to_req_x2(caller)); +} + +/******************************************************************************/ + +void afb_calls_legacy_subcall_v1( + struct afb_xreq *caller, + const char *api, + const char *verb, + struct json_object *args, + void (*callback)(void*, int, struct json_object*), + void *closure) +{ + do_legacy_async(NULL, caller, api, verb, args, LEGACY_SUBCALLFLAGS, callback, closure, final_legacy_subcall_v1, mode_legacy_async); +} + +void afb_calls_legacy_hooked_subcall_v1( + struct afb_xreq *caller, + const char *api, + const char *verb, + struct json_object *args, + void (*callback)(void*, int, struct json_object*), + void *closure) +{ + afb_hook_xreq_subcall(caller, api, verb, args, LEGACY_SUBCALLFLAGS); + do_legacy_async(NULL, caller, api, verb, args, LEGACY_SUBCALLFLAGS, callback, closure, final_legacy_subcall_v1, mode_hooked_legacy_async); +} + +void afb_calls_legacy_subcall_v2( + struct afb_xreq *caller, + const char *api, + const char *verb, + struct json_object *args, + void (*callback)(void*, int, struct json_object*, struct afb_req_x1), + void *closure) +{ + do_legacy_async(NULL, caller, api, verb, args, LEGACY_SUBCALLFLAGS, callback, closure, final_legacy_subcall_v2, mode_legacy_async); +} + +void afb_calls_legacy_hooked_subcall_v2( + struct afb_xreq *caller, + const char *api, + const char *verb, + struct json_object *args, + void (*callback)(void*, int, struct json_object*, struct afb_req_x1), + void *closure) +{ + afb_hook_xreq_subcall(caller, api, verb, args, LEGACY_SUBCALLFLAGS); + do_legacy_async(NULL, caller, api, verb, args, LEGACY_SUBCALLFLAGS, callback, closure, final_legacy_subcall_v2, mode_hooked_legacy_async); +} + +void afb_calls_legacy_subcall_v3( + struct afb_xreq *caller, + const char *api, + const char *verb, + struct json_object *args, + void (*callback)(void*, int, struct json_object*, struct afb_req_x2 *), + void *closure) +{ + do_legacy_async(NULL, caller, api, verb, args, LEGACY_SUBCALLFLAGS, callback, closure, final_legacy_subcall_v3, mode_legacy_async); +} + +void afb_calls_legacy_hooked_subcall_v3( + struct afb_xreq *caller, + const char *api, + const char *verb, + struct json_object *args, + void (*callback)(void*, int, struct json_object*, struct afb_req_x2 *), + void *closure) +{ + afb_hook_xreq_subcall(caller, api, verb, args, LEGACY_SUBCALLFLAGS); + do_legacy_async(NULL, caller, api, verb, args, LEGACY_SUBCALLFLAGS, callback, closure, final_legacy_subcall_v3, mode_hooked_legacy_async); +} + +int afb_calls_legacy_subcall_sync( + struct afb_xreq *caller, + const char *api, + const char *verb, + struct json_object *args, + struct json_object **result) +{ + return do_legacy_sync(NULL, caller, api, verb, args, LEGACY_SUBCALLFLAGS, result, mode_legacy_sync); +} + +int afb_calls_legacy_hooked_subcall_sync( + struct afb_xreq *caller, + const char *api, + const char *verb, + struct json_object *args, + struct json_object **result) +{ + afb_hook_xreq_subcallsync(caller, api, verb, args, LEGACY_SUBCALLFLAGS); + return do_legacy_sync(NULL, caller, api, verb, args, LEGACY_SUBCALLFLAGS, result, mode_hooked_legacy_sync); +}