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