2 * Copyright (C) 2016, 2017 "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.
27 #include <json-c/json.h>
29 #include <afb/afb-req-itf.h>
30 #include <afb/afb-event-itf.h>
32 #include "afb-context.h"
34 #include "afb-session.h"
41 * Definition of a hook for xreq
43 struct afb_hook_xreq {
44 struct afb_hook_xreq *next; /**< next hook */
45 unsigned refcount; /**< reference count */
46 char *api; /**< api hooked or NULL for any */
47 char *verb; /**< verb hooked or NULL for any */
48 struct afb_session *session; /**< session hooked or NULL if any */
49 unsigned flags; /**< hook flags */
50 struct afb_hook_xreq_itf *itf; /**< interface of hook */
51 void *closure; /**< closure for callbacks */
55 * Definition of a hook for ditf
57 struct afb_hook_ditf {
58 struct afb_hook_ditf *next; /**< next hook */
59 unsigned refcount; /**< reference count */
60 char *api; /**< api hooked or NULL for any */
61 unsigned flags; /**< hook flags */
62 struct afb_hook_ditf_itf *itf; /**< interface of hook */
63 void *closure; /**< closure for callbacks */
66 /* synchronisation across threads */
67 static pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
69 /* list of hooks for xreq */
70 static struct afb_hook_xreq *list_of_xreq_hooks = NULL;
72 /* list of hooks for ditf */
73 static struct afb_hook_ditf *list_of_ditf_hooks = NULL;
75 /******************************************************************************
76 * section: default callbacks for tracing requests
77 *****************************************************************************/
79 static void _hook_xreq_(const struct afb_xreq *xreq, const char *format, ...)
86 len = vasprintf(&buffer, format, ap);
90 NOTICE("hook xreq-%06d:%s/%s allocation error", xreq->hookindex, xreq->api, xreq->verb);
92 NOTICE("hook xreq-%06d:%s/%s %s", xreq->hookindex, xreq->api, xreq->verb, buffer);
97 static void hook_xreq_begin_default_cb(void * closure, const struct afb_xreq *xreq)
100 _hook_xreq_(xreq, "BEGIN");
102 _hook_xreq_(xreq, "BEGIN uid=%d=%s gid=%d pid=%d label=%s id=%s",
103 (int)xreq->cred->uid,
105 (int)xreq->cred->gid,
106 (int)xreq->cred->pid,
107 xreq->cred->label?:"(null)",
108 xreq->cred->id?:"(null)"
112 static void hook_xreq_end_default_cb(void * closure, const struct afb_xreq *xreq)
114 _hook_xreq_(xreq, "END");
117 static void hook_xreq_json_default_cb(void * closure, const struct afb_xreq *xreq, struct json_object *obj)
119 _hook_xreq_(xreq, "json() -> %s", json_object_to_json_string(obj));
122 static void hook_xreq_get_default_cb(void * closure, const struct afb_xreq *xreq, const char *name, struct afb_arg arg)
124 _hook_xreq_(xreq, "get(%s) -> { name: %s, value: %s, path: %s }", name, arg.name, arg.value, arg.path);
127 static void hook_xreq_success_default_cb(void * closure, const struct afb_xreq *xreq, struct json_object *obj, const char *info)
129 _hook_xreq_(xreq, "success(%s, %s)", json_object_to_json_string(obj), info);
132 static void hook_xreq_fail_default_cb(void * closure, const struct afb_xreq *xreq, const char *status, const char *info)
134 _hook_xreq_(xreq, "fail(%s, %s)", status, info);
137 static void hook_xreq_raw_default_cb(void * closure, const struct afb_xreq *xreq, const char *buffer, size_t size)
139 _hook_xreq_(xreq, "raw() -> %.*s", (int)size, buffer);
142 static void hook_xreq_send_default_cb(void * closure, const struct afb_xreq *xreq, const char *buffer, size_t size)
144 _hook_xreq_(xreq, "send(%.*s)", (int)size, buffer);
147 static void hook_xreq_context_get_default_cb(void * closure, const struct afb_xreq *xreq, void *value)
149 _hook_xreq_(xreq, "context_get() -> %p", value);
152 static void hook_xreq_context_set_default_cb(void * closure, const struct afb_xreq *xreq, void *value, void (*free_value)(void*))
154 _hook_xreq_(xreq, "context_set(%p, %p)", value, free_value);
157 static void hook_xreq_addref_default_cb(void * closure, const struct afb_xreq *xreq)
159 _hook_xreq_(xreq, "addref()");
162 static void hook_xreq_unref_default_cb(void * closure, const struct afb_xreq *xreq)
164 _hook_xreq_(xreq, "unref()");
167 static void hook_xreq_session_close_default_cb(void * closure, const struct afb_xreq *xreq)
169 _hook_xreq_(xreq, "session_close()");
172 static void hook_xreq_session_set_LOA_default_cb(void * closure, const struct afb_xreq *xreq, unsigned level, int result)
174 _hook_xreq_(xreq, "session_set_LOA(%u) -> %d", level, result);
177 static void hook_xreq_subscribe_default_cb(void * closure, const struct afb_xreq *xreq, struct afb_event event, int result)
179 _hook_xreq_(xreq, "subscribe(%s:%p) -> %d", afb_event_name(event), event.closure, result);
182 static void hook_xreq_unsubscribe_default_cb(void * closure, const struct afb_xreq *xreq, struct afb_event event, int result)
184 _hook_xreq_(xreq, "unsubscribe(%s:%p) -> %d", afb_event_name(event), event.closure, result);
187 static void hook_xreq_subcall_default_cb(void * closure, const struct afb_xreq *xreq, const char *api, const char *verb, struct json_object *args)
189 _hook_xreq_(xreq, "subcall(%s/%s, %s) ...", api, verb, json_object_to_json_string(args));
192 static void hook_xreq_subcall_result_default_cb(void * closure, const struct afb_xreq *xreq, int status, struct json_object *result)
194 _hook_xreq_(xreq, " ...subcall... -> %d: %s", status, json_object_to_json_string(result));
197 static void hook_xreq_subcallsync_default_cb(void * closure, const struct afb_xreq *xreq, const char *api, const char *verb, struct json_object *args)
199 _hook_xreq_(xreq, "subcallsync(%s/%s, %s) ...", api, verb, json_object_to_json_string(args));
202 static void hook_xreq_subcallsync_result_default_cb(void * closure, const struct afb_xreq *xreq, int status, struct json_object *result)
204 _hook_xreq_(xreq, " ...subcallsync... -> %d: %s", status, json_object_to_json_string(result));
207 static struct afb_hook_xreq_itf hook_xreq_default_itf = {
208 .hook_xreq_begin = hook_xreq_begin_default_cb,
209 .hook_xreq_end = hook_xreq_end_default_cb,
210 .hook_xreq_json = hook_xreq_json_default_cb,
211 .hook_xreq_get = hook_xreq_get_default_cb,
212 .hook_xreq_success = hook_xreq_success_default_cb,
213 .hook_xreq_fail = hook_xreq_fail_default_cb,
214 .hook_xreq_raw = hook_xreq_raw_default_cb,
215 .hook_xreq_send = hook_xreq_send_default_cb,
216 .hook_xreq_context_get = hook_xreq_context_get_default_cb,
217 .hook_xreq_context_set = hook_xreq_context_set_default_cb,
218 .hook_xreq_addref = hook_xreq_addref_default_cb,
219 .hook_xreq_unref = hook_xreq_unref_default_cb,
220 .hook_xreq_session_close = hook_xreq_session_close_default_cb,
221 .hook_xreq_session_set_LOA = hook_xreq_session_set_LOA_default_cb,
222 .hook_xreq_subscribe = hook_xreq_subscribe_default_cb,
223 .hook_xreq_unsubscribe = hook_xreq_unsubscribe_default_cb,
224 .hook_xreq_subcall = hook_xreq_subcall_default_cb,
225 .hook_xreq_subcall_result = hook_xreq_subcall_result_default_cb,
226 .hook_xreq_subcallsync = hook_xreq_subcallsync_default_cb,
227 .hook_xreq_subcallsync_result = hook_xreq_subcallsync_result_default_cb,
230 /******************************************************************************
231 * section: hooks for tracing requests
232 *****************************************************************************/
234 #define _HOOK_XREQ_(what,...) \
235 struct afb_hook_xreq *hook; \
236 pthread_rwlock_rdlock(&rwlock); \
237 hook = list_of_xreq_hooks; \
239 if (hook->itf->hook_xreq_##what \
240 && (hook->flags & afb_hook_flag_req_##what) != 0 \
241 && (!hook->session || hook->session == xreq->context.session) \
242 && (!hook->api || !strcasecmp(hook->api, xreq->api)) \
243 && (!hook->verb || !strcasecmp(hook->verb, xreq->verb))) { \
244 hook->itf->hook_xreq_##what(hook->closure, __VA_ARGS__); \
248 pthread_rwlock_unlock(&rwlock);
251 void afb_hook_xreq_begin(const struct afb_xreq *xreq)
253 _HOOK_XREQ_(begin, xreq);
256 void afb_hook_xreq_end(const struct afb_xreq *xreq)
258 _HOOK_XREQ_(end, xreq);
261 struct json_object *afb_hook_xreq_json(const struct afb_xreq *xreq, struct json_object *obj)
263 _HOOK_XREQ_(json, xreq, obj);
267 struct afb_arg afb_hook_xreq_get(const struct afb_xreq *xreq, const char *name, struct afb_arg arg)
269 _HOOK_XREQ_(get, xreq, name, arg);
273 void afb_hook_xreq_success(const struct afb_xreq *xreq, struct json_object *obj, const char *info)
275 _HOOK_XREQ_(success, xreq, obj, info);
278 void afb_hook_xreq_fail(const struct afb_xreq *xreq, const char *status, const char *info)
280 _HOOK_XREQ_(fail, xreq, status, info);
283 const char *afb_hook_xreq_raw(const struct afb_xreq *xreq, const char *buffer, size_t size)
285 _HOOK_XREQ_(raw, xreq, buffer, size);
289 void afb_hook_xreq_send(const struct afb_xreq *xreq, const char *buffer, size_t size)
291 _HOOK_XREQ_(send, xreq, buffer, size);
294 void *afb_hook_xreq_context_get(const struct afb_xreq *xreq, void *value)
296 _HOOK_XREQ_(context_get, xreq, value);
300 void afb_hook_xreq_context_set(const struct afb_xreq *xreq, void *value, void (*free_value)(void*))
302 _HOOK_XREQ_(context_set, xreq, value, free_value);
305 void afb_hook_xreq_addref(const struct afb_xreq *xreq)
307 _HOOK_XREQ_(addref, xreq);
310 void afb_hook_xreq_unref(const struct afb_xreq *xreq)
312 _HOOK_XREQ_(unref, xreq);
315 void afb_hook_xreq_session_close(const struct afb_xreq *xreq)
317 _HOOK_XREQ_(session_close, xreq);
320 int afb_hook_xreq_session_set_LOA(const struct afb_xreq *xreq, unsigned level, int result)
322 _HOOK_XREQ_(session_set_LOA, xreq, level, result);
326 int afb_hook_xreq_subscribe(const struct afb_xreq *xreq, struct afb_event event, int result)
328 _HOOK_XREQ_(subscribe, xreq, event, result);
332 int afb_hook_xreq_unsubscribe(const struct afb_xreq *xreq, struct afb_event event, int result)
334 _HOOK_XREQ_(unsubscribe, xreq, event, result);
338 void afb_hook_xreq_subcall(const struct afb_xreq *xreq, const char *api, const char *verb, struct json_object *args)
340 _HOOK_XREQ_(subcall, xreq, api, verb, args);
343 void afb_hook_xreq_subcall_result(const struct afb_xreq *xreq, int status, struct json_object *result)
345 _HOOK_XREQ_(subcall_result, xreq, status, result);
348 void afb_hook_xreq_subcallsync(const struct afb_xreq *xreq, const char *api, const char *verb, struct json_object *args)
350 _HOOK_XREQ_(subcallsync, xreq, api, verb, args);
353 int afb_hook_xreq_subcallsync_result(const struct afb_xreq *xreq, int status, struct json_object *result)
355 _HOOK_XREQ_(subcallsync_result, xreq, status, result);
359 /******************************************************************************
361 *****************************************************************************/
363 void afb_hook_init_xreq(struct afb_xreq *xreq)
369 struct afb_hook_xreq *hook;
371 /* scan hook list to get the expected flags */
373 pthread_rwlock_rdlock(&rwlock);
374 hook = list_of_xreq_hooks;
376 f = hook->flags & afb_hook_flags_req_all;
378 && (!hook->session || hook->session == xreq->context.session)
379 && (!hook->api || !strcasecmp(hook->api, xreq->api))
380 && (!hook->verb || !strcasecmp(hook->verb, xreq->verb));
385 pthread_rwlock_unlock(&rwlock);
387 /* store the hooking data */
388 xreq->hookflags = flags;
390 pthread_rwlock_wrlock(&rwlock);
393 xreq->hookindex = reqindex;
394 pthread_rwlock_unlock(&rwlock);
398 struct afb_hook_xreq *afb_hook_create_xreq(const char *api, const char *verb, struct afb_session *session, int flags, struct afb_hook_xreq_itf *itf, void *closure)
400 struct afb_hook_xreq *hook;
402 /* alloc the result */
403 hook = calloc(1, sizeof *hook);
407 /* get a copy of the names */
408 hook->api = api ? strdup(api) : NULL;
409 hook->verb = verb ? strdup(verb) : NULL;
410 if ((api && !hook->api) || (verb && !hook->verb)) {
417 /* initialise the rest */
418 hook->session = session;
420 afb_session_addref(session);
423 hook->itf = itf ? itf : &hook_xreq_default_itf;
424 hook->closure = closure;
426 /* record the hook */
427 pthread_rwlock_wrlock(&rwlock);
428 hook->next = list_of_xreq_hooks;
429 list_of_xreq_hooks = hook;
430 pthread_rwlock_unlock(&rwlock);
436 struct afb_hook_xreq *afb_hook_addref_xreq(struct afb_hook_xreq *hook)
438 pthread_rwlock_wrlock(&rwlock);
440 pthread_rwlock_unlock(&rwlock);
444 void afb_hook_unref_xreq(struct afb_hook_xreq *hook)
446 struct afb_hook_xreq **prv;
449 pthread_rwlock_wrlock(&rwlock);
450 if (--hook->refcount)
454 prv = &list_of_xreq_hooks;
455 while (*prv && *prv != hook)
460 pthread_rwlock_unlock(&rwlock);
466 afb_session_unref(hook->session);
472 /******************************************************************************
473 * section: default callbacks for tracing daemon interface
474 *****************************************************************************/
476 static void _hook_ditf_(const struct afb_ditf *ditf, const char *format, ...)
482 va_start(ap, format);
483 len = vasprintf(&buffer, format, ap);
487 NOTICE("hook ditf-%s allocation error for %s", ditf->prefix, format);
489 NOTICE("hook ditf-%s %s", ditf->prefix, buffer);
494 static void hook_ditf_event_broadcast_before_cb(void *closure, const struct afb_ditf *ditf, const char *name, struct json_object *object)
496 _hook_ditf_(ditf, "event_broadcast.before(%s, %s)....", name, json_object_to_json_string(object));
499 static void hook_ditf_event_broadcast_after_cb(void *closure, const struct afb_ditf *ditf, const char *name, struct json_object *object, int result)
501 _hook_ditf_(ditf, "event_broadcast.after(%s, %s) -> %d", name, json_object_to_json_string(object), result);
504 static void hook_ditf_get_event_loop_cb(void *closure, const struct afb_ditf *ditf, struct sd_event *result)
506 _hook_ditf_(ditf, "get_event_loop() -> %p", result);
509 static void hook_ditf_get_user_bus_cb(void *closure, const struct afb_ditf *ditf, struct sd_bus *result)
511 _hook_ditf_(ditf, "get_user_bus() -> %p", result);
514 static void hook_ditf_get_system_bus_cb(void *closure, const struct afb_ditf *ditf, struct sd_bus *result)
516 _hook_ditf_(ditf, "get_system_bus() -> %p", result);
519 static void hook_ditf_vverbose_cb(void*closure, const struct afb_ditf *ditf, int level, const char *file, int line, const char *function, const char *fmt, va_list args)
526 len = vasprintf(&msg, fmt, ap);
530 _hook_ditf_(ditf, "vverbose(%d, %s, %d, %s) -> %s ? ? ?", level, file, line, function, fmt);
532 _hook_ditf_(ditf, "vverbose(%d, %s, %d, %s) -> %s", level, file, line, function, msg);
537 static void hook_ditf_event_make_cb(void *closure, const struct afb_ditf *ditf, const char *name, struct afb_event result)
539 _hook_ditf_(ditf, "event_make(%s) -> %s:%p", name, afb_event_name(result), result.closure);
542 static void hook_ditf_rootdir_get_fd_cb(void *closure, const struct afb_ditf *ditf, int result)
546 _hook_ditf_(ditf, "rootdir_get_fd() -> %d, %m", result);
548 sprintf(path, "/proc/self/fd/%d", result);
549 readlink(path, path, sizeof path);
550 _hook_ditf_(ditf, "rootdir_get_fd() -> %d = %s", result, path);
554 static void hook_ditf_rootdir_open_locale_cb(void *closure, const struct afb_ditf *ditf, const char *filename, int flags, const char *locale, int result)
560 _hook_ditf_(ditf, "rootdir_open_locale(%s, %d, %s) -> %d, %m", filename, flags, locale, result);
562 sprintf(path, "/proc/self/fd/%d", result);
563 readlink(path, path, sizeof path);
564 _hook_ditf_(ditf, "rootdir_open_locale(%s, %d, %s) -> %d = %s", filename, flags, locale, result, path);
568 static void hook_ditf_queue_job(void *closure, const struct afb_ditf *ditf, void (*callback)(int signum, void *arg), void *argument, void *group, int timeout, int result)
570 _hook_ditf_(ditf, "queue_job(%p, %p, %p, %d) -> %d", callback, argument, group, timeout, result);
573 static struct afb_hook_ditf_itf hook_ditf_default_itf = {
574 .hook_ditf_event_broadcast_before = hook_ditf_event_broadcast_before_cb,
575 .hook_ditf_event_broadcast_after = hook_ditf_event_broadcast_after_cb,
576 .hook_ditf_get_event_loop = hook_ditf_get_event_loop_cb,
577 .hook_ditf_get_user_bus = hook_ditf_get_user_bus_cb,
578 .hook_ditf_get_system_bus = hook_ditf_get_system_bus_cb,
579 .hook_ditf_vverbose = hook_ditf_vverbose_cb,
580 .hook_ditf_event_make = hook_ditf_event_make_cb,
581 .hook_ditf_rootdir_get_fd = hook_ditf_rootdir_get_fd_cb,
582 .hook_ditf_rootdir_open_locale = hook_ditf_rootdir_open_locale_cb,
583 .hook_ditf_queue_job = hook_ditf_queue_job
586 /******************************************************************************
587 * section: hooks for tracing requests
588 *****************************************************************************/
590 #define _HOOK_DITF_(what,...) \
591 struct afb_hook_ditf *hook; \
592 pthread_rwlock_rdlock(&rwlock); \
593 hook = list_of_ditf_hooks; \
595 if (hook->itf->hook_ditf_##what \
596 && (hook->flags & afb_hook_flag_ditf_##what) != 0 \
597 && (!hook->api || !strcasecmp(hook->api, ditf->prefix))) { \
598 hook->itf->hook_ditf_##what(hook->closure, __VA_ARGS__); \
602 pthread_rwlock_unlock(&rwlock);
604 void afb_hook_ditf_event_broadcast_before(const struct afb_ditf *ditf, const char *name, struct json_object *object)
606 _HOOK_DITF_(event_broadcast_before, ditf, name, object);
609 int afb_hook_ditf_event_broadcast_after(const struct afb_ditf *ditf, const char *name, struct json_object *object, int result)
611 _HOOK_DITF_(event_broadcast_after, ditf, name, object, result);
615 struct sd_event *afb_hook_ditf_get_event_loop(const struct afb_ditf *ditf, struct sd_event *result)
617 _HOOK_DITF_(get_event_loop, ditf, result);
621 struct sd_bus *afb_hook_ditf_get_user_bus(const struct afb_ditf *ditf, struct sd_bus *result)
623 _HOOK_DITF_(get_user_bus, ditf, result);
627 struct sd_bus *afb_hook_ditf_get_system_bus(const struct afb_ditf *ditf, struct sd_bus *result)
629 _HOOK_DITF_(get_system_bus, ditf, result);
633 void afb_hook_ditf_vverbose(const struct afb_ditf *ditf, int level, const char *file, int line, const char *function, const char *fmt, va_list args)
635 _HOOK_DITF_(vverbose, ditf, level, file, line, function, fmt, args);
638 struct afb_event afb_hook_ditf_event_make(const struct afb_ditf *ditf, const char *name, struct afb_event result)
640 _HOOK_DITF_(event_make, ditf, name, result);
644 int afb_hook_ditf_rootdir_get_fd(const struct afb_ditf *ditf, int result)
646 _HOOK_DITF_(rootdir_get_fd, ditf, result);
650 int afb_hook_ditf_rootdir_open_locale(const struct afb_ditf *ditf, const char *filename, int flags, const char *locale, int result)
652 _HOOK_DITF_(rootdir_open_locale, ditf, filename, flags, locale, result);
656 int afb_hook_ditf_queue_job(const struct afb_ditf *ditf, void (*callback)(int signum, void *arg), void *argument, void *group, int timeout, int result)
658 _HOOK_DITF_(queue_job, ditf, callback, argument, group, timeout, result);
662 /******************************************************************************
664 *****************************************************************************/
666 int afb_hook_flags_ditf(const char *api)
669 struct afb_hook_ditf *hook;
671 pthread_rwlock_rdlock(&rwlock);
673 hook = list_of_ditf_hooks;
675 if (!api || !hook->api || !strcasecmp(hook->api, api))
676 flags |= hook->flags;
679 pthread_rwlock_unlock(&rwlock);
683 struct afb_hook_ditf *afb_hook_create_ditf(const char *api, int flags, struct afb_hook_ditf_itf *itf, void *closure)
685 struct afb_hook_ditf *hook;
687 /* alloc the result */
688 hook = calloc(1, sizeof *hook);
692 /* get a copy of the names */
693 hook->api = api ? strdup(api) : NULL;
694 if (api && !hook->api) {
699 /* initialise the rest */
702 hook->itf = itf ? itf : &hook_ditf_default_itf;
703 hook->closure = closure;
705 /* record the hook */
706 pthread_rwlock_wrlock(&rwlock);
707 hook->next = list_of_ditf_hooks;
708 list_of_ditf_hooks = hook;
709 pthread_rwlock_unlock(&rwlock);
715 struct afb_hook_ditf *afb_hook_addref_ditf(struct afb_hook_ditf *hook)
717 pthread_rwlock_wrlock(&rwlock);
719 pthread_rwlock_unlock(&rwlock);
723 void afb_hook_unref_ditf(struct afb_hook_ditf *hook)
725 struct afb_hook_ditf **prv;
728 pthread_rwlock_wrlock(&rwlock);
729 if (--hook->refcount)
733 prv = &list_of_ditf_hooks;
734 while (*prv && *prv != hook)
739 pthread_rwlock_unlock(&rwlock);