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