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