afb-hook: change output format
[src/app-framework-binder.git] / src / afb-hook.c
1 /*
2  * Copyright (C) 2016, 2017 "IoT.bzh"
3  * Author José Bollo <jose.bollo@iot.bzh>
4  *
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
8  *
9  *   http://www.apache.org/licenses/LICENSE-2.0
10  *
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.
16  */
17
18 #define _GNU_SOURCE
19
20 #include <limits.h>
21 #include <stdio.h>
22 #include <stdarg.h>
23 #include <string.h>
24 #include <pthread.h>
25 #include <unistd.h>
26 #include <fnmatch.h>
27 #include <sys/uio.h>
28
29 #include <json-c/json.h>
30
31 #include <afb/afb-req-common.h>
32 #include <afb/afb-event-itf.h>
33
34 #include "afb-context.h"
35 #include "afb-hook.h"
36 #include "afb-session.h"
37 #include "afb-cred.h"
38 #include "afb-xreq.h"
39 #include "afb-ditf.h"
40 #include "afb-svc.h"
41 #include "afb-evt.h"
42 #include "verbose.h"
43
44 /**
45  * Definition of a hook for xreq
46  */
47 struct afb_hook_xreq {
48         struct afb_hook_xreq *next; /**< next hook */
49         unsigned refcount; /**< reference count */
50         char *api; /**< api hooked or NULL for any */
51         char *verb; /**< verb hooked or NULL for any */
52         struct afb_session *session; /**< session hooked or NULL if any */
53         unsigned flags; /**< hook flags */
54         struct afb_hook_xreq_itf *itf; /**< interface of hook */
55         void *closure; /**< closure for callbacks */
56 };
57
58 /**
59  * Definition of a hook for ditf
60  */
61 struct afb_hook_ditf {
62         struct afb_hook_ditf *next; /**< next hook */
63         unsigned refcount; /**< reference count */
64         char *api; /**< api hooked or NULL for any */
65         unsigned flags; /**< hook flags */
66         struct afb_hook_ditf_itf *itf; /**< interface of hook */
67         void *closure; /**< closure for callbacks */
68 };
69
70 /**
71  * Definition of a hook for svc
72  */
73 struct afb_hook_svc {
74         struct afb_hook_svc *next; /**< next hook */
75         unsigned refcount; /**< reference count */
76         char *api; /**< api hooked or NULL for any */
77         unsigned flags; /**< hook flags */
78         struct afb_hook_svc_itf *itf; /**< interface of hook */
79         void *closure; /**< closure for callbacks */
80 };
81
82 /**
83  * Definition of a hook for evt
84  */
85 struct afb_hook_evt {
86         struct afb_hook_evt *next; /**< next hook */
87         unsigned refcount; /**< reference count */
88         char *pattern; /**< event pattern name hooked or NULL for any */
89         unsigned flags; /**< hook flags */
90         struct afb_hook_evt_itf *itf; /**< interface of hook */
91         void *closure; /**< closure for callbacks */
92 };
93
94 /**
95  * Definition of a hook for global
96  */
97 struct afb_hook_global {
98         struct afb_hook_global *next; /**< next hook */
99         unsigned refcount; /**< reference count */
100         unsigned flags; /**< hook flags */
101         struct afb_hook_global_itf *itf; /**< interface of hook */
102         void *closure; /**< closure for callbacks */
103 };
104
105 /* synchronisation across threads */
106 static pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
107
108 /* list of hooks for xreq */
109 static struct afb_hook_xreq *list_of_xreq_hooks = NULL;
110
111 /* list of hooks for ditf */
112 static struct afb_hook_ditf *list_of_ditf_hooks = NULL;
113
114 /* list of hooks for svc */
115 static struct afb_hook_svc *list_of_svc_hooks = NULL;
116
117 /* list of hooks for evt */
118 static struct afb_hook_evt *list_of_evt_hooks = NULL;
119
120 /* list of hooks for global */
121 static struct afb_hook_global *list_of_global_hooks = NULL;
122
123 /* hook id */
124 static unsigned next_hookid = 0;
125
126 /******************************************************************************
127  * section: hook id
128  *****************************************************************************/
129 static void init_hookid(struct afb_hookid *hookid)
130 {
131         hookid->id = __atomic_add_fetch(&next_hookid, 1, __ATOMIC_RELAXED);
132         clock_gettime(CLOCK_MONOTONIC, &hookid->time);
133 }
134
135 /******************************************************************************
136  * section: default callbacks for tracing requests
137  *****************************************************************************/
138
139 static char *_pbuf_(const char *fmt, va_list args, char **palloc, char *sbuf, size_t szsbuf, size_t *outlen)
140 {
141         int rc;
142         va_list cp;
143
144         *palloc = NULL;
145         va_copy(cp, args);
146         rc = vsnprintf(sbuf, szsbuf, fmt, args);
147         if ((size_t)rc >= szsbuf) {
148                 sbuf[szsbuf-1] = 0;
149                 sbuf[szsbuf-2] = sbuf[szsbuf-3] = sbuf[szsbuf-4] = '.';
150                 rc = vasprintf(palloc, fmt, cp);
151                 if (rc >= 0)
152                         sbuf = *palloc;
153         }
154         va_end(cp);
155         if (rc >= 0 && outlen)
156                 *outlen = (size_t)rc;
157         return sbuf;
158 }
159
160 #if 0 /* old behaviour: use NOTICE */
161 static void _hook_(const char *fmt1, const char *fmt2, va_list arg2, ...)
162 {
163         char *tag, *data, *mem1, *mem2, buf1[256], buf2[2000];
164         va_list arg1;
165
166         data = _pbuf_(fmt2, arg2, &mem2, buf2, sizeof buf2, NULL);
167
168         va_start(arg1, arg2);
169         tag = _pbuf_(fmt1, arg1, &mem1, buf1, sizeof buf1, NULL);
170         va_end(arg1);
171
172         NOTICE("[HOOK %s] %s", tag, data);
173
174         free(mem1);
175         free(mem2);
176 }
177 #else /* new behaviour: emits directly to stderr */
178 static void _hook_(const char *fmt1, const char *fmt2, va_list arg2, ...)
179 {
180         static const char chars[] = "HOOK: [] \n";
181         char *mem1, *mem2, buf1[256], buf2[2000];
182         struct iovec iov[5];
183         va_list arg1;
184
185         iov[0].iov_base = (void*)&chars[0];
186         iov[0].iov_len = 7;
187
188         va_start(arg1, arg2);
189         iov[1].iov_base = _pbuf_(fmt1, arg1, &mem1, buf1, sizeof buf1, &iov[1].iov_len);
190         va_end(arg1);
191
192         iov[2].iov_base = (void*)&chars[7];
193         iov[2].iov_len = 2;
194
195         iov[3].iov_base = _pbuf_(fmt2, arg2, &mem2, buf2, sizeof buf2, &iov[3].iov_len);
196
197         iov[4].iov_base = (void*)&chars[9];
198         iov[4].iov_len = 1;
199
200         writev(2, iov, 5);
201
202         free(mem1);
203         free(mem2);
204 }
205 #endif
206
207 static void _hook_xreq_(const struct afb_xreq *xreq, const char *format, ...)
208 {
209         va_list ap;
210         va_start(ap, format);
211         _hook_("xreq-%06d:%s/%s", format, ap, xreq->hookindex, xreq->api, xreq->verb);
212         va_end(ap);
213 }
214
215 static void hook_xreq_begin_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq)
216 {
217         if (!xreq->cred)
218                 _hook_xreq_(xreq, "BEGIN");
219         else
220                 _hook_xreq_(xreq, "BEGIN uid=%d=%s gid=%d pid=%d label=%s id=%s",
221                         (int)xreq->cred->uid,
222                         xreq->cred->user,
223                         (int)xreq->cred->gid,
224                         (int)xreq->cred->pid,
225                         xreq->cred->label?:"(null)",
226                         xreq->cred->id?:"(null)"
227                 );
228 }
229
230 static void hook_xreq_end_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq)
231 {
232         _hook_xreq_(xreq, "END");
233 }
234
235 static void hook_xreq_json_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq, struct json_object *obj)
236 {
237         _hook_xreq_(xreq, "json() -> %s", json_object_to_json_string(obj));
238 }
239
240 static void hook_xreq_get_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq, const char *name, struct afb_arg arg)
241 {
242         _hook_xreq_(xreq, "get(%s) -> { name: %s, value: %s, path: %s }", name, arg.name, arg.value, arg.path);
243 }
244
245 static void hook_xreq_success_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq, struct json_object *obj, const char *info)
246 {
247         _hook_xreq_(xreq, "success(%s, %s)", json_object_to_json_string(obj), info);
248 }
249
250 static void hook_xreq_fail_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq, const char *status, const char *info)
251 {
252         _hook_xreq_(xreq, "fail(%s, %s)", status, info);
253 }
254
255 static void hook_xreq_context_get_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq, void *value)
256 {
257         _hook_xreq_(xreq, "context_get() -> %p", value);
258 }
259
260 static void hook_xreq_context_set_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq, void *value, void (*free_value)(void*))
261 {
262         _hook_xreq_(xreq, "context_set(%p, %p)", value, free_value);
263 }
264
265 static void hook_xreq_addref_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq)
266 {
267         _hook_xreq_(xreq, "addref()");
268 }
269
270 static void hook_xreq_unref_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq)
271 {
272         _hook_xreq_(xreq, "unref()");
273 }
274
275 static void hook_xreq_session_close_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq)
276 {
277         _hook_xreq_(xreq, "session_close()");
278 }
279
280 static void hook_xreq_session_set_LOA_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq, unsigned level, int result)
281 {
282         _hook_xreq_(xreq, "session_set_LOA(%u) -> %d", level, result);
283 }
284
285 static void hook_xreq_subscribe_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq, struct afb_event event, int result)
286 {
287         _hook_xreq_(xreq, "subscribe(%s:%d) -> %d", afb_evt_event_name(event), afb_evt_event_id(event), result);
288 }
289
290 static void hook_xreq_unsubscribe_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq, struct afb_event event, int result)
291 {
292         _hook_xreq_(xreq, "unsubscribe(%s:%d) -> %d", afb_evt_event_name(event), afb_evt_event_id(event), result);
293 }
294
295 static void hook_xreq_subcall_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq, const char *api, const char *verb, struct json_object *args)
296 {
297         _hook_xreq_(xreq, "subcall(%s/%s, %s) ...", api, verb, json_object_to_json_string(args));
298 }
299
300 static void hook_xreq_subcall_result_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq, int status, struct json_object *result)
301 {
302         _hook_xreq_(xreq, "    ...subcall... -> %d: %s", status, json_object_to_json_string(result));
303 }
304
305 static void hook_xreq_subcallsync_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq, const char *api, const char *verb, struct json_object *args)
306 {
307         _hook_xreq_(xreq, "subcallsync(%s/%s, %s) ...", api, verb, json_object_to_json_string(args));
308 }
309
310 static void hook_xreq_subcallsync_result_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq, int status, struct json_object *result)
311 {
312         _hook_xreq_(xreq, "    ...subcallsync... -> %d: %s", status, json_object_to_json_string(result));
313 }
314
315 static void hook_xreq_vverbose_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq, int level, const char *file, int line, const char *func, const char *fmt, va_list args)
316 {
317         int len;
318         char *msg;
319         va_list ap;
320
321         va_copy(ap, args);
322         len = vasprintf(&msg, fmt, ap);
323         va_end(ap);
324
325         if (len < 0)
326                 _hook_xreq_(xreq, "vverbose(%d, %s, %d, %s) -> %s ? ? ?", level, file, line, func, fmt);
327         else {
328                 _hook_xreq_(xreq, "vverbose(%d, %s, %d, %s) -> %s", level, file, line, func, msg);
329                 free(msg);
330         }
331 }
332
333 static void hook_xreq_store_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq, struct afb_stored_req *sreq)
334 {
335         _hook_xreq_(xreq, "store() -> %p", sreq);
336 }
337
338 static void hook_xreq_unstore_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq)
339 {
340         _hook_xreq_(xreq, "unstore()");
341 }
342
343 static void hook_xreq_subcall_req_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq, const char *api, const char *verb, struct json_object *args)
344 {
345         _hook_xreq_(xreq, "subcall_req(%s/%s, %s) ...", api, verb, json_object_to_json_string(args));
346 }
347
348 static void hook_xreq_subcall_req_result_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq, int status, struct json_object *result)
349 {
350         _hook_xreq_(xreq, "    ...subcall_req... -> %d: %s", status, json_object_to_json_string(result));
351 }
352
353 static void hook_xreq_has_permission_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq, const char *permission, int result)
354 {
355         _hook_xreq_(xreq, "has_permission(%s) -> %d", permission, result);
356 }
357
358 static struct afb_hook_xreq_itf hook_xreq_default_itf = {
359         .hook_xreq_begin = hook_xreq_begin_default_cb,
360         .hook_xreq_end = hook_xreq_end_default_cb,
361         .hook_xreq_json = hook_xreq_json_default_cb,
362         .hook_xreq_get = hook_xreq_get_default_cb,
363         .hook_xreq_success = hook_xreq_success_default_cb,
364         .hook_xreq_fail = hook_xreq_fail_default_cb,
365         .hook_xreq_context_get = hook_xreq_context_get_default_cb,
366         .hook_xreq_context_set = hook_xreq_context_set_default_cb,
367         .hook_xreq_addref = hook_xreq_addref_default_cb,
368         .hook_xreq_unref = hook_xreq_unref_default_cb,
369         .hook_xreq_session_close = hook_xreq_session_close_default_cb,
370         .hook_xreq_session_set_LOA = hook_xreq_session_set_LOA_default_cb,
371         .hook_xreq_subscribe = hook_xreq_subscribe_default_cb,
372         .hook_xreq_unsubscribe = hook_xreq_unsubscribe_default_cb,
373         .hook_xreq_subcall = hook_xreq_subcall_default_cb,
374         .hook_xreq_subcall_result = hook_xreq_subcall_result_default_cb,
375         .hook_xreq_subcallsync = hook_xreq_subcallsync_default_cb,
376         .hook_xreq_subcallsync_result = hook_xreq_subcallsync_result_default_cb,
377         .hook_xreq_vverbose = hook_xreq_vverbose_default_cb,
378         .hook_xreq_store = hook_xreq_store_default_cb,
379         .hook_xreq_unstore = hook_xreq_unstore_default_cb,
380         .hook_xreq_subcall_req = hook_xreq_subcall_req_default_cb,
381         .hook_xreq_subcall_req_result = hook_xreq_subcall_req_result_default_cb,
382         .hook_xreq_has_permission = hook_xreq_has_permission_default_cb
383 };
384
385 /******************************************************************************
386  * section: hooks for tracing requests
387  *****************************************************************************/
388
389 #define _HOOK_XREQ_(what,...)   \
390         struct afb_hook_xreq *hook; \
391         struct afb_hookid hookid; \
392         pthread_rwlock_rdlock(&rwlock); \
393         init_hookid(&hookid); \
394         hook = list_of_xreq_hooks; \
395         while (hook) { \
396                 if (hook->itf->hook_xreq_##what \
397                  && (hook->flags & afb_hook_flag_req_##what) != 0 \
398                  && (!hook->session || hook->session == xreq->context.session) \
399                  && (!hook->api || !strcasecmp(hook->api, xreq->api)) \
400                  && (!hook->verb || !strcasecmp(hook->verb, xreq->verb))) { \
401                         hook->itf->hook_xreq_##what(hook->closure, &hookid, __VA_ARGS__); \
402                 } \
403                 hook = hook->next; \
404         } \
405         pthread_rwlock_unlock(&rwlock);
406
407
408 void afb_hook_xreq_begin(const struct afb_xreq *xreq)
409 {
410         _HOOK_XREQ_(begin, xreq);
411 }
412
413 void afb_hook_xreq_end(const struct afb_xreq *xreq)
414 {
415         _HOOK_XREQ_(end, xreq);
416 }
417
418 struct json_object *afb_hook_xreq_json(const struct afb_xreq *xreq, struct json_object *obj)
419 {
420         _HOOK_XREQ_(json, xreq, obj);
421         return obj;
422 }
423
424 struct afb_arg afb_hook_xreq_get(const struct afb_xreq *xreq, const char *name, struct afb_arg arg)
425 {
426         _HOOK_XREQ_(get, xreq, name, arg);
427         return arg;
428 }
429
430 void afb_hook_xreq_success(const struct afb_xreq *xreq, struct json_object *obj, const char *info)
431 {
432         _HOOK_XREQ_(success, xreq, obj, info);
433 }
434
435 void afb_hook_xreq_fail(const struct afb_xreq *xreq, const char *status, const char *info)
436 {
437         _HOOK_XREQ_(fail, xreq, status, info);
438 }
439
440 void *afb_hook_xreq_context_get(const struct afb_xreq *xreq, void *value)
441 {
442         _HOOK_XREQ_(context_get, xreq, value);
443         return value;
444 }
445
446 void afb_hook_xreq_context_set(const struct afb_xreq *xreq, void *value, void (*free_value)(void*))
447 {
448         _HOOK_XREQ_(context_set, xreq, value, free_value);
449 }
450
451 void afb_hook_xreq_addref(const struct afb_xreq *xreq)
452 {
453         _HOOK_XREQ_(addref, xreq);
454 }
455
456 void afb_hook_xreq_unref(const struct afb_xreq *xreq)
457 {
458         _HOOK_XREQ_(unref, xreq);
459 }
460
461 void afb_hook_xreq_session_close(const struct afb_xreq *xreq)
462 {
463         _HOOK_XREQ_(session_close, xreq);
464 }
465
466 int afb_hook_xreq_session_set_LOA(const struct afb_xreq *xreq, unsigned level, int result)
467 {
468         _HOOK_XREQ_(session_set_LOA, xreq, level, result);
469         return result;
470 }
471
472 int afb_hook_xreq_subscribe(const struct afb_xreq *xreq, struct afb_event event, int result)
473 {
474         _HOOK_XREQ_(subscribe, xreq, event, result);
475         return result;
476 }
477
478 int afb_hook_xreq_unsubscribe(const struct afb_xreq *xreq, struct afb_event event, int result)
479 {
480         _HOOK_XREQ_(unsubscribe, xreq, event, result);
481         return result;
482 }
483
484 void afb_hook_xreq_subcall(const struct afb_xreq *xreq, const char *api, const char *verb, struct json_object *args)
485 {
486         _HOOK_XREQ_(subcall, xreq, api, verb, args);
487 }
488
489 void afb_hook_xreq_subcall_result(const struct afb_xreq *xreq, int status, struct json_object *result)
490 {
491         _HOOK_XREQ_(subcall_result, xreq, status, result);
492 }
493
494 void afb_hook_xreq_subcallsync(const struct afb_xreq *xreq, const char *api, const char *verb, struct json_object *args)
495 {
496         _HOOK_XREQ_(subcallsync, xreq, api, verb, args);
497 }
498
499 int afb_hook_xreq_subcallsync_result(const struct afb_xreq *xreq, int status, struct json_object *result)
500 {
501         _HOOK_XREQ_(subcallsync_result, xreq, status, result);
502         return status;
503 }
504
505 void afb_hook_xreq_vverbose(const struct afb_xreq *xreq, int level, const char *file, int line, const char *func, const char *fmt, va_list args)
506 {
507         _HOOK_XREQ_(vverbose, xreq, level, file ?: "?", line, func ?: "?", fmt, args);
508 }
509
510 void afb_hook_xreq_store(const struct afb_xreq *xreq, struct afb_stored_req *sreq)
511 {
512         _HOOK_XREQ_(store, xreq, sreq);
513 }
514
515 void afb_hook_xreq_unstore(const struct afb_xreq *xreq)
516 {
517         _HOOK_XREQ_(unstore, xreq);
518 }
519
520 void afb_hook_xreq_subcall_req(const struct afb_xreq *xreq, const char *api, const char *verb, struct json_object *args)
521 {
522         _HOOK_XREQ_(subcall_req, xreq, api, verb, args);
523 }
524
525 void afb_hook_xreq_subcall_req_result(const struct afb_xreq *xreq, int status, struct json_object *result)
526 {
527         _HOOK_XREQ_(subcall_req_result, xreq, status, result);
528 }
529
530 int afb_hook_xreq_has_permission(const struct afb_xreq *xreq, const char *permission, int result)
531 {
532         _HOOK_XREQ_(has_permission, xreq, permission, result);
533         return result;
534 }
535
536 /******************************************************************************
537  * section: hooking xreqs
538  *****************************************************************************/
539
540 void afb_hook_init_xreq(struct afb_xreq *xreq)
541 {
542         static int reqindex;
543
544         int f, flags;
545         int add;
546         struct afb_hook_xreq *hook;
547
548         /* scan hook list to get the expected flags */
549         flags = 0;
550         pthread_rwlock_rdlock(&rwlock);
551         hook = list_of_xreq_hooks;
552         while (hook) {
553                 f = hook->flags & afb_hook_flags_req_all;
554                 add = f != 0
555                    && (!hook->session || hook->session == xreq->context.session)
556                    && (!hook->api || !strcasecmp(hook->api, xreq->api))
557                    && (!hook->verb || !strcasecmp(hook->verb, xreq->verb));
558                 if (add)
559                         flags |= f;
560                 hook = hook->next;
561         }
562         pthread_rwlock_unlock(&rwlock);
563
564         /* store the hooking data */
565         xreq->hookflags = flags;
566         if (flags) {
567                 pthread_rwlock_wrlock(&rwlock);
568                 if (++reqindex < 0)
569                         reqindex = 1;
570                 xreq->hookindex = reqindex;
571                 pthread_rwlock_unlock(&rwlock);
572         }
573 }
574
575 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)
576 {
577         struct afb_hook_xreq *hook;
578
579         /* alloc the result */
580         hook = calloc(1, sizeof *hook);
581         if (hook == NULL)
582                 return NULL;
583
584         /* get a copy of the names */
585         hook->api = api ? strdup(api) : NULL;
586         hook->verb = verb ? strdup(verb) : NULL;
587         if ((api && !hook->api) || (verb && !hook->verb)) {
588                 free(hook->api);
589                 free(hook->verb);
590                 free(hook);
591                 return NULL;
592         }
593
594         /* initialise the rest */
595         hook->session = session;
596         if (session)
597                 afb_session_addref(session);
598         hook->refcount = 1;
599         hook->flags = flags;
600         hook->itf = itf ? itf : &hook_xreq_default_itf;
601         hook->closure = closure;
602
603         /* record the hook */
604         pthread_rwlock_wrlock(&rwlock);
605         hook->next = list_of_xreq_hooks;
606         list_of_xreq_hooks = hook;
607         pthread_rwlock_unlock(&rwlock);
608
609         /* returns it */
610         return hook;
611 }
612
613 struct afb_hook_xreq *afb_hook_addref_xreq(struct afb_hook_xreq *hook)
614 {
615         pthread_rwlock_wrlock(&rwlock);
616         hook->refcount++;
617         pthread_rwlock_unlock(&rwlock);
618         return hook;
619 }
620
621 void afb_hook_unref_xreq(struct afb_hook_xreq *hook)
622 {
623         struct afb_hook_xreq **prv;
624
625         if (hook) {
626                 pthread_rwlock_wrlock(&rwlock);
627                 if (--hook->refcount)
628                         hook = NULL;
629                 else {
630                         /* unlink */
631                         prv = &list_of_xreq_hooks;
632                         while (*prv && *prv != hook)
633                                 prv = &(*prv)->next;
634                         if(*prv)
635                                 *prv = hook->next;
636                 }
637                 pthread_rwlock_unlock(&rwlock);
638                 if (hook) {
639                         /* free */
640                         free(hook->api);
641                         free(hook->verb);
642                         if (hook->session)
643                                 afb_session_unref(hook->session);
644                         free(hook);
645                 }
646         }
647 }
648
649 /******************************************************************************
650  * section: default callbacks for tracing daemon interface
651  *****************************************************************************/
652
653 static void _hook_ditf_(const struct afb_ditf *ditf, const char *format, ...)
654 {
655         va_list ap;
656         va_start(ap, format);
657         _hook_("ditf-%s", format, ap, ditf->api);
658         va_end(ap);
659 }
660
661 static void hook_ditf_event_broadcast_before_cb(void *closure, const struct afb_hookid *hookid, const struct afb_ditf *ditf, const char *name, struct json_object *object)
662 {
663         _hook_ditf_(ditf, "event_broadcast.before(%s, %s)....", name, json_object_to_json_string(object));
664 }
665
666 static void hook_ditf_event_broadcast_after_cb(void *closure, const struct afb_hookid *hookid, const struct afb_ditf *ditf, const char *name, struct json_object *object, int result)
667 {
668         _hook_ditf_(ditf, "event_broadcast.after(%s, %s) -> %d", name, json_object_to_json_string(object), result);
669 }
670
671 static void hook_ditf_get_event_loop_cb(void *closure, const struct afb_hookid *hookid, const struct afb_ditf *ditf, struct sd_event *result)
672 {
673         _hook_ditf_(ditf, "get_event_loop() -> %p", result);
674 }
675
676 static void hook_ditf_get_user_bus_cb(void *closure, const struct afb_hookid *hookid, const struct afb_ditf *ditf, struct sd_bus *result)
677 {
678         _hook_ditf_(ditf, "get_user_bus() -> %p", result);
679 }
680
681 static void hook_ditf_get_system_bus_cb(void *closure, const struct afb_hookid *hookid, const struct afb_ditf *ditf, struct sd_bus *result)
682 {
683         _hook_ditf_(ditf, "get_system_bus() -> %p", result);
684 }
685
686 static void hook_ditf_vverbose_cb(void *closure, const struct afb_hookid *hookid, const struct afb_ditf *ditf, int level, const char *file, int line, const char *function, const char *fmt, va_list args)
687 {
688         int len;
689         char *msg;
690         va_list ap;
691
692         va_copy(ap, args);
693         len = vasprintf(&msg, fmt, ap);
694         va_end(ap);
695
696         if (len < 0)
697                 _hook_ditf_(ditf, "vverbose(%d, %s, %d, %s) -> %s ? ? ?", level, file, line, function, fmt);
698         else {
699                 _hook_ditf_(ditf, "vverbose(%d, %s, %d, %s) -> %s", level, file, line, function, msg);
700                 free(msg);
701         }
702 }
703
704 static void hook_ditf_event_make_cb(void *closure, const struct afb_hookid *hookid, const struct afb_ditf *ditf, const char *name, struct afb_event result)
705 {
706         _hook_ditf_(ditf, "event_make(%s) -> %s:%d", name, afb_evt_event_name(result), afb_evt_event_id(result));
707 }
708
709 static void hook_ditf_rootdir_get_fd_cb(void *closure, const struct afb_hookid *hookid, const struct afb_ditf *ditf, int result)
710 {
711         char path[PATH_MAX];
712         if (result < 0)
713                 _hook_ditf_(ditf, "rootdir_get_fd() -> %d, %m", result);
714         else {
715                 sprintf(path, "/proc/self/fd/%d", result);
716                 readlink(path, path, sizeof path);
717                 _hook_ditf_(ditf, "rootdir_get_fd() -> %d = %s", result, path);
718         }
719 }
720
721 static void hook_ditf_rootdir_open_locale_cb(void *closure, const struct afb_hookid *hookid, const struct afb_ditf *ditf, const char *filename, int flags, const char *locale, int result)
722 {
723         char path[PATH_MAX];
724         if (!locale)
725                 locale = "(null)";
726         if (result < 0)
727                 _hook_ditf_(ditf, "rootdir_open_locale(%s, %d, %s) -> %d, %m", filename, flags, locale, result);
728         else {
729                 sprintf(path, "/proc/self/fd/%d", result);
730                 readlink(path, path, sizeof path);
731                 _hook_ditf_(ditf, "rootdir_open_locale(%s, %d, %s) -> %d = %s", filename, flags, locale, result, path);
732         }
733 }
734
735 static void hook_ditf_queue_job_cb(void *closure, const struct afb_hookid *hookid, const struct afb_ditf *ditf, void (*callback)(int signum, void *arg), void *argument, void *group, int timeout, int result)
736 {
737         _hook_ditf_(ditf, "queue_job(%p, %p, %p, %d) -> %d", callback, argument, group, timeout, result);
738 }
739
740 static void hook_ditf_unstore_req_cb(void *closure, const struct afb_hookid *hookid,  const struct afb_ditf *ditf, struct afb_stored_req *sreq)
741 {
742         _hook_ditf_(ditf, "unstore_req(%p)", sreq);
743 }
744
745 static void hook_ditf_require_api_cb(void *closure, const struct afb_hookid *hookid, const struct afb_ditf *ditf, const char *name, int initialized)
746 {
747         _hook_ditf_(ditf, "require_api(%s, %d)...", name, initialized);
748 }
749
750 static void hook_ditf_require_api_result_cb(void *closure, const struct afb_hookid *hookid, const struct afb_ditf *ditf, const char *name, int initialized, int result)
751 {
752         _hook_ditf_(ditf, "...require_api(%s, %d) -> %d", name, initialized, result);
753 }
754
755 static struct afb_hook_ditf_itf hook_ditf_default_itf = {
756         .hook_ditf_event_broadcast_before = hook_ditf_event_broadcast_before_cb,
757         .hook_ditf_event_broadcast_after = hook_ditf_event_broadcast_after_cb,
758         .hook_ditf_get_event_loop = hook_ditf_get_event_loop_cb,
759         .hook_ditf_get_user_bus = hook_ditf_get_user_bus_cb,
760         .hook_ditf_get_system_bus = hook_ditf_get_system_bus_cb,
761         .hook_ditf_vverbose = hook_ditf_vverbose_cb,
762         .hook_ditf_event_make = hook_ditf_event_make_cb,
763         .hook_ditf_rootdir_get_fd = hook_ditf_rootdir_get_fd_cb,
764         .hook_ditf_rootdir_open_locale = hook_ditf_rootdir_open_locale_cb,
765         .hook_ditf_queue_job = hook_ditf_queue_job_cb,
766         .hook_ditf_unstore_req = hook_ditf_unstore_req_cb,
767         .hook_ditf_require_api = hook_ditf_require_api_cb,
768         .hook_ditf_require_api_result = hook_ditf_require_api_result_cb
769 };
770
771 /******************************************************************************
772  * section: hooks for tracing daemon interface (ditf)
773  *****************************************************************************/
774
775 #define _HOOK_DITF_(what,...)   \
776         struct afb_hook_ditf *hook; \
777         struct afb_hookid hookid; \
778         pthread_rwlock_rdlock(&rwlock); \
779         init_hookid(&hookid); \
780         hook = list_of_ditf_hooks; \
781         while (hook) { \
782                 if (hook->itf->hook_ditf_##what \
783                  && (hook->flags & afb_hook_flag_ditf_##what) != 0 \
784                  && (!hook->api || !strcasecmp(hook->api, ditf->api))) { \
785                         hook->itf->hook_ditf_##what(hook->closure, &hookid, __VA_ARGS__); \
786                 } \
787                 hook = hook->next; \
788         } \
789         pthread_rwlock_unlock(&rwlock);
790
791 void afb_hook_ditf_event_broadcast_before(const struct afb_ditf *ditf, const char *name, struct json_object *object)
792 {
793         _HOOK_DITF_(event_broadcast_before, ditf, name, object);
794 }
795
796 int afb_hook_ditf_event_broadcast_after(const struct afb_ditf *ditf, const char *name, struct json_object *object, int result)
797 {
798         _HOOK_DITF_(event_broadcast_after, ditf, name, object, result);
799         return result;
800 }
801
802 struct sd_event *afb_hook_ditf_get_event_loop(const struct afb_ditf *ditf, struct sd_event *result)
803 {
804         _HOOK_DITF_(get_event_loop, ditf, result);
805         return result;
806 }
807
808 struct sd_bus *afb_hook_ditf_get_user_bus(const struct afb_ditf *ditf, struct sd_bus *result)
809 {
810         _HOOK_DITF_(get_user_bus, ditf, result);
811         return result;
812 }
813
814 struct sd_bus *afb_hook_ditf_get_system_bus(const struct afb_ditf *ditf, struct sd_bus *result)
815 {
816         _HOOK_DITF_(get_system_bus, ditf, result);
817         return result;
818 }
819
820 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)
821 {
822         _HOOK_DITF_(vverbose, ditf, level, file, line, function, fmt, args);
823 }
824
825 struct afb_event afb_hook_ditf_event_make(const struct afb_ditf *ditf, const char *name, struct afb_event result)
826 {
827         _HOOK_DITF_(event_make, ditf, name, result);
828         return result;
829 }
830
831 int afb_hook_ditf_rootdir_get_fd(const struct afb_ditf *ditf, int result)
832 {
833         _HOOK_DITF_(rootdir_get_fd, ditf, result);
834         return result;
835 }
836
837 int afb_hook_ditf_rootdir_open_locale(const struct afb_ditf *ditf, const char *filename, int flags, const char *locale, int result)
838 {
839         _HOOK_DITF_(rootdir_open_locale, ditf, filename, flags, locale, result);
840         return result;
841 }
842
843 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)
844 {
845         _HOOK_DITF_(queue_job, ditf, callback, argument, group, timeout, result);
846         return result;
847 }
848
849 void afb_hook_ditf_unstore_req(const struct afb_ditf *ditf, struct afb_stored_req *sreq)
850 {
851         _HOOK_DITF_(unstore_req, ditf, sreq);
852 }
853
854 void afb_hook_ditf_require_api(const struct afb_ditf *ditf, const char *name, int initialized)
855 {
856         _HOOK_DITF_(require_api, ditf, name, initialized);
857 }
858
859 int afb_hook_ditf_require_api_result(const struct afb_ditf *ditf, const char *name, int initialized, int result)
860 {
861         _HOOK_DITF_(require_api_result, ditf, name, initialized, result);
862         return result;
863 }
864
865 /******************************************************************************
866  * section: hooking ditf
867  *****************************************************************************/
868
869 int afb_hook_flags_ditf(const char *api)
870 {
871         int flags;
872         struct afb_hook_ditf *hook;
873
874         pthread_rwlock_rdlock(&rwlock);
875         flags = 0;
876         hook = list_of_ditf_hooks;
877         while (hook) {
878                 if (!api || !hook->api || !strcasecmp(hook->api, api))
879                         flags |= hook->flags;
880                 hook = hook->next;
881         }
882         pthread_rwlock_unlock(&rwlock);
883         return flags;
884 }
885
886 struct afb_hook_ditf *afb_hook_create_ditf(const char *api, int flags, struct afb_hook_ditf_itf *itf, void *closure)
887 {
888         struct afb_hook_ditf *hook;
889
890         /* alloc the result */
891         hook = calloc(1, sizeof *hook);
892         if (hook == NULL)
893                 return NULL;
894
895         /* get a copy of the names */
896         hook->api = api ? strdup(api) : NULL;
897         if (api && !hook->api) {
898                 free(hook);
899                 return NULL;
900         }
901
902         /* initialise the rest */
903         hook->refcount = 1;
904         hook->flags = flags;
905         hook->itf = itf ? itf : &hook_ditf_default_itf;
906         hook->closure = closure;
907
908         /* record the hook */
909         pthread_rwlock_wrlock(&rwlock);
910         hook->next = list_of_ditf_hooks;
911         list_of_ditf_hooks = hook;
912         pthread_rwlock_unlock(&rwlock);
913
914         /* returns it */
915         return hook;
916 }
917
918 struct afb_hook_ditf *afb_hook_addref_ditf(struct afb_hook_ditf *hook)
919 {
920         pthread_rwlock_wrlock(&rwlock);
921         hook->refcount++;
922         pthread_rwlock_unlock(&rwlock);
923         return hook;
924 }
925
926 void afb_hook_unref_ditf(struct afb_hook_ditf *hook)
927 {
928         struct afb_hook_ditf **prv;
929
930         if (hook) {
931                 pthread_rwlock_wrlock(&rwlock);
932                 if (--hook->refcount)
933                         hook = NULL;
934                 else {
935                         /* unlink */
936                         prv = &list_of_ditf_hooks;
937                         while (*prv && *prv != hook)
938                                 prv = &(*prv)->next;
939                         if(*prv)
940                                 *prv = hook->next;
941                 }
942                 pthread_rwlock_unlock(&rwlock);
943                 if (hook) {
944                         /* free */
945                         free(hook->api);
946                         free(hook);
947                 }
948         }
949 }
950
951 /******************************************************************************
952  * section: default callbacks for tracing service interface (svc)
953  *****************************************************************************/
954
955 static void _hook_svc_(const struct afb_svc *svc, const char *format, ...)
956 {
957         va_list ap;
958         va_start(ap, format);
959         _hook_("svc-%s", format, ap, svc->api);
960         va_end(ap);
961 }
962
963 static void hook_svc_start_before_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_svc *svc)
964 {
965         _hook_svc_(svc, "start.before");
966 }
967
968 static void hook_svc_start_after_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_svc *svc, int status)
969 {
970         _hook_svc_(svc, "start.after -> %d", status);
971 }
972
973 static void hook_svc_on_event_before_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_svc *svc, const char *event, int eventid, struct json_object *object)
974 {
975         _hook_svc_(svc, "on_event.before(%s, %d, %s)", event, eventid, json_object_to_json_string(object));
976 }
977
978 static void hook_svc_on_event_after_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_svc *svc, const char *event, int eventid, struct json_object *object)
979 {
980         _hook_svc_(svc, "on_event.after(%s, %d, %s)", event, eventid, json_object_to_json_string(object));
981 }
982
983 static void hook_svc_call_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_svc *svc, const char *api, const char *verb, struct json_object *args)
984 {
985         _hook_svc_(svc, "call(%s/%s, %s) ...", api, verb, json_object_to_json_string(args));
986 }
987
988 static void hook_svc_call_result_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_svc *svc, int status, struct json_object *result)
989 {
990         _hook_svc_(svc, "    ...call... -> %d: %s", status, json_object_to_json_string(result));
991 }
992
993 static void hook_svc_callsync_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_svc *svc, const char *api, const char *verb, struct json_object *args)
994 {
995         _hook_svc_(svc, "callsync(%s/%s, %s) ...", api, verb, json_object_to_json_string(args));
996 }
997
998 static void hook_svc_callsync_result_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_svc *svc, int status, struct json_object *result)
999 {
1000         _hook_svc_(svc, "    ...callsync... -> %d: %s", status, json_object_to_json_string(result));
1001 }
1002
1003 static struct afb_hook_svc_itf hook_svc_default_itf = {
1004         .hook_svc_start_before = hook_svc_start_before_default_cb,
1005         .hook_svc_start_after = hook_svc_start_after_default_cb,
1006         .hook_svc_on_event_before = hook_svc_on_event_before_default_cb,
1007         .hook_svc_on_event_after = hook_svc_on_event_after_default_cb,
1008         .hook_svc_call = hook_svc_call_default_cb,
1009         .hook_svc_call_result = hook_svc_call_result_default_cb,
1010         .hook_svc_callsync = hook_svc_callsync_default_cb,
1011         .hook_svc_callsync_result = hook_svc_callsync_result_default_cb
1012 };
1013
1014 /******************************************************************************
1015  * section: hooks for tracing service interface (svc)
1016  *****************************************************************************/
1017
1018 #define _HOOK_SVC_(what,...)   \
1019         struct afb_hook_svc *hook; \
1020         struct afb_hookid hookid; \
1021         pthread_rwlock_rdlock(&rwlock); \
1022         init_hookid(&hookid); \
1023         hook = list_of_svc_hooks; \
1024         while (hook) { \
1025                 if (hook->itf->hook_svc_##what \
1026                  && (hook->flags & afb_hook_flag_svc_##what) != 0 \
1027                  && (!hook->api || !strcasecmp(hook->api, svc->api))) { \
1028                         hook->itf->hook_svc_##what(hook->closure, &hookid, __VA_ARGS__); \
1029                 } \
1030                 hook = hook->next; \
1031         } \
1032         pthread_rwlock_unlock(&rwlock);
1033
1034 void afb_hook_svc_start_before(const struct afb_svc *svc)
1035 {
1036         _HOOK_SVC_(start_before, svc);
1037 }
1038
1039 int afb_hook_svc_start_after(const struct afb_svc *svc, int status)
1040 {
1041         _HOOK_SVC_(start_after, svc, status);
1042         return status;
1043 }
1044
1045 void afb_hook_svc_on_event_before(const struct afb_svc *svc, const char *event, int eventid, struct json_object *object)
1046 {
1047         _HOOK_SVC_(on_event_before, svc, event, eventid, object);
1048 }
1049
1050 void afb_hook_svc_on_event_after(const struct afb_svc *svc, const char *event, int eventid, struct json_object *object)
1051 {
1052         _HOOK_SVC_(on_event_after, svc, event, eventid, object);
1053 }
1054
1055 void afb_hook_svc_call(const struct afb_svc *svc, const char *api, const char *verb, struct json_object *args)
1056 {
1057         _HOOK_SVC_(call, svc, api, verb, args);
1058 }
1059
1060 void afb_hook_svc_call_result(const struct afb_svc *svc, int status, struct json_object *result)
1061 {
1062         _HOOK_SVC_(call_result, svc, status, result);
1063 }
1064
1065 void afb_hook_svc_callsync(const struct afb_svc *svc, const char *api, const char *verb, struct json_object *args)
1066 {
1067         _HOOK_SVC_(callsync, svc, api, verb, args);
1068 }
1069
1070 int afb_hook_svc_callsync_result(const struct afb_svc *svc, int status, struct json_object *result)
1071 {
1072         _HOOK_SVC_(callsync_result, svc, status, result);
1073         return status;
1074 }
1075
1076 /******************************************************************************
1077  * section: hooking services (svc)
1078  *****************************************************************************/
1079
1080 int afb_hook_flags_svc(const char *api)
1081 {
1082         int flags;
1083         struct afb_hook_svc *hook;
1084
1085         pthread_rwlock_rdlock(&rwlock);
1086         flags = 0;
1087         hook = list_of_svc_hooks;
1088         while (hook) {
1089                 if (!api || !hook->api || !strcasecmp(hook->api, api))
1090                         flags |= hook->flags;
1091                 hook = hook->next;
1092         }
1093         pthread_rwlock_unlock(&rwlock);
1094         return flags;
1095 }
1096
1097 struct afb_hook_svc *afb_hook_create_svc(const char *api, int flags, struct afb_hook_svc_itf *itf, void *closure)
1098 {
1099         struct afb_hook_svc *hook;
1100
1101         /* alloc the result */
1102         hook = calloc(1, sizeof *hook);
1103         if (hook == NULL)
1104                 return NULL;
1105
1106         /* get a copy of the names */
1107         hook->api = api ? strdup(api) : NULL;
1108         if (api && !hook->api) {
1109                 free(hook);
1110                 return NULL;
1111         }
1112
1113         /* initialise the rest */
1114         hook->refcount = 1;
1115         hook->flags = flags;
1116         hook->itf = itf ? itf : &hook_svc_default_itf;
1117         hook->closure = closure;
1118
1119         /* record the hook */
1120         pthread_rwlock_wrlock(&rwlock);
1121         hook->next = list_of_svc_hooks;
1122         list_of_svc_hooks = hook;
1123         pthread_rwlock_unlock(&rwlock);
1124
1125         /* returns it */
1126         return hook;
1127 }
1128
1129 struct afb_hook_svc *afb_hook_addref_svc(struct afb_hook_svc *hook)
1130 {
1131         pthread_rwlock_wrlock(&rwlock);
1132         hook->refcount++;
1133         pthread_rwlock_unlock(&rwlock);
1134         return hook;
1135 }
1136
1137 void afb_hook_unref_svc(struct afb_hook_svc *hook)
1138 {
1139         struct afb_hook_svc **prv;
1140
1141         if (hook) {
1142                 pthread_rwlock_wrlock(&rwlock);
1143                 if (--hook->refcount)
1144                         hook = NULL;
1145                 else {
1146                         /* unlink */
1147                         prv = &list_of_svc_hooks;
1148                         while (*prv && *prv != hook)
1149                                 prv = &(*prv)->next;
1150                         if(*prv)
1151                                 *prv = hook->next;
1152                 }
1153                 pthread_rwlock_unlock(&rwlock);
1154                 if (hook) {
1155                         /* free */
1156                         free(hook->api);
1157                         free(hook);
1158                 }
1159         }
1160 }
1161
1162 /******************************************************************************
1163  * section: default callbacks for tracing service interface (evt)
1164  *****************************************************************************/
1165
1166 static void _hook_evt_(const char *evt, int id, const char *format, ...)
1167 {
1168         va_list ap;
1169         va_start(ap, format);
1170         _hook_("evt-%s:%d", format, ap, evt, id);
1171         va_end(ap);
1172 }
1173
1174 static void hook_evt_create_default_cb(void *closure, const struct afb_hookid *hookid, const char *evt, int id)
1175 {
1176         _hook_evt_(evt, id, "create");
1177 }
1178
1179 static void hook_evt_push_before_default_cb(void *closure, const struct afb_hookid *hookid, const char *evt, int id, struct json_object *obj)
1180 {
1181         _hook_evt_(evt, id, "push.before(%s)", json_object_to_json_string(obj));
1182 }
1183
1184
1185 static void hook_evt_push_after_default_cb(void *closure, const struct afb_hookid *hookid, const char *evt, int id, struct json_object *obj, int result)
1186 {
1187         _hook_evt_(evt, id, "push.after(%s) -> %d", json_object_to_json_string(obj), result);
1188 }
1189
1190 static void hook_evt_broadcast_before_default_cb(void *closure, const struct afb_hookid *hookid, const char *evt, int id, struct json_object *obj)
1191 {
1192         _hook_evt_(evt, id, "broadcast.before(%s)", json_object_to_json_string(obj));
1193 }
1194
1195 static void hook_evt_broadcast_after_default_cb(void *closure, const struct afb_hookid *hookid, const char *evt, int id, struct json_object *obj, int result)
1196 {
1197         _hook_evt_(evt, id, "broadcast.after(%s) -> %d", json_object_to_json_string(obj), result);
1198 }
1199
1200 static void hook_evt_name_default_cb(void *closure, const struct afb_hookid *hookid, const char *evt, int id)
1201 {
1202         _hook_evt_(evt, id, "name");
1203 }
1204
1205 static void hook_evt_drop_default_cb(void *closure, const struct afb_hookid *hookid, const char *evt, int id)
1206 {
1207         _hook_evt_(evt, id, "drop");
1208 }
1209
1210 static struct afb_hook_evt_itf hook_evt_default_itf = {
1211         .hook_evt_create = hook_evt_create_default_cb,
1212         .hook_evt_push_before = hook_evt_push_before_default_cb,
1213         .hook_evt_push_after = hook_evt_push_after_default_cb,
1214         .hook_evt_broadcast_before = hook_evt_broadcast_before_default_cb,
1215         .hook_evt_broadcast_after = hook_evt_broadcast_after_default_cb,
1216         .hook_evt_name = hook_evt_name_default_cb,
1217         .hook_evt_drop = hook_evt_drop_default_cb
1218 };
1219
1220 /******************************************************************************
1221  * section: hooks for tracing events interface (evt)
1222  *****************************************************************************/
1223
1224 #define _HOOK_EVT_(what,...)   \
1225         struct afb_hook_evt *hook; \
1226         struct afb_hookid hookid; \
1227         pthread_rwlock_rdlock(&rwlock); \
1228         init_hookid(&hookid); \
1229         hook = list_of_evt_hooks; \
1230         while (hook) { \
1231                 if (hook->itf->hook_evt_##what \
1232                  && (hook->flags & afb_hook_flag_evt_##what) != 0 \
1233                  && (!hook->pattern || !fnmatch(hook->pattern, evt, FNM_CASEFOLD))) { \
1234                         hook->itf->hook_evt_##what(hook->closure, &hookid, __VA_ARGS__); \
1235                 } \
1236                 hook = hook->next; \
1237         } \
1238         pthread_rwlock_unlock(&rwlock);
1239
1240 void afb_hook_evt_create(const char *evt, int id)
1241 {
1242         _HOOK_EVT_(create, evt, id);
1243 }
1244
1245 void afb_hook_evt_push_before(const char *evt, int id, struct json_object *obj)
1246 {
1247         _HOOK_EVT_(push_before, evt, id, obj);
1248 }
1249
1250 int afb_hook_evt_push_after(const char *evt, int id, struct json_object *obj, int result)
1251 {
1252         _HOOK_EVT_(push_after, evt, id, obj, result);
1253         return result;
1254 }
1255
1256 void afb_hook_evt_broadcast_before(const char *evt, int id, struct json_object *obj)
1257 {
1258         _HOOK_EVT_(broadcast_before, evt, id, obj);
1259 }
1260
1261 int afb_hook_evt_broadcast_after(const char *evt, int id, struct json_object *obj, int result)
1262 {
1263         _HOOK_EVT_(broadcast_after, evt, id, obj, result);
1264         return result;
1265 }
1266
1267 void afb_hook_evt_name(const char *evt, int id)
1268 {
1269         _HOOK_EVT_(name, evt, id);
1270 }
1271
1272 void afb_hook_evt_drop(const char *evt, int id)
1273 {
1274         _HOOK_EVT_(drop, evt, id);
1275 }
1276
1277 /******************************************************************************
1278  * section: hooking services (evt)
1279  *****************************************************************************/
1280
1281 int afb_hook_flags_evt(const char *name)
1282 {
1283         int flags;
1284         struct afb_hook_evt *hook;
1285
1286         pthread_rwlock_rdlock(&rwlock);
1287         flags = 0;
1288         hook = list_of_evt_hooks;
1289         while (hook) {
1290                 if (!name || !hook->pattern || !fnmatch(hook->pattern, name, FNM_CASEFOLD))
1291                         flags |= hook->flags;
1292                 hook = hook->next;
1293         }
1294         pthread_rwlock_unlock(&rwlock);
1295         return flags;
1296 }
1297
1298 struct afb_hook_evt *afb_hook_create_evt(const char *pattern, int flags, struct afb_hook_evt_itf *itf, void *closure)
1299 {
1300         struct afb_hook_evt *hook;
1301
1302         /* alloc the result */
1303         hook = calloc(1, sizeof *hook);
1304         if (hook == NULL)
1305                 return NULL;
1306
1307         /* get a copy of the names */
1308         hook->pattern = pattern ? strdup(pattern) : NULL;
1309         if (pattern && !hook->pattern) {
1310                 free(hook);
1311                 return NULL;
1312         }
1313
1314         /* initialise the rest */
1315         hook->refcount = 1;
1316         hook->flags = flags;
1317         hook->itf = itf ? itf : &hook_evt_default_itf;
1318         hook->closure = closure;
1319
1320         /* record the hook */
1321         pthread_rwlock_wrlock(&rwlock);
1322         hook->next = list_of_evt_hooks;
1323         list_of_evt_hooks = hook;
1324         pthread_rwlock_unlock(&rwlock);
1325
1326         /* returns it */
1327         return hook;
1328 }
1329
1330 struct afb_hook_evt *afb_hook_addref_evt(struct afb_hook_evt *hook)
1331 {
1332         pthread_rwlock_wrlock(&rwlock);
1333         hook->refcount++;
1334         pthread_rwlock_unlock(&rwlock);
1335         return hook;
1336 }
1337
1338 void afb_hook_unref_evt(struct afb_hook_evt *hook)
1339 {
1340         struct afb_hook_evt **prv;
1341
1342         if (hook) {
1343                 pthread_rwlock_wrlock(&rwlock);
1344                 if (--hook->refcount)
1345                         hook = NULL;
1346                 else {
1347                         /* unlink */
1348                         prv = &list_of_evt_hooks;
1349                         while (*prv && *prv != hook)
1350                                 prv = &(*prv)->next;
1351                         if(*prv)
1352                                 *prv = hook->next;
1353                 }
1354                 pthread_rwlock_unlock(&rwlock);
1355                 if (hook) {
1356                         /* free */
1357                         free(hook->pattern);
1358                         free(hook);
1359                 }
1360         }
1361 }
1362
1363 /******************************************************************************
1364  * section: default callbacks for globals (global)
1365  *****************************************************************************/
1366
1367 static void _hook_global_(const char *format, ...)
1368 {
1369         va_list ap;
1370         va_start(ap, format);
1371         _hook_("global", format, ap);
1372         va_end(ap);
1373 }
1374
1375 static void hook_global_vverbose_default_cb(void *closure, const struct afb_hookid *hookid, int level, const char *file, int line, const char *func, const char *fmt, va_list args)
1376 {
1377         int len;
1378         char *msg;
1379         va_list ap;
1380
1381         va_copy(ap, args);
1382         len = vasprintf(&msg, fmt, ap);
1383         va_end(ap);
1384
1385         if (len < 0)
1386                 _hook_global_("vverbose(%d, %s, %d, %s) -> %s ? ? ?", level, file, line, func, fmt);
1387         else {
1388                 _hook_global_("vverbose(%d, %s, %d, %s) -> %s", level, file, line, func, msg);
1389                 free(msg);
1390         }
1391 }
1392
1393 static struct afb_hook_global_itf hook_global_default_itf = {
1394         .hook_global_vverbose = hook_global_vverbose_default_cb
1395 };
1396
1397 /******************************************************************************
1398  * section: hooks for tracing globals (global)
1399  *****************************************************************************/
1400
1401 #define _HOOK_GLOBAL_(what,...)   \
1402         struct afb_hook_global *hook; \
1403         struct afb_hookid hookid; \
1404         pthread_rwlock_rdlock(&rwlock); \
1405         init_hookid(&hookid); \
1406         hook = list_of_global_hooks; \
1407         while (hook) { \
1408                 if (hook->itf->hook_global_##what \
1409                  && (hook->flags & afb_hook_flag_global_##what) != 0) { \
1410                         hook->itf->hook_global_##what(hook->closure, &hookid, __VA_ARGS__); \
1411                 } \
1412                 hook = hook->next; \
1413         } \
1414         pthread_rwlock_unlock(&rwlock);
1415
1416 static void afb_hook_global_vverbose(int level, const char *file, int line, const char *func, const char *fmt, va_list args)
1417 {
1418         _HOOK_GLOBAL_(vverbose, level, file ?: "?", line, func ?: "?", fmt, args);
1419 }
1420
1421 /******************************************************************************
1422  * section: hooking globals (global)
1423  *****************************************************************************/
1424
1425 static void update_global()
1426 {
1427         struct afb_hook_global *hook;
1428         int flags = 0;
1429
1430         pthread_rwlock_rdlock(&rwlock);
1431         hook = list_of_global_hooks;
1432         while (hook) {
1433                 flags = hook->flags;
1434                 hook = hook->next;
1435         }
1436         verbose_observer = (flags & afb_hook_flag_global_vverbose) ? afb_hook_global_vverbose : NULL;
1437         pthread_rwlock_unlock(&rwlock);
1438 }
1439
1440 struct afb_hook_global *afb_hook_create_global(int flags, struct afb_hook_global_itf *itf, void *closure)
1441 {
1442         struct afb_hook_global *hook;
1443
1444         /* alloc the result */
1445         hook = calloc(1, sizeof *hook);
1446         if (hook == NULL)
1447                 return NULL;
1448
1449         /* initialise the rest */
1450         hook->refcount = 1;
1451         hook->flags = flags;
1452         hook->itf = itf ? itf : &hook_global_default_itf;
1453         hook->closure = closure;
1454
1455         /* record the hook */
1456         pthread_rwlock_wrlock(&rwlock);
1457         hook->next = list_of_global_hooks;
1458         list_of_global_hooks = hook;
1459         pthread_rwlock_unlock(&rwlock);
1460
1461         /* update hooking */
1462         update_global();
1463
1464         /* returns it */
1465         return hook;
1466 }
1467
1468 struct afb_hook_global *afb_hook_addref_global(struct afb_hook_global *hook)
1469 {
1470         pthread_rwlock_wrlock(&rwlock);
1471         hook->refcount++;
1472         pthread_rwlock_unlock(&rwlock);
1473         return hook;
1474 }
1475
1476 void afb_hook_unref_global(struct afb_hook_global *hook)
1477 {
1478         struct afb_hook_global **prv;
1479
1480         if (hook) {
1481                 pthread_rwlock_wrlock(&rwlock);
1482                 if (--hook->refcount)
1483                         hook = NULL;
1484                 else {
1485                         /* unlink */
1486                         prv = &list_of_global_hooks;
1487                         while (*prv && *prv != hook)
1488                                 prv = &(*prv)->next;
1489                         if(*prv)
1490                                 *prv = hook->next;
1491                 }
1492                 pthread_rwlock_unlock(&rwlock);
1493                 if (hook) {
1494                         /* free */
1495                         free(hook);
1496
1497                         /* update hooking */
1498                         update_global();
1499                 }
1500         }
1501 }
1502