diff options
author | 2016-10-26 09:23:30 +0200 | |
---|---|---|
committer | 2016-10-28 14:20:43 +0200 | |
commit | 36e53e878baaa9d56c09097ada0b7061266caff8 (patch) | |
tree | 48e7fde72d264eacf1197d8871023d2a00adabbd /src/afb-hook.c | |
parent | 91d08eedaa810edfd38dcb8ce67c66813e5e3dce (diff) |
hook: adding of hook feature for requests
The current implementation is for hooking requests.
It allows implementation of command line option for
debugging. Further development is needed to also
handle events and service API. Also a binding for
debugging would be cool.
Change-Id: Ib1ac4711180db7b4097ed92ebbbf1a1a9fd2cc1c
Signed-off-by: José Bollo <jose.bollo@iot.bzh>
Diffstat (limited to 'src/afb-hook.c')
-rw-r--r-- | src/afb-hook.c | 579 |
1 files changed, 579 insertions, 0 deletions
diff --git a/src/afb-hook.c b/src/afb-hook.c new file mode 100644 index 00000000..2dfd1b1e --- /dev/null +++ b/src/afb-hook.c @@ -0,0 +1,579 @@ +/* + * Copyright (C) 2016 "IoT.bzh" + * Author José Bollo <jose.bollo@iot.bzh> + * + * 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 <stdio.h> +#include <stdarg.h> +#include <string.h> + +#include <json-c/json.h> + +#include <afb/afb-req-itf.h> +#include <afb/afb-event-itf.h> + +#include "afb-context.h" +#include "afb-hook.h" +#include "session.h" +#include "verbose.h" + +/* + * Trace + */ +struct afb_hook { + struct afb_hook *next; /* next hook */ + unsigned refcount; /* reference count */ + char *api; + char *verb; + struct AFB_clientCtx *session; + unsigned flags; /* hook flags */ + struct afb_hook_req_itf *reqitf; + void *closure; +}; + +struct hook_req_observer { + struct afb_hook *hook; + struct hook_req_observer *next; +}; + +/* + * Structure recording a request to hook + */ +struct afb_hook_req { + struct hook_req_observer *observers; /* observers */ + struct afb_context *context; /* context of the request */ + struct afb_req req; /* the request hookd */ + unsigned refcount; /* reference count proxy for request */ + char name[1]; /* hook info for the request */ +}; + +/* + * Structure for handling subcalls callbacks + */ +struct hook_subcall { + struct afb_hook_req *tr; /* hookd request */ + void (*callback)(void*, int, struct json_object*); /* client callback */ + void *cb_closure; /* cient closure */ +}; + +static unsigned hook_count = 0; + +static struct afb_hook *list_of_hooks = NULL; + +/****************************************************************************** + * section: default callbacks for tracing requests + *****************************************************************************/ + +static void _hook_(const struct afb_hook_req *tr, const char *format, ...) +{ + int len; + char *buffer; + va_list ap; + + va_start(ap, format); + len = vasprintf(&buffer, format, ap); + va_end(ap); + + if (len < 0) + NOTICE("tracing %s allocation error", tr->name); + else { + NOTICE("hook %s %s", tr->name, buffer); + free(buffer); + } +} + +static void hook_req_begin_default_cb(void * closure, const struct afb_hook_req *tr) +{ + _hook_(tr, "BEGIN"); +} + +static void hook_req_end_default_cb(void * closure, const struct afb_hook_req *tr) +{ + _hook_(tr, "END"); +} + +static void hook_req_json_default_cb(void * closure, const struct afb_hook_req *tr, struct json_object *obj) +{ + _hook_(tr, "json() -> %s", json_object_to_json_string(obj)); +} + +static void hook_req_get_default_cb(void * closure, const struct afb_hook_req *tr, const char *name, struct afb_arg arg) +{ + _hook_(tr, "get(%s) -> { name: %s, value: %s, path: %s }", name, arg.name, arg.value, arg.path); +} + +static void hook_req_success_default_cb(void * closure, const struct afb_hook_req *tr, struct json_object *obj, const char *info) +{ + _hook_(tr, "success(%s, %s)", json_object_to_json_string(obj), info); +} + +static void hook_req_fail_default_cb(void * closure, const struct afb_hook_req *tr, const char *status, const char *info) +{ + _hook_(tr, "fail(%s, %s)", status, info); +} + +static void hook_req_raw_default_cb(void * closure, const struct afb_hook_req *tr, const char *buffer, size_t size) +{ + _hook_(tr, "raw() -> %.*s", (int)size, buffer); +} + +static void hook_req_send_default_cb(void * closure, const struct afb_hook_req *tr, const char *buffer, size_t size) +{ + _hook_(tr, "send(%.*s)", (int)size, buffer); +} + +static void hook_req_context_get_default_cb(void * closure, const struct afb_hook_req *tr, void *value) +{ + _hook_(tr, "context_get() -> %p", value); +} + +static void hook_req_context_set_default_cb(void * closure, const struct afb_hook_req *tr, void *value, void (*free_value)(void*)) +{ + _hook_(tr, "context_set(%p, %p)", value, free_value); +} + +static void hook_req_addref_default_cb(void * closure, const struct afb_hook_req *tr) +{ + _hook_(tr, "addref()"); +} + +static void hook_req_unref_default_cb(void * closure, const struct afb_hook_req *tr) +{ + _hook_(tr, "unref()"); +} + +static void hook_req_session_close_default_cb(void * closure, const struct afb_hook_req *tr) +{ + _hook_(tr, "session_close()"); +} + +static void hook_req_session_set_LOA_default_cb(void * closure, const struct afb_hook_req *tr, unsigned level, int result) +{ + _hook_(tr, "session_set_LOA(%u) -> %d", level, result); +} + +static void hook_req_subscribe_default_cb(void * closure, const struct afb_hook_req *tr, struct afb_event event, int result) +{ + _hook_(tr, "subscribe(%s:%p) -> %d", afb_event_name(event), event.closure, result); +} + +static void hook_req_unsubscribe_default_cb(void * closure, const struct afb_hook_req *tr, struct afb_event event, int result) +{ + _hook_(tr, "unsubscribe(%s:%p) -> %d", afb_event_name(event), event.closure, result); +} + +static void hook_req_subcall_default_cb(void * closure, const struct afb_hook_req *tr, const char *api, const char *verb, struct json_object *args) +{ + _hook_(tr, "subcall(%s/%s, %s) ...", api, verb, json_object_to_json_string(args)); +} + +static void hook_req_subcall_result_default_cb(void * closure, const struct afb_hook_req *tr, int status, struct json_object *result) +{ + _hook_(tr, " ...subcall... -> %d: %s", status, json_object_to_json_string(result)); +} + +static struct afb_hook_req_itf hook_req_default_itf = { + .hook_req_begin = hook_req_begin_default_cb, + .hook_req_end = hook_req_end_default_cb, + .hook_req_json = hook_req_json_default_cb, + .hook_req_get = hook_req_get_default_cb, + .hook_req_success = hook_req_success_default_cb, + .hook_req_fail = hook_req_fail_default_cb, + .hook_req_raw = hook_req_raw_default_cb, + .hook_req_send = hook_req_send_default_cb, + .hook_req_context_get = hook_req_context_get_default_cb, + .hook_req_context_set = hook_req_context_set_default_cb, + .hook_req_addref = hook_req_addref_default_cb, + .hook_req_unref = hook_req_unref_default_cb, + .hook_req_session_close = hook_req_session_close_default_cb, + .hook_req_session_set_LOA = hook_req_session_set_LOA_default_cb, + .hook_req_subscribe = hook_req_subscribe_default_cb, + .hook_req_unsubscribe = hook_req_unsubscribe_default_cb, + .hook_req_subcall = hook_req_subcall_default_cb, + .hook_req_subcall_result = hook_req_subcall_result_default_cb, +}; + +/****************************************************************************** + * section: macro for tracing requests + *****************************************************************************/ + +#define TRACE_REQX(what,tr) do{\ + struct hook_req_observer *observer = tr->observers;\ + while (observer != NULL) {\ + struct afb_hook *hook = observer->hook;\ + observer = observer->next;\ + if (hook->reqitf && hook->reqitf->hook_req_##what)\ + hook->reqitf->hook_req_##what(hook->closure, tr);\ + }\ + }while(0) + +#define TRACE_REQ_(what,tr) do{\ + struct hook_req_observer *observer = tr->observers;\ + while (observer != NULL) {\ + struct afb_hook *hook = observer->hook;\ + observer = observer->next;\ + if ((hook->flags & afb_hook_flag_req_##what) && hook->reqitf && hook->reqitf->hook_req_##what)\ + hook->reqitf->hook_req_##what(hook->closure, tr);\ + }\ + }while(0) + +#define TRACE_REQ(what,tr,...) do{\ + struct hook_req_observer *observer = tr->observers;\ + while (observer != NULL) {\ + struct afb_hook *hook = observer->hook;\ + observer = observer->next;\ + if ((hook->flags & afb_hook_flag_req_##what) && hook->reqitf && hook->reqitf->hook_req_##what)\ + hook->reqitf->hook_req_##what(hook->closure, tr, __VA_ARGS__);\ + }\ + }while(0) + +/****************************************************************************** + * section: afb_hook_req handling + *****************************************************************************/ + +static void hook_req_addref(struct afb_hook_req *tr) +{ + tr->refcount++; +} + +static void hook_req_unref(struct afb_hook_req *tr) +{ + struct hook_req_observer *o1, *o2; + if (!--tr->refcount) { + TRACE_REQX(end, tr); + afb_req_unref(tr->req); + o1 = tr->observers; + while(o1) { + afb_hook_unref(o1->hook); + o2 = o1->next; + free(o1); + o1 = o2; + } + free(tr); + } +} + +static struct afb_hook_req *hook_req_create(struct afb_req req, struct afb_context *context, const char *api, size_t lenapi, const char *verb, size_t lenverb) +{ + unsigned id; + struct afb_hook_req *tr; + + tr = malloc(sizeof *tr + 8 + lenapi + lenverb); + if (tr != NULL) { + /* get the call id */ + id = ++hook_count; + if (id == 1000000) + id = hook_count = 1; + + /* init hook */ + tr->observers = NULL; + tr->refcount = 1; + tr->context = context; + tr->req = req; + afb_req_addref(req); + snprintf(tr->name, 9 + lenapi + lenverb, "%06d:%.*s/%.*s", id, (int)lenapi, api, (int)lenverb, verb); + } + return tr; +} + +static void hook_req_add_observer(struct afb_hook_req *tr, struct afb_hook *hook) +{ + struct hook_req_observer *observer; + + observer = malloc(sizeof *observer); + if (observer) { + observer->hook = afb_hook_addref(hook); + observer->next = tr->observers; + tr->observers = observer; + } +} + +/****************************************************************************** + * section: hooks for tracing requests + *****************************************************************************/ + +static struct json_object *req_hook_json(void *closure) +{ + struct afb_hook_req *tr = closure; + struct json_object *r; + + r = afb_req_json(tr->req); + TRACE_REQ(json, tr, r); + return r; +} + +static struct afb_arg req_hook_get(void *closure, const char *name) +{ + struct afb_hook_req *tr = closure; + struct afb_arg a; + + a = afb_req_get(tr->req, name); + TRACE_REQ(get, tr, name, a); + return a; +} + +static void req_hook_success(void *closure, struct json_object *obj, const char *info) +{ + struct afb_hook_req *tr = closure; + + TRACE_REQ(success, tr, obj, info); + afb_req_success(tr->req, obj, info); + hook_req_unref(tr); +} + +static void req_hook_fail(void *closure, const char *status, const char *info) +{ + struct afb_hook_req *tr = closure; + + TRACE_REQ(fail, tr, status, info); + afb_req_fail(tr->req, status, info); + hook_req_unref(tr); +} + +static const char *req_hook_raw(void *closure, size_t *size) +{ + struct afb_hook_req *tr = closure; + const char *r; + size_t s; + + r = afb_req_raw(tr->req, &s); + TRACE_REQ(raw, tr, r, s); + if (size) + *size = s; + return r; +} + +static void req_hook_send(void *closure, const char *buffer, size_t size) +{ + struct afb_hook_req *tr = closure; + + TRACE_REQ(send, tr, buffer, size); + afb_req_send(tr->req, buffer, size); +} + +static void *req_hook_context_get(void *closure) +{ + struct afb_hook_req *tr = closure; + void *r; + + r = afb_req_context_get(tr->req); + TRACE_REQ(context_get, tr, r); + + return r; +} + +static void req_hook_context_set(void *closure, void *value, void (*free_value)(void*)) +{ + struct afb_hook_req *tr = closure; + + TRACE_REQ(context_set, tr, value, free_value); + afb_req_context_set(tr->req, value, free_value); +} + +static void req_hook_addref(void *closure) +{ + struct afb_hook_req *tr = closure; + + TRACE_REQ_(addref, tr); + hook_req_addref(tr); +} + +static void req_hook_unref(void *closure) +{ + struct afb_hook_req *tr = closure; + + TRACE_REQ_(unref, tr); + hook_req_unref(tr); +} + +static void req_hook_session_close(void *closure) +{ + struct afb_hook_req *tr = closure; + + TRACE_REQ_(session_close, tr); + afb_req_session_close(tr->req); +} + +static int req_hook_session_set_LOA(void *closure, unsigned level) +{ + struct afb_hook_req *tr = closure; + int r; + + r = afb_req_session_set_LOA(tr->req, level); + TRACE_REQ(session_set_LOA, tr, level, r); + return r; +} + +static int req_hook_subscribe(void *closure, struct afb_event event) +{ + struct afb_hook_req *tr = closure; + int r; + + r = afb_req_subscribe(tr->req, event); + TRACE_REQ(subscribe, tr, event, r); + return r; +} + +static int req_hook_unsubscribe(void *closure, struct afb_event event) +{ + struct afb_hook_req *tr = closure; + int r; + + r = afb_req_unsubscribe(tr->req, event); + TRACE_REQ(unsubscribe, tr, event, r); + return r; +} + +static void req_hook_subcall_result(void *closure, int status, struct json_object *result) +{ + struct hook_subcall *sc = closure; + struct hook_subcall s = *sc; + + free(sc); + TRACE_REQ(subcall_result, s.tr, status, result); + hook_req_unref(s.tr); + s.callback(s.cb_closure, status, result); +} + +static void req_hook_subcall(void *closure, const char *api, const char *verb, struct json_object *args, void (*callback)(void*, int, struct json_object*), void *cb_closure) +{ + struct afb_hook_req *tr = closure; + struct hook_subcall *sc; + + TRACE_REQ(subcall, tr, api, verb, args); + sc = malloc(sizeof *sc); + if (sc) { + sc->tr = tr; + sc->callback = callback; + sc->cb_closure = cb_closure; + hook_req_addref(tr); + cb_closure = sc; + callback = req_hook_subcall_result; + } + afb_req_subcall(tr->req, api, verb, args, callback, cb_closure); +} + +static struct afb_req_itf req_hook_itf = { + .json = req_hook_json, + .get = req_hook_get, + .success = req_hook_success, + .fail = req_hook_fail, + .raw = req_hook_raw, + .send = req_hook_send, + .context_get = req_hook_context_get, + .context_set = req_hook_context_set, + .addref = req_hook_addref, + .unref = req_hook_unref, + .session_close = req_hook_session_close, + .session_set_LOA = req_hook_session_set_LOA, + .subscribe = req_hook_subscribe, + .unsubscribe = req_hook_unsubscribe, + .subcall = req_hook_subcall +}; + +/****************************************************************************** + * section: + *****************************************************************************/ + +struct afb_req afb_hook_req_call(struct afb_req req, struct afb_context *context, const char *api, size_t lenapi, const char *verb, size_t lenverb) +{ + int add; + struct afb_hook_req *tr; + struct afb_hook *hook; + + hook = list_of_hooks; + if (hook) { + tr = NULL; + do { + add = (hook->flags & afb_hook_flags_req_all) != 0 + && (!hook->session || hook->session == context->session) + && (!hook->api || !(memcmp(hook->api, api, lenapi) || hook->api[lenapi])) + && (!hook->verb || !(memcmp(hook->verb, verb, lenverb) || hook->verb[lenverb])); + if (add) { + if (!tr) + tr = hook_req_create(req, context, api, lenapi, verb, lenverb); + if (tr) + hook_req_add_observer(tr, hook); + } + hook = hook->next; + } while(hook); + if (tr) { + req.closure = tr; + req.itf = &req_hook_itf; + TRACE_REQX(begin, tr); + } + } + + return req; +} + +struct afb_hook *afb_hook_req_create(const char *api, const char *verb, struct AFB_clientCtx *session, unsigned flags, struct afb_hook_req_itf *itf, void *closure) +{ + struct afb_hook *hook; + + hook = malloc(sizeof *hook); + if (hook == NULL) + return NULL; + + hook->api = api ? strdup(api) : NULL; + hook->verb = verb ? strdup(verb) : NULL; + hook->session = session ? ctxClientAddRef(session) : NULL; + + if ((api && !hook->api) || (verb && !hook->verb)) { + free(hook->api); + free(hook->verb); + if (hook->session) + ctxClientUnref(hook->session); + free(hook); + return NULL; + } + + hook->refcount = 1; + hook->flags = flags; + hook->reqitf = itf ? itf : &hook_req_default_itf; + hook->closure = closure; + + hook->next = list_of_hooks; + list_of_hooks = hook; + return hook; +} + +struct afb_hook *afb_hook_addref(struct afb_hook *hook) +{ + hook->refcount++; + return hook; +} + +void afb_hook_unref(struct afb_hook *hook) +{ + if (!--hook->refcount) { + /* unlink */ + struct afb_hook **prv = &list_of_hooks; + while (*prv && *prv != hook) + prv = &(*prv)->next; + if(*prv) + *prv = hook->next; + + /* free */ + free(hook->api); + free(hook->verb); + if (hook->session) + ctxClientUnref(hook->session); + free(hook); + } +} + |