3c015dcfd1838918c41c8b923355b3354c002311
[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_fullname(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_fullname(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 void hook_xreq_get_application_id_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq, char *result)
358 {
359         _hook_xreq_(xreq, "get_application_id() -> %s", result);
360 }
361
362 static void hook_xreq_context_make_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq, int replace, void *(*create_value)(void*), void (*free_value)(void*), void *create_closure, void *result)
363 {
364         _hook_xreq_(xreq, "context_make(replace=%s, %p, %p, %p) -> %p", replace?"yes":"no", create_value, free_value, create_closure, result);
365 }
366
367 static struct afb_hook_xreq_itf hook_xreq_default_itf = {
368         .hook_xreq_begin = hook_xreq_begin_default_cb,
369         .hook_xreq_end = hook_xreq_end_default_cb,
370         .hook_xreq_json = hook_xreq_json_default_cb,
371         .hook_xreq_get = hook_xreq_get_default_cb,
372         .hook_xreq_success = hook_xreq_success_default_cb,
373         .hook_xreq_fail = hook_xreq_fail_default_cb,
374         .hook_xreq_context_get = hook_xreq_context_get_default_cb,
375         .hook_xreq_context_set = hook_xreq_context_set_default_cb,
376         .hook_xreq_addref = hook_xreq_addref_default_cb,
377         .hook_xreq_unref = hook_xreq_unref_default_cb,
378         .hook_xreq_session_close = hook_xreq_session_close_default_cb,
379         .hook_xreq_session_set_LOA = hook_xreq_session_set_LOA_default_cb,
380         .hook_xreq_subscribe = hook_xreq_subscribe_default_cb,
381         .hook_xreq_unsubscribe = hook_xreq_unsubscribe_default_cb,
382         .hook_xreq_subcall = hook_xreq_subcall_default_cb,
383         .hook_xreq_subcall_result = hook_xreq_subcall_result_default_cb,
384         .hook_xreq_subcallsync = hook_xreq_subcallsync_default_cb,
385         .hook_xreq_subcallsync_result = hook_xreq_subcallsync_result_default_cb,
386         .hook_xreq_vverbose = hook_xreq_vverbose_default_cb,
387         .hook_xreq_store = hook_xreq_store_default_cb,
388         .hook_xreq_unstore = hook_xreq_unstore_default_cb,
389         .hook_xreq_subcall_req = hook_xreq_subcall_req_default_cb,
390         .hook_xreq_subcall_req_result = hook_xreq_subcall_req_result_default_cb,
391         .hook_xreq_has_permission = hook_xreq_has_permission_default_cb,
392         .hook_xreq_get_application_id = hook_xreq_get_application_id_default_cb,
393         .hook_xreq_context_make = hook_xreq_context_make_default_cb
394 };
395
396 /******************************************************************************
397  * section: hooks for tracing requests
398  *****************************************************************************/
399
400 #define _HOOK_XREQ_(what,...)   \
401         struct afb_hook_xreq *hook; \
402         struct afb_hookid hookid; \
403         pthread_rwlock_rdlock(&rwlock); \
404         init_hookid(&hookid); \
405         hook = list_of_xreq_hooks; \
406         while (hook) { \
407                 if (hook->itf->hook_xreq_##what \
408                  && (hook->flags & afb_hook_flag_req_##what) != 0 \
409                  && (!hook->session || hook->session == xreq->context.session) \
410                  && (!hook->api || !strcasecmp(hook->api, xreq->api)) \
411                  && (!hook->verb || !strcasecmp(hook->verb, xreq->verb))) { \
412                         hook->itf->hook_xreq_##what(hook->closure, &hookid, __VA_ARGS__); \
413                 } \
414                 hook = hook->next; \
415         } \
416         pthread_rwlock_unlock(&rwlock);
417
418
419 void afb_hook_xreq_begin(const struct afb_xreq *xreq)
420 {
421         _HOOK_XREQ_(begin, xreq);
422 }
423
424 void afb_hook_xreq_end(const struct afb_xreq *xreq)
425 {
426         _HOOK_XREQ_(end, xreq);
427 }
428
429 struct json_object *afb_hook_xreq_json(const struct afb_xreq *xreq, struct json_object *obj)
430 {
431         _HOOK_XREQ_(json, xreq, obj);
432         return obj;
433 }
434
435 struct afb_arg afb_hook_xreq_get(const struct afb_xreq *xreq, const char *name, struct afb_arg arg)
436 {
437         _HOOK_XREQ_(get, xreq, name, arg);
438         return arg;
439 }
440
441 void afb_hook_xreq_success(const struct afb_xreq *xreq, struct json_object *obj, const char *info)
442 {
443         _HOOK_XREQ_(success, xreq, obj, info);
444 }
445
446 void afb_hook_xreq_fail(const struct afb_xreq *xreq, const char *status, const char *info)
447 {
448         _HOOK_XREQ_(fail, xreq, status, info);
449 }
450
451 void *afb_hook_xreq_context_get(const struct afb_xreq *xreq, void *value)
452 {
453         _HOOK_XREQ_(context_get, xreq, value);
454         return value;
455 }
456
457 void afb_hook_xreq_context_set(const struct afb_xreq *xreq, void *value, void (*free_value)(void*))
458 {
459         _HOOK_XREQ_(context_set, xreq, value, free_value);
460 }
461
462 void afb_hook_xreq_addref(const struct afb_xreq *xreq)
463 {
464         _HOOK_XREQ_(addref, xreq);
465 }
466
467 void afb_hook_xreq_unref(const struct afb_xreq *xreq)
468 {
469         _HOOK_XREQ_(unref, xreq);
470 }
471
472 void afb_hook_xreq_session_close(const struct afb_xreq *xreq)
473 {
474         _HOOK_XREQ_(session_close, xreq);
475 }
476
477 int afb_hook_xreq_session_set_LOA(const struct afb_xreq *xreq, unsigned level, int result)
478 {
479         _HOOK_XREQ_(session_set_LOA, xreq, level, result);
480         return result;
481 }
482
483 int afb_hook_xreq_subscribe(const struct afb_xreq *xreq, struct afb_event event, int result)
484 {
485         _HOOK_XREQ_(subscribe, xreq, event, result);
486         return result;
487 }
488
489 int afb_hook_xreq_unsubscribe(const struct afb_xreq *xreq, struct afb_event event, int result)
490 {
491         _HOOK_XREQ_(unsubscribe, xreq, event, result);
492         return result;
493 }
494
495 void afb_hook_xreq_subcall(const struct afb_xreq *xreq, const char *api, const char *verb, struct json_object *args)
496 {
497         _HOOK_XREQ_(subcall, xreq, api, verb, args);
498 }
499
500 void afb_hook_xreq_subcall_result(const struct afb_xreq *xreq, int status, struct json_object *result)
501 {
502         _HOOK_XREQ_(subcall_result, xreq, status, result);
503 }
504
505 void afb_hook_xreq_subcallsync(const struct afb_xreq *xreq, const char *api, const char *verb, struct json_object *args)
506 {
507         _HOOK_XREQ_(subcallsync, xreq, api, verb, args);
508 }
509
510 int afb_hook_xreq_subcallsync_result(const struct afb_xreq *xreq, int status, struct json_object *result)
511 {
512         _HOOK_XREQ_(subcallsync_result, xreq, status, result);
513         return status;
514 }
515
516 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)
517 {
518         _HOOK_XREQ_(vverbose, xreq, level, file ?: "?", line, func ?: "?", fmt, args);
519 }
520
521 void afb_hook_xreq_store(const struct afb_xreq *xreq, struct afb_stored_req *sreq)
522 {
523         _HOOK_XREQ_(store, xreq, sreq);
524 }
525
526 void afb_hook_xreq_unstore(const struct afb_xreq *xreq)
527 {
528         _HOOK_XREQ_(unstore, xreq);
529 }
530
531 void afb_hook_xreq_subcall_req(const struct afb_xreq *xreq, const char *api, const char *verb, struct json_object *args)
532 {
533         _HOOK_XREQ_(subcall_req, xreq, api, verb, args);
534 }
535
536 void afb_hook_xreq_subcall_req_result(const struct afb_xreq *xreq, int status, struct json_object *result)
537 {
538         _HOOK_XREQ_(subcall_req_result, xreq, status, result);
539 }
540
541 int afb_hook_xreq_has_permission(const struct afb_xreq *xreq, const char *permission, int result)
542 {
543         _HOOK_XREQ_(has_permission, xreq, permission, result);
544         return result;
545 }
546
547 char *afb_hook_xreq_get_application_id(const struct afb_xreq *xreq, char *result)
548 {
549         _HOOK_XREQ_(get_application_id, xreq, result);
550         return result;
551 }
552
553 void *afb_hook_xreq_context_make(const struct afb_xreq *xreq, int replace, void *(*create_value)(void*), void (*free_value)(void*), void *create_closure, void *result)
554 {
555         _HOOK_XREQ_(context_make, xreq, replace, create_value, free_value, create_closure, result);
556         return result;
557 }
558
559 /******************************************************************************
560  * section: hooking xreqs
561  *****************************************************************************/
562
563 void afb_hook_init_xreq(struct afb_xreq *xreq)
564 {
565         static int reqindex;
566
567         int f, flags;
568         int add;
569         struct afb_hook_xreq *hook;
570
571         /* scan hook list to get the expected flags */
572         flags = 0;
573         pthread_rwlock_rdlock(&rwlock);
574         hook = list_of_xreq_hooks;
575         while (hook) {
576                 f = hook->flags & afb_hook_flags_req_all;
577                 add = f != 0
578                    && (!hook->session || hook->session == xreq->context.session)
579                    && (!hook->api || !strcasecmp(hook->api, xreq->api))
580                    && (!hook->verb || !strcasecmp(hook->verb, xreq->verb));
581                 if (add)
582                         flags |= f;
583                 hook = hook->next;
584         }
585         pthread_rwlock_unlock(&rwlock);
586
587         /* store the hooking data */
588         xreq->hookflags = flags;
589         if (flags) {
590                 pthread_rwlock_wrlock(&rwlock);
591                 if (++reqindex < 0)
592                         reqindex = 1;
593                 xreq->hookindex = reqindex;
594                 pthread_rwlock_unlock(&rwlock);
595         }
596 }
597
598 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)
599 {
600         struct afb_hook_xreq *hook;
601
602         /* alloc the result */
603         hook = calloc(1, sizeof *hook);
604         if (hook == NULL)
605                 return NULL;
606
607         /* get a copy of the names */
608         hook->api = api ? strdup(api) : NULL;
609         hook->verb = verb ? strdup(verb) : NULL;
610         if ((api && !hook->api) || (verb && !hook->verb)) {
611                 free(hook->api);
612                 free(hook->verb);
613                 free(hook);
614                 return NULL;
615         }
616
617         /* initialise the rest */
618         hook->session = session;
619         if (session)
620                 afb_session_addref(session);
621         hook->refcount = 1;
622         hook->flags = flags;
623         hook->itf = itf ? itf : &hook_xreq_default_itf;
624         hook->closure = closure;
625
626         /* record the hook */
627         pthread_rwlock_wrlock(&rwlock);
628         hook->next = list_of_xreq_hooks;
629         list_of_xreq_hooks = hook;
630         pthread_rwlock_unlock(&rwlock);
631
632         /* returns it */
633         return hook;
634 }
635
636 struct afb_hook_xreq *afb_hook_addref_xreq(struct afb_hook_xreq *hook)
637 {
638         pthread_rwlock_wrlock(&rwlock);
639         hook->refcount++;
640         pthread_rwlock_unlock(&rwlock);
641         return hook;
642 }
643
644 void afb_hook_unref_xreq(struct afb_hook_xreq *hook)
645 {
646         struct afb_hook_xreq **prv;
647
648         if (hook) {
649                 pthread_rwlock_wrlock(&rwlock);
650                 if (--hook->refcount)
651                         hook = NULL;
652                 else {
653                         /* unlink */
654                         prv = &list_of_xreq_hooks;
655                         while (*prv && *prv != hook)
656                                 prv = &(*prv)->next;
657                         if(*prv)
658                                 *prv = hook->next;
659                 }
660                 pthread_rwlock_unlock(&rwlock);
661                 if (hook) {
662                         /* free */
663                         free(hook->api);
664                         free(hook->verb);
665                         if (hook->session)
666                                 afb_session_unref(hook->session);
667                         free(hook);
668                 }
669         }
670 }
671
672 /******************************************************************************
673  * section: default callbacks for tracing daemon interface
674  *****************************************************************************/
675
676 static void _hook_ditf_(const struct afb_export *export, const char *format, ...)
677 {
678         va_list ap;
679         va_start(ap, format);
680         _hook_("export-%s", format, ap, afb_export_apiname(export));
681         va_end(ap);
682 }
683
684 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)
685 {
686         _hook_ditf_(export, "event_broadcast.before(%s, %s)....", name, json_object_to_json_string(object));
687 }
688
689 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)
690 {
691         _hook_ditf_(export, "event_broadcast.after(%s, %s) -> %d", name, json_object_to_json_string(object), result);
692 }
693
694 static void hook_ditf_get_event_loop_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export, struct sd_event *result)
695 {
696         _hook_ditf_(export, "get_event_loop() -> %p", result);
697 }
698
699 static void hook_ditf_get_user_bus_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export, struct sd_bus *result)
700 {
701         _hook_ditf_(export, "get_user_bus() -> %p", result);
702 }
703
704 static void hook_ditf_get_system_bus_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export, struct sd_bus *result)
705 {
706         _hook_ditf_(export, "get_system_bus() -> %p", result);
707 }
708
709 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)
710 {
711         int len;
712         char *msg;
713         va_list ap;
714
715         va_copy(ap, args);
716         len = vasprintf(&msg, fmt, ap);
717         va_end(ap);
718
719         if (len < 0)
720                 _hook_ditf_(export, "vverbose(%d, %s, %d, %s) -> %s ? ? ?", level, file, line, function, fmt);
721         else {
722                 _hook_ditf_(export, "vverbose(%d, %s, %d, %s) -> %s", level, file, line, function, msg);
723                 free(msg);
724         }
725 }
726
727 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)
728 {
729         _hook_ditf_(export, "event_make(%s) -> %s:%d", name, afb_evt_event_fullname(result), afb_evt_event_id(result));
730 }
731
732 static void hook_ditf_rootdir_get_fd_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export, int result)
733 {
734         char path[PATH_MAX];
735         if (result < 0)
736                 _hook_ditf_(export, "rootdir_get_fd() -> %d, %m", result);
737         else {
738                 sprintf(path, "/proc/self/fd/%d", result);
739                 readlink(path, path, sizeof path);
740                 _hook_ditf_(export, "rootdir_get_fd() -> %d = %s", result, path);
741         }
742 }
743
744 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)
745 {
746         char path[PATH_MAX];
747         if (!locale)
748                 locale = "(null)";
749         if (result < 0)
750                 _hook_ditf_(export, "rootdir_open_locale(%s, %d, %s) -> %d, %m", filename, flags, locale, result);
751         else {
752                 sprintf(path, "/proc/self/fd/%d", result);
753                 readlink(path, path, sizeof path);
754                 _hook_ditf_(export, "rootdir_open_locale(%s, %d, %s) -> %d = %s", filename, flags, locale, result, path);
755         }
756 }
757
758 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)
759 {
760         _hook_ditf_(export, "queue_job(%p, %p, %p, %d) -> %d", callback, argument, group, timeout, result);
761 }
762
763 static void hook_ditf_unstore_req_cb(void *closure, const struct afb_hookid *hookid,  const struct afb_export *export, struct afb_stored_req *sreq)
764 {
765         _hook_ditf_(export, "unstore_req(%p)", sreq);
766 }
767
768 static void hook_ditf_require_api_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export, const char *name, int initialized)
769 {
770         _hook_ditf_(export, "require_api(%s, %d)...", name, initialized);
771 }
772
773 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)
774 {
775         _hook_ditf_(export, "...require_api(%s, %d) -> %d", name, initialized, result);
776 }
777
778 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)
779 {
780         _hook_ditf_(export, "rename_api(%s -> %s) -> %d", oldname, newname, result);
781 }
782
783 static struct afb_hook_ditf_itf hook_ditf_default_itf = {
784         .hook_ditf_event_broadcast_before = hook_ditf_event_broadcast_before_cb,
785         .hook_ditf_event_broadcast_after = hook_ditf_event_broadcast_after_cb,
786         .hook_ditf_get_event_loop = hook_ditf_get_event_loop_cb,
787         .hook_ditf_get_user_bus = hook_ditf_get_user_bus_cb,
788         .hook_ditf_get_system_bus = hook_ditf_get_system_bus_cb,
789         .hook_ditf_vverbose = hook_ditf_vverbose_cb,
790         .hook_ditf_event_make = hook_ditf_event_make_cb,
791         .hook_ditf_rootdir_get_fd = hook_ditf_rootdir_get_fd_cb,
792         .hook_ditf_rootdir_open_locale = hook_ditf_rootdir_open_locale_cb,
793         .hook_ditf_queue_job = hook_ditf_queue_job_cb,
794         .hook_ditf_unstore_req = hook_ditf_unstore_req_cb,
795         .hook_ditf_require_api = hook_ditf_require_api_cb,
796         .hook_ditf_require_api_result = hook_ditf_require_api_result_cb,
797         .hook_ditf_rename_api = hook_ditf_rename_api_cb
798 };
799
800 /******************************************************************************
801  * section: hooks for tracing daemon interface (export)
802  *****************************************************************************/
803
804 #define _HOOK_DITF_(what,...)   \
805         struct afb_hook_ditf *hook; \
806         struct afb_hookid hookid; \
807         const char *apiname = afb_export_apiname(export); \
808         pthread_rwlock_rdlock(&rwlock); \
809         init_hookid(&hookid); \
810         hook = list_of_ditf_hooks; \
811         while (hook) { \
812                 if (hook->itf->hook_ditf_##what \
813                  && (hook->flags & afb_hook_flag_ditf_##what) != 0 \
814                  && (!hook->api || !strcasecmp(hook->api, apiname))) { \
815                         hook->itf->hook_ditf_##what(hook->closure, &hookid, __VA_ARGS__); \
816                 } \
817                 hook = hook->next; \
818         } \
819         pthread_rwlock_unlock(&rwlock);
820
821 void afb_hook_ditf_event_broadcast_before(const struct afb_export *export, const char *name, struct json_object *object)
822 {
823         _HOOK_DITF_(event_broadcast_before, export, name, object);
824 }
825
826 int afb_hook_ditf_event_broadcast_after(const struct afb_export *export, const char *name, struct json_object *object, int result)
827 {
828         _HOOK_DITF_(event_broadcast_after, export, name, object, result);
829         return result;
830 }
831
832 struct sd_event *afb_hook_ditf_get_event_loop(const struct afb_export *export, struct sd_event *result)
833 {
834         _HOOK_DITF_(get_event_loop, export, result);
835         return result;
836 }
837
838 struct sd_bus *afb_hook_ditf_get_user_bus(const struct afb_export *export, struct sd_bus *result)
839 {
840         _HOOK_DITF_(get_user_bus, export, result);
841         return result;
842 }
843
844 struct sd_bus *afb_hook_ditf_get_system_bus(const struct afb_export *export, struct sd_bus *result)
845 {
846         _HOOK_DITF_(get_system_bus, export, result);
847         return result;
848 }
849
850 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)
851 {
852         _HOOK_DITF_(vverbose, export, level, file, line, function, fmt, args);
853 }
854
855 struct afb_event afb_hook_ditf_event_make(const struct afb_export *export, const char *name, struct afb_event result)
856 {
857         _HOOK_DITF_(event_make, export, name, result);
858         return result;
859 }
860
861 int afb_hook_ditf_rootdir_get_fd(const struct afb_export *export, int result)
862 {
863         _HOOK_DITF_(rootdir_get_fd, export, result);
864         return result;
865 }
866
867 int afb_hook_ditf_rootdir_open_locale(const struct afb_export *export, const char *filename, int flags, const char *locale, int result)
868 {
869         _HOOK_DITF_(rootdir_open_locale, export, filename, flags, locale, result);
870         return result;
871 }
872
873 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)
874 {
875         _HOOK_DITF_(queue_job, export, callback, argument, group, timeout, result);
876         return result;
877 }
878
879 void afb_hook_ditf_unstore_req(const struct afb_export *export, struct afb_stored_req *sreq)
880 {
881         _HOOK_DITF_(unstore_req, export, sreq);
882 }
883
884 void afb_hook_ditf_require_api(const struct afb_export *export, const char *name, int initialized)
885 {
886         _HOOK_DITF_(require_api, export, name, initialized);
887 }
888
889 int afb_hook_ditf_require_api_result(const struct afb_export *export, const char *name, int initialized, int result)
890 {
891         _HOOK_DITF_(require_api_result, export, name, initialized, result);
892         return result;
893 }
894
895 int afb_hook_ditf_rename_api(const struct afb_export *export, const char *oldname, const char *newname, int result)
896 {
897         _HOOK_DITF_(rename_api, export, oldname, newname, result);
898         return result;
899 }
900
901 /******************************************************************************
902  * section: hooking export
903  *****************************************************************************/
904
905 int afb_hook_flags_ditf(const char *api)
906 {
907         int flags;
908         struct afb_hook_ditf *hook;
909
910         pthread_rwlock_rdlock(&rwlock);
911         flags = 0;
912         hook = list_of_ditf_hooks;
913         while (hook) {
914                 if (!api || !hook->api || !strcasecmp(hook->api, api))
915                         flags |= hook->flags;
916                 hook = hook->next;
917         }
918         pthread_rwlock_unlock(&rwlock);
919         return flags;
920 }
921
922 struct afb_hook_ditf *afb_hook_create_ditf(const char *api, int flags, struct afb_hook_ditf_itf *itf, void *closure)
923 {
924         struct afb_hook_ditf *hook;
925
926         /* alloc the result */
927         hook = calloc(1, sizeof *hook);
928         if (hook == NULL)
929                 return NULL;
930
931         /* get a copy of the names */
932         hook->api = api ? strdup(api) : NULL;
933         if (api && !hook->api) {
934                 free(hook);
935                 return NULL;
936         }
937
938         /* initialise the rest */
939         hook->refcount = 1;
940         hook->flags = flags;
941         hook->itf = itf ? itf : &hook_ditf_default_itf;
942         hook->closure = closure;
943
944         /* record the hook */
945         pthread_rwlock_wrlock(&rwlock);
946         hook->next = list_of_ditf_hooks;
947         list_of_ditf_hooks = hook;
948         pthread_rwlock_unlock(&rwlock);
949
950         /* returns it */
951         return hook;
952 }
953
954 struct afb_hook_ditf *afb_hook_addref_ditf(struct afb_hook_ditf *hook)
955 {
956         pthread_rwlock_wrlock(&rwlock);
957         hook->refcount++;
958         pthread_rwlock_unlock(&rwlock);
959         return hook;
960 }
961
962 void afb_hook_unref_ditf(struct afb_hook_ditf *hook)
963 {
964         struct afb_hook_ditf **prv;
965
966         if (hook) {
967                 pthread_rwlock_wrlock(&rwlock);
968                 if (--hook->refcount)
969                         hook = NULL;
970                 else {
971                         /* unlink */
972                         prv = &list_of_ditf_hooks;
973                         while (*prv && *prv != hook)
974                                 prv = &(*prv)->next;
975                         if(*prv)
976                                 *prv = hook->next;
977                 }
978                 pthread_rwlock_unlock(&rwlock);
979                 if (hook) {
980                         /* free */
981                         free(hook->api);
982                         free(hook);
983                 }
984         }
985 }
986
987 /******************************************************************************
988  * section: default callbacks for tracing service interface (export)
989  *****************************************************************************/
990
991 static void _hook_svc_(const struct afb_export *export, const char *format, ...)
992 {
993         va_list ap;
994         va_start(ap, format);
995         _hook_("export-%s", format, ap, afb_export_apiname(export));
996         va_end(ap);
997 }
998
999 static void hook_svc_start_before_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export)
1000 {
1001         _hook_svc_(export, "start.before");
1002 }
1003
1004 static void hook_svc_start_after_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export, int status)
1005 {
1006         _hook_svc_(export, "start.after -> %d", status);
1007 }
1008
1009 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)
1010 {
1011         _hook_svc_(export, "on_event.before(%s, %d, %s)", event, eventid, json_object_to_json_string(object));
1012 }
1013
1014 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)
1015 {
1016         _hook_svc_(export, "on_event.after(%s, %d, %s)", event, eventid, json_object_to_json_string(object));
1017 }
1018
1019 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)
1020 {
1021         _hook_svc_(export, "call(%s/%s, %s) ...", api, verb, json_object_to_json_string(args));
1022 }
1023
1024 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)
1025 {
1026         _hook_svc_(export, "    ...call... -> %d: %s", status, json_object_to_json_string(result));
1027 }
1028
1029 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)
1030 {
1031         _hook_svc_(export, "callsync(%s/%s, %s) ...", api, verb, json_object_to_json_string(args));
1032 }
1033
1034 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)
1035 {
1036         _hook_svc_(export, "    ...callsync... -> %d: %s", status, json_object_to_json_string(result));
1037 }
1038
1039 static struct afb_hook_svc_itf hook_svc_default_itf = {
1040         .hook_svc_start_before = hook_svc_start_before_default_cb,
1041         .hook_svc_start_after = hook_svc_start_after_default_cb,
1042         .hook_svc_on_event_before = hook_svc_on_event_before_default_cb,
1043         .hook_svc_on_event_after = hook_svc_on_event_after_default_cb,
1044         .hook_svc_call = hook_svc_call_default_cb,
1045         .hook_svc_call_result = hook_svc_call_result_default_cb,
1046         .hook_svc_callsync = hook_svc_callsync_default_cb,
1047         .hook_svc_callsync_result = hook_svc_callsync_result_default_cb
1048 };
1049
1050 /******************************************************************************
1051  * section: hooks for tracing service interface (export)
1052  *****************************************************************************/
1053
1054 #define _HOOK_SVC_(what,...)   \
1055         struct afb_hook_svc *hook; \
1056         struct afb_hookid hookid; \
1057         const char *apiname = afb_export_apiname(export); \
1058         pthread_rwlock_rdlock(&rwlock); \
1059         init_hookid(&hookid); \
1060         hook = list_of_svc_hooks; \
1061         while (hook) { \
1062                 if (hook->itf->hook_svc_##what \
1063                  && (hook->flags & afb_hook_flag_svc_##what) != 0 \
1064                  && (!hook->api || !strcasecmp(hook->api, apiname))) { \
1065                         hook->itf->hook_svc_##what(hook->closure, &hookid, __VA_ARGS__); \
1066                 } \
1067                 hook = hook->next; \
1068         } \
1069         pthread_rwlock_unlock(&rwlock);
1070
1071 void afb_hook_svc_start_before(const struct afb_export *export)
1072 {
1073         _HOOK_SVC_(start_before, export);
1074 }
1075
1076 int afb_hook_svc_start_after(const struct afb_export *export, int status)
1077 {
1078         _HOOK_SVC_(start_after, export, status);
1079         return status;
1080 }
1081
1082 void afb_hook_svc_on_event_before(const struct afb_export *export, const char *event, int eventid, struct json_object *object)
1083 {
1084         _HOOK_SVC_(on_event_before, export, event, eventid, object);
1085 }
1086
1087 void afb_hook_svc_on_event_after(const struct afb_export *export, const char *event, int eventid, struct json_object *object)
1088 {
1089         _HOOK_SVC_(on_event_after, export, event, eventid, object);
1090 }
1091
1092 void afb_hook_svc_call(const struct afb_export *export, const char *api, const char *verb, struct json_object *args)
1093 {
1094         _HOOK_SVC_(call, export, api, verb, args);
1095 }
1096
1097 void afb_hook_svc_call_result(const struct afb_export *export, int status, struct json_object *result)
1098 {
1099         _HOOK_SVC_(call_result, export, status, result);
1100 }
1101
1102 void afb_hook_svc_callsync(const struct afb_export *export, const char *api, const char *verb, struct json_object *args)
1103 {
1104         _HOOK_SVC_(callsync, export, api, verb, args);
1105 }
1106
1107 int afb_hook_svc_callsync_result(const struct afb_export *export, int status, struct json_object *result)
1108 {
1109         _HOOK_SVC_(callsync_result, export, status, result);
1110         return status;
1111 }
1112
1113 /******************************************************************************
1114  * section: hooking services (export)
1115  *****************************************************************************/
1116
1117 int afb_hook_flags_svc(const char *api)
1118 {
1119         int flags;
1120         struct afb_hook_svc *hook;
1121
1122         pthread_rwlock_rdlock(&rwlock);
1123         flags = 0;
1124         hook = list_of_svc_hooks;
1125         while (hook) {
1126                 if (!api || !hook->api || !strcasecmp(hook->api, api))
1127                         flags |= hook->flags;
1128                 hook = hook->next;
1129         }
1130         pthread_rwlock_unlock(&rwlock);
1131         return flags;
1132 }
1133
1134 struct afb_hook_svc *afb_hook_create_svc(const char *api, int flags, struct afb_hook_svc_itf *itf, void *closure)
1135 {
1136         struct afb_hook_svc *hook;
1137
1138         /* alloc the result */
1139         hook = calloc(1, sizeof *hook);
1140         if (hook == NULL)
1141                 return NULL;
1142
1143         /* get a copy of the names */
1144         hook->api = api ? strdup(api) : NULL;
1145         if (api && !hook->api) {
1146                 free(hook);
1147                 return NULL;
1148         }
1149
1150         /* initialise the rest */
1151         hook->refcount = 1;
1152         hook->flags = flags;
1153         hook->itf = itf ? itf : &hook_svc_default_itf;
1154         hook->closure = closure;
1155
1156         /* record the hook */
1157         pthread_rwlock_wrlock(&rwlock);
1158         hook->next = list_of_svc_hooks;
1159         list_of_svc_hooks = hook;
1160         pthread_rwlock_unlock(&rwlock);
1161
1162         /* returns it */
1163         return hook;
1164 }
1165
1166 struct afb_hook_svc *afb_hook_addref_svc(struct afb_hook_svc *hook)
1167 {
1168         pthread_rwlock_wrlock(&rwlock);
1169         hook->refcount++;
1170         pthread_rwlock_unlock(&rwlock);
1171         return hook;
1172 }
1173
1174 void afb_hook_unref_svc(struct afb_hook_svc *hook)
1175 {
1176         struct afb_hook_svc **prv;
1177
1178         if (hook) {
1179                 pthread_rwlock_wrlock(&rwlock);
1180                 if (--hook->refcount)
1181                         hook = NULL;
1182                 else {
1183                         /* unlink */
1184                         prv = &list_of_svc_hooks;
1185                         while (*prv && *prv != hook)
1186                                 prv = &(*prv)->next;
1187                         if(*prv)
1188                                 *prv = hook->next;
1189                 }
1190                 pthread_rwlock_unlock(&rwlock);
1191                 if (hook) {
1192                         /* free */
1193                         free(hook->api);
1194                         free(hook);
1195                 }
1196         }
1197 }
1198
1199 /******************************************************************************
1200  * section: default callbacks for tracing service interface (evt)
1201  *****************************************************************************/
1202
1203 static void _hook_evt_(const char *evt, int id, const char *format, ...)
1204 {
1205         va_list ap;
1206         va_start(ap, format);
1207         _hook_("evt-%s:%d", format, ap, evt, id);
1208         va_end(ap);
1209 }
1210
1211 static void hook_evt_create_default_cb(void *closure, const struct afb_hookid *hookid, const char *evt, int id)
1212 {
1213         _hook_evt_(evt, id, "create");
1214 }
1215
1216 static void hook_evt_push_before_default_cb(void *closure, const struct afb_hookid *hookid, const char *evt, int id, struct json_object *obj)
1217 {
1218         _hook_evt_(evt, id, "push.before(%s)", json_object_to_json_string(obj));
1219 }
1220
1221
1222 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)
1223 {
1224         _hook_evt_(evt, id, "push.after(%s) -> %d", json_object_to_json_string(obj), result);
1225 }
1226
1227 static void hook_evt_broadcast_before_default_cb(void *closure, const struct afb_hookid *hookid, const char *evt, int id, struct json_object *obj)
1228 {
1229         _hook_evt_(evt, id, "broadcast.before(%s)", json_object_to_json_string(obj));
1230 }
1231
1232 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)
1233 {
1234         _hook_evt_(evt, id, "broadcast.after(%s) -> %d", json_object_to_json_string(obj), result);
1235 }
1236
1237 static void hook_evt_name_default_cb(void *closure, const struct afb_hookid *hookid, const char *evt, int id, const char *result)
1238 {
1239         _hook_evt_(evt, id, "name -> %s", result);
1240 }
1241
1242 static void hook_evt_addref_default_cb(void *closure, const struct afb_hookid *hookid, const char *evt, int id)
1243 {
1244         _hook_evt_(evt, id, "addref");
1245 }
1246
1247 static void hook_evt_unref_default_cb(void *closure, const struct afb_hookid *hookid, const char *evt, int id)
1248 {
1249         _hook_evt_(evt, id, "unref");
1250 }
1251
1252 static struct afb_hook_evt_itf hook_evt_default_itf = {
1253         .hook_evt_create = hook_evt_create_default_cb,
1254         .hook_evt_push_before = hook_evt_push_before_default_cb,
1255         .hook_evt_push_after = hook_evt_push_after_default_cb,
1256         .hook_evt_broadcast_before = hook_evt_broadcast_before_default_cb,
1257         .hook_evt_broadcast_after = hook_evt_broadcast_after_default_cb,
1258         .hook_evt_name = hook_evt_name_default_cb,
1259         .hook_evt_addref = hook_evt_addref_default_cb,
1260         .hook_evt_unref = hook_evt_unref_default_cb
1261 };
1262
1263 /******************************************************************************
1264  * section: hooks for tracing events interface (evt)
1265  *****************************************************************************/
1266
1267 #define _HOOK_EVT_(what,...)   \
1268         struct afb_hook_evt *hook; \
1269         struct afb_hookid hookid; \
1270         pthread_rwlock_rdlock(&rwlock); \
1271         init_hookid(&hookid); \
1272         hook = list_of_evt_hooks; \
1273         while (hook) { \
1274                 if (hook->itf->hook_evt_##what \
1275                  && (hook->flags & afb_hook_flag_evt_##what) != 0 \
1276                  && (!hook->pattern || !fnmatch(hook->pattern, evt, FNM_CASEFOLD))) { \
1277                         hook->itf->hook_evt_##what(hook->closure, &hookid, __VA_ARGS__); \
1278                 } \
1279                 hook = hook->next; \
1280         } \
1281         pthread_rwlock_unlock(&rwlock);
1282
1283 void afb_hook_evt_create(const char *evt, int id)
1284 {
1285         _HOOK_EVT_(create, evt, id);
1286 }
1287
1288 void afb_hook_evt_push_before(const char *evt, int id, struct json_object *obj)
1289 {
1290         _HOOK_EVT_(push_before, evt, id, obj);
1291 }
1292
1293 int afb_hook_evt_push_after(const char *evt, int id, struct json_object *obj, int result)
1294 {
1295         _HOOK_EVT_(push_after, evt, id, obj, result);
1296         return result;
1297 }
1298
1299 void afb_hook_evt_broadcast_before(const char *evt, int id, struct json_object *obj)
1300 {
1301         _HOOK_EVT_(broadcast_before, evt, id, obj);
1302 }
1303
1304 int afb_hook_evt_broadcast_after(const char *evt, int id, struct json_object *obj, int result)
1305 {
1306         _HOOK_EVT_(broadcast_after, evt, id, obj, result);
1307         return result;
1308 }
1309
1310 void afb_hook_evt_name(const char *evt, int id, const char *result)
1311 {
1312         _HOOK_EVT_(name, evt, id, result);
1313 }
1314
1315 void afb_hook_evt_addref(const char *evt, int id)
1316 {
1317         _HOOK_EVT_(addref, evt, id);
1318 }
1319
1320 void afb_hook_evt_unref(const char *evt, int id)
1321 {
1322         _HOOK_EVT_(unref, evt, id);
1323 }
1324
1325 /******************************************************************************
1326  * section: hooking services (evt)
1327  *****************************************************************************/
1328
1329 int afb_hook_flags_evt(const char *name)
1330 {
1331         int flags;
1332         struct afb_hook_evt *hook;
1333
1334         pthread_rwlock_rdlock(&rwlock);
1335         flags = 0;
1336         hook = list_of_evt_hooks;
1337         while (hook) {
1338                 if (!name || !hook->pattern || !fnmatch(hook->pattern, name, FNM_CASEFOLD))
1339                         flags |= hook->flags;
1340                 hook = hook->next;
1341         }
1342         pthread_rwlock_unlock(&rwlock);
1343         return flags;
1344 }
1345
1346 struct afb_hook_evt *afb_hook_create_evt(const char *pattern, int flags, struct afb_hook_evt_itf *itf, void *closure)
1347 {
1348         struct afb_hook_evt *hook;
1349
1350         /* alloc the result */
1351         hook = calloc(1, sizeof *hook);
1352         if (hook == NULL)
1353                 return NULL;
1354
1355         /* get a copy of the names */
1356         hook->pattern = pattern ? strdup(pattern) : NULL;
1357         if (pattern && !hook->pattern) {
1358                 free(hook);
1359                 return NULL;
1360         }
1361
1362         /* initialise the rest */
1363         hook->refcount = 1;
1364         hook->flags = flags;
1365         hook->itf = itf ? itf : &hook_evt_default_itf;
1366         hook->closure = closure;
1367
1368         /* record the hook */
1369         pthread_rwlock_wrlock(&rwlock);
1370         hook->next = list_of_evt_hooks;
1371         list_of_evt_hooks = hook;
1372         pthread_rwlock_unlock(&rwlock);
1373
1374         /* returns it */
1375         return hook;
1376 }
1377
1378 struct afb_hook_evt *afb_hook_addref_evt(struct afb_hook_evt *hook)
1379 {
1380         pthread_rwlock_wrlock(&rwlock);
1381         hook->refcount++;
1382         pthread_rwlock_unlock(&rwlock);
1383         return hook;
1384 }
1385
1386 void afb_hook_unref_evt(struct afb_hook_evt *hook)
1387 {
1388         struct afb_hook_evt **prv;
1389
1390         if (hook) {
1391                 pthread_rwlock_wrlock(&rwlock);
1392                 if (--hook->refcount)
1393                         hook = NULL;
1394                 else {
1395                         /* unlink */
1396                         prv = &list_of_evt_hooks;
1397                         while (*prv && *prv != hook)
1398                                 prv = &(*prv)->next;
1399                         if(*prv)
1400                                 *prv = hook->next;
1401                 }
1402                 pthread_rwlock_unlock(&rwlock);
1403                 if (hook) {
1404                         /* free */
1405                         free(hook->pattern);
1406                         free(hook);
1407                 }
1408         }
1409 }
1410
1411 /******************************************************************************
1412  * section: default callbacks for globals (global)
1413  *****************************************************************************/
1414
1415 static void _hook_global_(const char *format, ...)
1416 {
1417         va_list ap;
1418         va_start(ap, format);
1419         _hook_("global", format, ap);
1420         va_end(ap);
1421 }
1422
1423 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)
1424 {
1425         int len;
1426         char *msg;
1427         va_list ap;
1428
1429         va_copy(ap, args);
1430         len = vasprintf(&msg, fmt, ap);
1431         va_end(ap);
1432
1433         if (len < 0)
1434                 _hook_global_("vverbose(%d, %s, %d, %s) -> %s ? ? ?", level, file, line, func, fmt);
1435         else {
1436                 _hook_global_("vverbose(%d, %s, %d, %s) -> %s", level, file, line, func, msg);
1437                 free(msg);
1438         }
1439 }
1440
1441 static struct afb_hook_global_itf hook_global_default_itf = {
1442         .hook_global_vverbose = hook_global_vverbose_default_cb
1443 };
1444
1445 /******************************************************************************
1446  * section: hooks for tracing globals (global)
1447  *****************************************************************************/
1448
1449 #define _HOOK_GLOBAL_(what,...)   \
1450         struct afb_hook_global *hook; \
1451         struct afb_hookid hookid; \
1452         pthread_rwlock_rdlock(&rwlock); \
1453         init_hookid(&hookid); \
1454         hook = list_of_global_hooks; \
1455         while (hook) { \
1456                 if (hook->itf->hook_global_##what \
1457                  && (hook->flags & afb_hook_flag_global_##what) != 0) { \
1458                         hook->itf->hook_global_##what(hook->closure, &hookid, __VA_ARGS__); \
1459                 } \
1460                 hook = hook->next; \
1461         } \
1462         pthread_rwlock_unlock(&rwlock);
1463
1464 static void afb_hook_global_vverbose(int level, const char *file, int line, const char *func, const char *fmt, va_list args)
1465 {
1466         _HOOK_GLOBAL_(vverbose, level, file ?: "?", line, func ?: "?", fmt ?: "", args);
1467 }
1468
1469 /******************************************************************************
1470  * section: hooking globals (global)
1471  *****************************************************************************/
1472
1473 static void update_global()
1474 {
1475         struct afb_hook_global *hook;
1476         int flags = 0;
1477
1478         pthread_rwlock_rdlock(&rwlock);
1479         hook = list_of_global_hooks;
1480         while (hook) {
1481                 flags = hook->flags;
1482                 hook = hook->next;
1483         }
1484         verbose_observer = (flags & afb_hook_flag_global_vverbose) ? afb_hook_global_vverbose : NULL;
1485         pthread_rwlock_unlock(&rwlock);
1486 }
1487
1488 struct afb_hook_global *afb_hook_create_global(int flags, struct afb_hook_global_itf *itf, void *closure)
1489 {
1490         struct afb_hook_global *hook;
1491
1492         /* alloc the result */
1493         hook = calloc(1, sizeof *hook);
1494         if (hook == NULL)
1495                 return NULL;
1496
1497         /* initialise the rest */
1498         hook->refcount = 1;
1499         hook->flags = flags;
1500         hook->itf = itf ? itf : &hook_global_default_itf;
1501         hook->closure = closure;
1502
1503         /* record the hook */
1504         pthread_rwlock_wrlock(&rwlock);
1505         hook->next = list_of_global_hooks;
1506         list_of_global_hooks = hook;
1507         pthread_rwlock_unlock(&rwlock);
1508
1509         /* update hooking */
1510         update_global();
1511
1512         /* returns it */
1513         return hook;
1514 }
1515
1516 struct afb_hook_global *afb_hook_addref_global(struct afb_hook_global *hook)
1517 {
1518         pthread_rwlock_wrlock(&rwlock);
1519         hook->refcount++;
1520         pthread_rwlock_unlock(&rwlock);
1521         return hook;
1522 }
1523
1524 void afb_hook_unref_global(struct afb_hook_global *hook)
1525 {
1526         struct afb_hook_global **prv;
1527
1528         if (hook) {
1529                 pthread_rwlock_wrlock(&rwlock);
1530                 if (--hook->refcount)
1531                         hook = NULL;
1532                 else {
1533                         /* unlink */
1534                         prv = &list_of_global_hooks;
1535                         while (*prv && *prv != hook)
1536                                 prv = &(*prv)->next;
1537                         if(*prv)
1538                                 *prv = hook->next;
1539                 }
1540                 pthread_rwlock_unlock(&rwlock);
1541                 if (hook) {
1542                         /* free */
1543                         free(hook);
1544
1545                         /* update hooking */
1546                         update_global();
1547                 }
1548         }
1549 }
1550