f8b593dfdb2485a56477d9e60884785074b28608
[src/app-framework-binder.git] / src / afb-hook.c
1 /*
2  * Copyright (C) 2016-2019 "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 #if WITH_AFB_HOOK  /***********************************************************/
19
20 #define _GNU_SOURCE
21
22 #include <limits.h>
23 #include <stdio.h>
24 #include <stdarg.h>
25 #include <string.h>
26 #include <pthread.h>
27 #include <unistd.h>
28 #include <sys/uio.h>
29
30 #include <json-c/json.h>
31 #if !defined(JSON_C_TO_STRING_NOSLASHESCAPE)
32 #define JSON_C_TO_STRING_NOSLASHESCAPE 0
33 #endif
34
35 #include <afb/afb-req-x1.h>
36 #include <afb/afb-event-x2.h>
37
38 #include "afb-context.h"
39 #include "afb-hook.h"
40 #include "afb-session.h"
41 #include "afb-cred.h"
42 #include "afb-xreq.h"
43 #include "afb-export.h"
44 #include "afb-evt.h"
45 #include "afb-api.h"
46 #include "afb-msg-json.h"
47 #include "verbose.h"
48
49 #include <fnmatch.h>
50 #define MATCH(pattern,string)   (\
51                 pattern \
52                         ? !fnmatch((pattern),(string),FNM_CASEFOLD|FNM_EXTMATCH|FNM_PERIOD) \
53                         : afb_api_is_public(string))
54
55 #define MATCH_API(pattern,string)       MATCH(pattern,string)
56 #define MATCH_VERB(pattern,string)      MATCH(pattern,string)
57 #define MATCH_EVENT(pattern,string)     MATCH(pattern,string)
58 #define MATCH_SESSION(pattern,string)   MATCH(pattern,string)
59
60 /**
61  * Definition of a hook for xreq
62  */
63 struct afb_hook_xreq {
64         struct afb_hook_xreq *next; /**< next hook */
65         unsigned refcount; /**< reference count */
66         unsigned flags; /**< hook flags */
67         char *api; /**< api hooked or NULL for any */
68         char *verb; /**< verb hooked or NULL for any */
69         struct afb_session *session; /**< session hooked or NULL if any */
70         struct afb_hook_xreq_itf *itf; /**< interface of hook */
71         void *closure; /**< closure for callbacks */
72 };
73
74 /**
75  * Definition of a hook for export
76  */
77 struct afb_hook_api {
78         struct afb_hook_api *next; /**< next hook */
79         unsigned refcount; /**< reference count */
80         unsigned flags; /**< hook flags */
81         char *api; /**< api hooked or NULL for any */
82         struct afb_hook_api_itf *itf; /**< interface of hook */
83         void *closure; /**< closure for callbacks */
84 };
85
86 /**
87  * Definition of a hook for evt
88  */
89 struct afb_hook_evt {
90         struct afb_hook_evt *next; /**< next hook */
91         unsigned refcount; /**< reference count */
92         unsigned flags; /**< hook flags */
93         char *pattern; /**< event pattern name hooked or NULL for any */
94         struct afb_hook_evt_itf *itf; /**< interface of hook */
95         void *closure; /**< closure for callbacks */
96 };
97
98 /**
99  * Definition of a hook for session
100  */
101 struct afb_hook_session {
102         struct afb_hook_session *next; /**< next hook */
103         unsigned refcount; /**< reference count */
104         unsigned flags; /**< hook flags */
105         char *pattern; /**< event pattern name hooked or NULL for any */
106         struct afb_hook_session_itf *itf; /**< interface of hook */
107         void *closure; /**< closure for callbacks */
108 };
109
110 /**
111  * Definition of a hook for global
112  */
113 struct afb_hook_global {
114         struct afb_hook_global *next; /**< next hook */
115         unsigned refcount; /**< reference count */
116         unsigned flags; /**< hook flags */
117         struct afb_hook_global_itf *itf; /**< interface of hook */
118         void *closure; /**< closure for callbacks */
119 };
120
121 /* synchronization across threads */
122 static pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
123 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
124
125 /* list of hooks for xreq */
126 static struct afb_hook_xreq *list_of_xreq_hooks = NULL;
127
128 /* list of hooks for export */
129 static struct afb_hook_api *list_of_api_hooks = NULL;
130
131 /* list of hooks for evt */
132 static struct afb_hook_evt *list_of_evt_hooks = NULL;
133
134 /* list of hooks for session */
135 static struct afb_hook_session *list_of_session_hooks = NULL;
136
137 /* list of hooks for global */
138 static struct afb_hook_global *list_of_global_hooks = NULL;
139
140 /* hook id */
141 static unsigned next_hookid = 0;
142
143 /******************************************************************************
144  * section: hook id
145  *****************************************************************************/
146 static void init_hookid(struct afb_hookid *hookid)
147 {
148         hookid->id = __atomic_add_fetch(&next_hookid, 1, __ATOMIC_RELAXED);
149         clock_gettime(CLOCK_REALTIME, &hookid->time);
150 }
151
152 /******************************************************************************
153  * section: default callbacks for tracing requests
154  *****************************************************************************/
155
156 static char *_pbuf_(const char *fmt, va_list args, char **palloc, char *sbuf, size_t szsbuf, size_t *outlen)
157 {
158         int rc;
159         va_list cp;
160
161         *palloc = NULL;
162         va_copy(cp, args);
163         rc = vsnprintf(sbuf, szsbuf, fmt, args);
164         if ((size_t)rc >= szsbuf) {
165                 sbuf[szsbuf-1] = 0;
166                 sbuf[szsbuf-2] = sbuf[szsbuf-3] = sbuf[szsbuf-4] = '.';
167                 rc = vasprintf(palloc, fmt, cp);
168                 if (rc >= 0)
169                         sbuf = *palloc;
170         }
171         va_end(cp);
172         if (rc >= 0 && outlen)
173                 *outlen = (size_t)rc;
174         return sbuf;
175 }
176
177 static void _hook_(const char *fmt1, const char *fmt2, va_list arg2, ...)
178 {
179         static const char chars[] = "HOOK: [] \n";
180         char *mem1, *mem2, buf1[256], buf2[2000];
181         struct iovec iov[5];
182         va_list arg1;
183
184         /* "HOOK: [" */
185         iov[0].iov_base = (void*)&chars[0];
186         iov[0].iov_len = 7;
187
188         /* fmt1 ... */
189         va_start(arg1, arg2);
190         iov[1].iov_base = _pbuf_(fmt1, arg1, &mem1, buf1, sizeof buf1, &iov[1].iov_len);
191         va_end(arg1);
192
193         /* "] " */
194         iov[2].iov_base = (void*)&chars[7];
195         iov[2].iov_len = 2;
196
197         /* fmt2 arg2 */
198         iov[3].iov_base = _pbuf_(fmt2, arg2, &mem2, buf2, sizeof buf2, &iov[3].iov_len);
199
200         /* "\n" */
201         iov[4].iov_base = (void*)&chars[9];
202         iov[4].iov_len = 1;
203
204         (void)writev(2, iov, 5);
205
206         free(mem1);
207         free(mem2);
208 }
209
210 static void _hook_xreq_(const struct afb_xreq *xreq, const char *format, ...)
211 {
212         va_list ap;
213         va_start(ap, format);
214         _hook_("xreq-%06d:%s/%s", format, ap, xreq->hookindex, xreq->request.called_api, xreq->request.called_verb);
215         va_end(ap);
216 }
217
218 static void hook_xreq_begin_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq)
219 {
220         if (!xreq->cred)
221                 _hook_xreq_(xreq, "BEGIN");
222         else
223                 _hook_xreq_(xreq, "BEGIN uid=%d=%s gid=%d pid=%d label=%s id=%s",
224                         (int)xreq->cred->uid,
225                         xreq->cred->user,
226                         (int)xreq->cred->gid,
227                         (int)xreq->cred->pid,
228                         xreq->cred->label?:"(null)",
229                         xreq->cred->id?:"(null)"
230                 );
231 }
232
233 static void hook_xreq_end_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq)
234 {
235         _hook_xreq_(xreq, "END");
236 }
237
238 static void hook_xreq_json_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq, struct json_object *obj)
239 {
240         _hook_xreq_(xreq, "json() -> %s", json_object_to_json_string_ext(obj, JSON_C_TO_STRING_NOSLASHESCAPE));
241 }
242
243 static void hook_xreq_get_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq, const char *name, struct afb_arg arg)
244 {
245         _hook_xreq_(xreq, "get(%s) -> { name: %s, value: %s, path: %s }", name, arg.name, arg.value, arg.path);
246 }
247
248 static void hook_xreq_reply_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq, struct json_object *obj, const char *error, const char *info)
249 {
250         _hook_xreq_(xreq, "reply[%s](%s, %s)", error?:"success", json_object_to_json_string_ext(obj, JSON_C_TO_STRING_NOSLASHESCAPE), info);
251 }
252
253 static void hook_xreq_legacy_context_get_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq, void *value)
254 {
255         _hook_xreq_(xreq, "context_get() -> %p", value);
256 }
257
258 static void hook_xreq_legacy_context_set_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq, void *value, void (*free_value)(void*))
259 {
260         _hook_xreq_(xreq, "context_set(%p, %p)", value, free_value);
261 }
262
263 static void hook_xreq_addref_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq)
264 {
265         _hook_xreq_(xreq, "addref()");
266 }
267
268 static void hook_xreq_unref_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq)
269 {
270         _hook_xreq_(xreq, "unref()");
271 }
272
273 static void hook_xreq_session_close_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq)
274 {
275         _hook_xreq_(xreq, "session_close()");
276 }
277
278 static void hook_xreq_session_set_LOA_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq, unsigned level, int result)
279 {
280         _hook_xreq_(xreq, "session_set_LOA(%u) -> %d", level, result);
281 }
282
283 static void hook_xreq_subscribe_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq, struct afb_event_x2 *event_x2, int result)
284 {
285         _hook_xreq_(xreq, "subscribe(%s:%d) -> %d", afb_evt_event_x2_fullname(event_x2), afb_evt_event_x2_id(event_x2), result);
286 }
287
288 static void hook_xreq_unsubscribe_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq, struct afb_event_x2 *event_x2, int result)
289 {
290         _hook_xreq_(xreq, "unsubscribe(%s:%d) -> %d", afb_evt_event_x2_fullname(event_x2), afb_evt_event_x2_id(event_x2), result);
291 }
292
293 static void hook_xreq_subcall_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq, const char *api, const char *verb, struct json_object *args)
294 {
295         _hook_xreq_(xreq, "subcall(%s/%s, %s) ...", api, verb, json_object_to_json_string_ext(args, JSON_C_TO_STRING_NOSLASHESCAPE));
296 }
297
298 static void hook_xreq_subcall_result_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq, struct json_object *object, const char *error, const char *info)
299 {
300         _hook_xreq_(xreq, "    ...subcall... [%s] -> %s (%s)", error?:"success", json_object_to_json_string_ext(object, JSON_C_TO_STRING_NOSLASHESCAPE), info?:"");
301 }
302
303 static void hook_xreq_subcallsync_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq, const char *api, const char *verb, struct json_object *args)
304 {
305         _hook_xreq_(xreq, "subcallsync(%s/%s, %s) ...", api, verb, json_object_to_json_string_ext(args, JSON_C_TO_STRING_NOSLASHESCAPE));
306 }
307
308 static void hook_xreq_subcallsync_result_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq, int status, struct json_object *object, const char *error, const char *info)
309 {
310         _hook_xreq_(xreq, "    ...subcallsync... %d [%s] -> %s (%s)", status, error?:"success", json_object_to_json_string_ext(object, JSON_C_TO_STRING_NOSLASHESCAPE), info?:"");
311 }
312
313 static void hook_xreq_vverbose_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)
314 {
315         int len;
316         char *msg;
317         va_list ap;
318
319         va_copy(ap, args);
320         len = vasprintf(&msg, fmt, ap);
321         va_end(ap);
322
323         if (len < 0)
324                 _hook_xreq_(xreq, "vverbose(%d:%s, %s, %d, %s) -> %s ? ? ?", level, verbose_name_of_level(level), file, line, func, fmt);
325         else {
326                 _hook_xreq_(xreq, "vverbose(%d:%s, %s, %d, %s) -> %s", level, verbose_name_of_level(level), file, line, func, msg);
327                 free(msg);
328         }
329 }
330
331 static void hook_xreq_legacy_store_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq, struct afb_stored_req *sreq)
332 {
333         _hook_xreq_(xreq, "store() -> %p", sreq);
334 }
335
336 static void hook_xreq_legacy_unstore_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq)
337 {
338         _hook_xreq_(xreq, "unstore()");
339 }
340
341 static void hook_xreq_has_permission_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq, const char *permission, int result)
342 {
343         _hook_xreq_(xreq, "has_permission(%s) -> %d", permission, result);
344 }
345
346 static void hook_xreq_get_application_id_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq, char *result)
347 {
348         _hook_xreq_(xreq, "get_application_id() -> %s", result);
349 }
350
351 static void hook_xreq_context_make_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq, int replace, void *(*create_value)(void*), void (*free_value)(void*), void *create_closure, void *result)
352 {
353         _hook_xreq_(xreq, "context_make(replace=%s, %p, %p, %p) -> %p", replace?"yes":"no", create_value, free_value, create_closure, result);
354 }
355
356 static void hook_xreq_get_uid_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq, int result)
357 {
358         _hook_xreq_(xreq, "get_uid() -> %d", result);
359 }
360
361 static void hook_xreq_get_client_info_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq, struct json_object *result)
362 {
363         _hook_xreq_(xreq, "get_client_info() -> %s", json_object_to_json_string_ext(result, JSON_C_TO_STRING_NOSLASHESCAPE));
364 }
365
366 static struct afb_hook_xreq_itf hook_xreq_default_itf = {
367         .hook_xreq_begin = hook_xreq_begin_cb,
368         .hook_xreq_end = hook_xreq_end_cb,
369         .hook_xreq_json = hook_xreq_json_cb,
370         .hook_xreq_get = hook_xreq_get_cb,
371         .hook_xreq_reply = hook_xreq_reply_cb,
372         .hook_xreq_legacy_context_get = hook_xreq_legacy_context_get_cb,
373         .hook_xreq_legacy_context_set = hook_xreq_legacy_context_set_cb,
374         .hook_xreq_addref = hook_xreq_addref_cb,
375         .hook_xreq_unref = hook_xreq_unref_cb,
376         .hook_xreq_session_close = hook_xreq_session_close_cb,
377         .hook_xreq_session_set_LOA = hook_xreq_session_set_LOA_cb,
378         .hook_xreq_subscribe = hook_xreq_subscribe_cb,
379         .hook_xreq_unsubscribe = hook_xreq_unsubscribe_cb,
380         .hook_xreq_subcall = hook_xreq_subcall_cb,
381         .hook_xreq_subcall_result = hook_xreq_subcall_result_cb,
382         .hook_xreq_subcallsync = hook_xreq_subcallsync_cb,
383         .hook_xreq_subcallsync_result = hook_xreq_subcallsync_result_cb,
384         .hook_xreq_vverbose = hook_xreq_vverbose_cb,
385         .hook_xreq_legacy_store = hook_xreq_legacy_store_cb,
386         .hook_xreq_legacy_unstore = hook_xreq_legacy_unstore_cb,
387         .hook_xreq_has_permission = hook_xreq_has_permission_cb,
388         .hook_xreq_get_application_id = hook_xreq_get_application_id_cb,
389         .hook_xreq_context_make = hook_xreq_context_make_cb,
390         .hook_xreq_get_uid = hook_xreq_get_uid_cb,
391         .hook_xreq_get_client_info = hook_xreq_get_client_info_cb,
392 };
393
394 /******************************************************************************
395  * section: hooks for tracing requests
396  *****************************************************************************/
397
398 #define _HOOK_XREQ_2_(flag,func,...)   \
399         struct afb_hook_xreq *hook; \
400         struct afb_hookid hookid; \
401         pthread_rwlock_rdlock(&rwlock); \
402         init_hookid(&hookid); \
403         hook = list_of_xreq_hooks; \
404         while (hook) { \
405                 if (hook->refcount \
406                  && hook->itf->hook_xreq_##func \
407                  && (hook->flags & afb_hook_flag_req_##flag) != 0 \
408                  && (!hook->session || hook->session == xreq->context.session) \
409                  && MATCH_API(hook->api, xreq->request.called_api) \
410                  && MATCH_VERB(hook->verb, xreq->request.called_verb)) { \
411                         hook->itf->hook_xreq_##func(hook->closure, &hookid, __VA_ARGS__); \
412                 } \
413                 hook = hook->next; \
414         } \
415         pthread_rwlock_unlock(&rwlock);
416
417 #define _HOOK_XREQ_(what,...)   _HOOK_XREQ_2_(what,what,__VA_ARGS__)
418
419 void afb_hook_xreq_begin(const struct afb_xreq *xreq)
420 {
421         _HOOK_XREQ_(begin, xreq);
422 }
423
424 void afb_hook_xreq_end(const struct afb_xreq *xreq)
425 {
426         _HOOK_XREQ_(end, xreq);
427 }
428
429 struct json_object *afb_hook_xreq_json(const struct afb_xreq *xreq, struct json_object *obj)
430 {
431         _HOOK_XREQ_(json, xreq, obj);
432         return obj;
433 }
434
435 struct afb_arg afb_hook_xreq_get(const struct afb_xreq *xreq, const char *name, struct afb_arg arg)
436 {
437         _HOOK_XREQ_(get, xreq, name, arg);
438         return arg;
439 }
440
441 void afb_hook_xreq_reply(const struct afb_xreq *xreq, struct json_object *obj, const char *error, const char *info)
442 {
443         _HOOK_XREQ_(reply, xreq, obj, error, info);
444 }
445
446 void *afb_hook_xreq_legacy_context_get(const struct afb_xreq *xreq, void *value)
447 {
448         _HOOK_XREQ_(legacy_context_get, xreq, value);
449         return value;
450 }
451
452 void afb_hook_xreq_legacy_context_set(const struct afb_xreq *xreq, void *value, void (*free_value)(void*))
453 {
454         _HOOK_XREQ_(legacy_context_set, xreq, value, free_value);
455 }
456
457 void afb_hook_xreq_addref(const struct afb_xreq *xreq)
458 {
459         _HOOK_XREQ_(addref, xreq);
460 }
461
462 void afb_hook_xreq_unref(const struct afb_xreq *xreq)
463 {
464         _HOOK_XREQ_(unref, xreq);
465 }
466
467 void afb_hook_xreq_session_close(const struct afb_xreq *xreq)
468 {
469         _HOOK_XREQ_(session_close, xreq);
470 }
471
472 int afb_hook_xreq_session_set_LOA(const struct afb_xreq *xreq, unsigned level, int result)
473 {
474         _HOOK_XREQ_(session_set_LOA, xreq, level, result);
475         return result;
476 }
477
478 int afb_hook_xreq_subscribe(const struct afb_xreq *xreq, struct afb_event_x2 *event_x2, int result)
479 {
480         _HOOK_XREQ_(subscribe, xreq, event_x2, result);
481         return result;
482 }
483
484 int afb_hook_xreq_unsubscribe(const struct afb_xreq *xreq, struct afb_event_x2 *event_x2, int result)
485 {
486         _HOOK_XREQ_(unsubscribe, xreq, event_x2, result);
487         return result;
488 }
489
490 void afb_hook_xreq_subcall(const struct afb_xreq *xreq, const char *api, const char *verb, struct json_object *args, int flags)
491 {
492         _HOOK_XREQ_(subcall, xreq, api, verb, args);
493 }
494
495 void afb_hook_xreq_subcall_result(const struct afb_xreq *xreq, struct json_object *object, const char *error, const char *info)
496 {
497         _HOOK_XREQ_(subcall_result, xreq, object, error, info);
498 }
499
500 void afb_hook_xreq_subcallsync(const struct afb_xreq *xreq, const char *api, const char *verb, struct json_object *args, int flags)
501 {
502         _HOOK_XREQ_(subcallsync, xreq, api, verb, args);
503 }
504
505 int  afb_hook_xreq_subcallsync_result(const struct afb_xreq *xreq, int status, struct json_object *object, const char *error, const char *info)
506 {
507         _HOOK_XREQ_(subcallsync_result, xreq, status, object, error, info);
508         return status;
509 }
510
511 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)
512 {
513         _HOOK_XREQ_(vverbose, xreq, level, file ?: "?", line, func ?: "?", fmt, args);
514 }
515
516 void afb_hook_xreq_legacy_store(const struct afb_xreq *xreq, struct afb_stored_req *sreq)
517 {
518         _HOOK_XREQ_(legacy_store, xreq, sreq);
519 }
520
521 void afb_hook_xreq_legacy_unstore(const struct afb_xreq *xreq)
522 {
523         _HOOK_XREQ_(legacy_unstore, xreq);
524 }
525
526 int afb_hook_xreq_has_permission(const struct afb_xreq *xreq, const char *permission, int result)
527 {
528         _HOOK_XREQ_(has_permission, xreq, permission, result);
529         return result;
530 }
531
532 char *afb_hook_xreq_get_application_id(const struct afb_xreq *xreq, char *result)
533 {
534         _HOOK_XREQ_(get_application_id, xreq, result);
535         return result;
536 }
537
538 void *afb_hook_xreq_context_make(const struct afb_xreq *xreq, int replace, void *(*create_value)(void*), void (*free_value)(void*), void *create_closure, void *result)
539 {
540         _HOOK_XREQ_(context_make, xreq, replace, create_value, free_value, create_closure, result);
541         return result;
542 }
543
544 int afb_hook_xreq_get_uid(const struct afb_xreq *xreq, int result)
545 {
546         _HOOK_XREQ_(get_uid, xreq, result);
547         return result;
548 }
549
550 struct json_object *afb_hook_xreq_get_client_info(const struct afb_xreq *xreq, struct json_object *result)
551 {
552         _HOOK_XREQ_(get_client_info, xreq, result);
553         return result;
554 }
555
556 /******************************************************************************
557  * section: hooking xreqs
558  *****************************************************************************/
559
560 void afb_hook_init_xreq(struct afb_xreq *xreq)
561 {
562         static int reqindex = 0;
563
564         int f, flags;
565         int add, x;
566         struct afb_hook_xreq *hook;
567
568         /* scan hook list to get the expected flags */
569         flags = 0;
570         pthread_rwlock_rdlock(&rwlock);
571         hook = list_of_xreq_hooks;
572         while (hook) {
573                 f = hook->flags & afb_hook_flags_req_all;
574                 add = f != 0
575                    && (!hook->session || hook->session == xreq->context.session)
576                    && MATCH_API(hook->api, xreq->request.called_api)
577                    && MATCH_VERB(hook->verb, xreq->request.called_verb);
578                 if (add)
579                         flags |= f;
580                 hook = hook->next;
581         }
582         pthread_rwlock_unlock(&rwlock);
583
584         /* store the hooking data */
585         xreq->hookflags = flags;
586         if (flags) {
587                 do {
588                         x = __atomic_load_n(&reqindex, __ATOMIC_RELAXED);
589                         xreq->hookindex = (x + 1) % 1000000 ?: 1;
590                 } while (x != __atomic_exchange_n(&reqindex, xreq->hookindex, __ATOMIC_RELAXED));
591         }
592 }
593
594 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)
595 {
596         struct afb_hook_xreq *hook;
597
598         /* alloc the result */
599         hook = calloc(1, sizeof *hook);
600         if (hook == NULL)
601                 return NULL;
602
603         /* get a copy of the names */
604         hook->api = api ? strdup(api) : NULL;
605         hook->verb = verb ? strdup(verb) : NULL;
606         if ((api && !hook->api) || (verb && !hook->verb)) {
607                 free(hook->api);
608                 free(hook->verb);
609                 free(hook);
610                 return NULL;
611         }
612
613         /* initialise the rest */
614         hook->session = session;
615         if (session)
616                 afb_session_addref(session);
617         hook->refcount = 1;
618         hook->flags = flags;
619         hook->itf = itf ? itf : &hook_xreq_default_itf;
620         hook->closure = closure;
621
622         /* record the hook */
623         pthread_mutex_lock(&mutex);
624         hook->next = list_of_xreq_hooks;
625         list_of_xreq_hooks = hook;
626         pthread_mutex_unlock(&mutex);
627
628         /* returns it */
629         return hook;
630 }
631
632 struct afb_hook_xreq *afb_hook_addref_xreq(struct afb_hook_xreq *hook)
633 {
634         __atomic_add_fetch(&hook->refcount, 1, __ATOMIC_RELAXED);
635         return hook;
636 }
637
638 static void hook_clean_xreq()
639 {
640         struct afb_hook_xreq **prv, *hook, *head;
641
642         if (pthread_rwlock_trywrlock(&rwlock) == 0) {
643                 /* unlink under mutex */
644                 head = NULL;
645                 pthread_mutex_lock(&mutex);
646                 prv = &list_of_xreq_hooks;
647                 while ((hook = *prv)) {
648                         if (hook->refcount)
649                                 prv = &(*prv)->next;
650                         else {
651                                 *prv = hook->next;
652                                 hook->next = head;
653                                 head = hook;
654                         }
655                 }
656                 pthread_mutex_unlock(&mutex);
657                 pthread_rwlock_unlock(&rwlock);
658
659                 /* free found hooks */
660                 while((hook = head)) {
661                         head = hook->next;
662                         free(hook->api);
663                         free(hook->verb);
664                         if (hook->session)
665                                 afb_session_unref(hook->session);
666                         free(hook);
667                 }
668         }
669 }
670
671 void afb_hook_unref_xreq(struct afb_hook_xreq *hook)
672 {
673         if (hook && !__atomic_sub_fetch(&hook->refcount, 1, __ATOMIC_RELAXED))
674                 hook_clean_xreq();
675 }
676
677 /******************************************************************************
678  * section: default callbacks for tracing daemon interface
679  *****************************************************************************/
680
681 static void _hook_api_(const struct afb_export *export, const char *format, ...)
682 {
683         va_list ap;
684         va_start(ap, format);
685         _hook_("api-%s", format, ap, afb_export_apiname(export));
686         va_end(ap);
687 }
688
689 static void hook_api_event_broadcast_before_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export, const char *name, struct json_object *object)
690 {
691         _hook_api_(export, "event_broadcast.before(%s, %s)....", name, json_object_to_json_string_ext(object, JSON_C_TO_STRING_NOSLASHESCAPE));
692 }
693
694 static void hook_api_event_broadcast_after_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export, const char *name, struct json_object *object, int result)
695 {
696         _hook_api_(export, "event_broadcast.after(%s, %s) -> %d", name, json_object_to_json_string_ext(object, JSON_C_TO_STRING_NOSLASHESCAPE), result);
697 }
698
699 static void hook_api_get_event_loop_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export, struct sd_event *result)
700 {
701         _hook_api_(export, "get_event_loop() -> %p", result);
702 }
703
704 static void hook_api_get_user_bus_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export, struct sd_bus *result)
705 {
706         _hook_api_(export, "get_user_bus() -> %p", result);
707 }
708
709 static void hook_api_get_system_bus_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export, struct sd_bus *result)
710 {
711         _hook_api_(export, "get_system_bus() -> %p", result);
712 }
713
714 static void hook_api_vverbose_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export, int level, const char *file, int line, const char *function, const char *fmt, va_list args)
715 {
716         int len;
717         char *msg;
718         va_list ap;
719
720         va_copy(ap, args);
721         len = vasprintf(&msg, fmt, ap);
722         va_end(ap);
723
724         if (len < 0)
725                 _hook_api_(export, "vverbose(%d:%s, %s, %d, %s) -> %s ? ? ?", level, verbose_name_of_level(level), file, line, function, fmt);
726         else {
727                 _hook_api_(export, "vverbose(%d:%s, %s, %d, %s) -> %s", level, verbose_name_of_level(level), file, line, function, msg);
728                 free(msg);
729         }
730 }
731
732 static void hook_api_event_make_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export, const char *name, struct afb_event_x2 *result)
733 {
734         _hook_api_(export, "event_make(%s) -> %s:%d", name, afb_evt_event_x2_fullname(result), afb_evt_event_x2_id(result));
735 }
736
737 static void hook_api_rootdir_get_fd_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export, int result)
738 {
739         char path[PATH_MAX], proc[100];
740         ssize_t s;
741
742         if (result < 0)
743                 _hook_api_(export, "rootdir_get_fd() -> %d, %m", result);
744         else {
745                 snprintf(proc, sizeof proc, "/proc/self/fd/%d", result);
746                 s = readlink(proc, path, sizeof path);
747                 path[s < 0 ? 0 : s >= sizeof path ? sizeof path - 1 : s] = 0;
748                 _hook_api_(export, "rootdir_get_fd() -> %d = %s", result, path);
749         }
750 }
751
752 static void hook_api_rootdir_open_locale_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export, const char *filename, int flags, const char *locale, int result)
753 {
754         char path[PATH_MAX], proc[100];
755         ssize_t s;
756
757         if (!locale)
758                 locale = "(null)";
759         if (result < 0)
760                 _hook_api_(export, "rootdir_open_locale(%s, %d, %s) -> %d, %m", filename, flags, locale, result);
761         else {
762                 snprintf(proc, sizeof proc, "/proc/self/fd/%d", result);
763                 s = readlink(proc, path, sizeof path);
764                 path[s < 0 ? 0 : s >= sizeof path ? sizeof path - 1 : s] = 0;
765                 _hook_api_(export, "rootdir_open_locale(%s, %d, %s) -> %d = %s", filename, flags, locale, result, path);
766         }
767 }
768
769 static void hook_api_queue_job_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export, void (*callback)(int signum, void *arg), void *argument, void *group, int timeout, int result)
770 {
771         _hook_api_(export, "queue_job(%p, %p, %p, %d) -> %d", callback, argument, group, timeout, result);
772 }
773
774 static void hook_api_unstore_req_cb(void *closure, const struct afb_hookid *hookid,  const struct afb_export *export, struct afb_stored_req *sreq)
775 {
776         _hook_api_(export, "unstore_req(%p)", sreq);
777 }
778
779 static void hook_api_require_api_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export, const char *name, int initialized)
780 {
781         _hook_api_(export, "require_api(%s, %d)...", name, initialized);
782 }
783
784 static void hook_api_require_api_result_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export, const char *name, int initialized, int result)
785 {
786         _hook_api_(export, "...require_api(%s, %d) -> %d", name, initialized, result);
787 }
788
789 static void hook_api_add_alias_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export, const char *api, const char *alias, int result)
790 {
791         _hook_api_(export, "add_alias(%s -> %s) -> %d", api, alias?:"<null>", result);
792 }
793
794 static void hook_api_start_before_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export)
795 {
796         _hook_api_(export, "start.before");
797 }
798
799 static void hook_api_start_after_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export, int status)
800 {
801         _hook_api_(export, "start.after -> %d", status);
802 }
803
804 static void hook_api_on_event_before_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export, const char *event, int event_x2, struct json_object *object)
805 {
806         _hook_api_(export, "on_event.before(%s, %d, %s)", event, event_x2, json_object_to_json_string_ext(object, JSON_C_TO_STRING_NOSLASHESCAPE));
807 }
808
809 static void hook_api_on_event_after_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export, const char *event, int event_x2, struct json_object *object)
810 {
811         _hook_api_(export, "on_event.after(%s, %d, %s)", event, event_x2, json_object_to_json_string_ext(object, JSON_C_TO_STRING_NOSLASHESCAPE));
812 }
813
814 static void hook_api_call_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export, const char *api, const char *verb, struct json_object *args)
815 {
816         _hook_api_(export, "call(%s/%s, %s) ...", api, verb, json_object_to_json_string_ext(args, JSON_C_TO_STRING_NOSLASHESCAPE));
817 }
818
819 static void hook_api_call_result_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export, struct json_object *object, const char *error, const char *info)
820 {
821         _hook_api_(export, "    ...call... [%s] -> %s (%s)", error?:"success", json_object_to_json_string_ext(object, JSON_C_TO_STRING_NOSLASHESCAPE), info?:"");
822 }
823
824 static void hook_api_callsync_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export, const char *api, const char *verb, struct json_object *args)
825 {
826         _hook_api_(export, "callsync(%s/%s, %s) ...", api, verb, json_object_to_json_string_ext(args, JSON_C_TO_STRING_NOSLASHESCAPE));
827 }
828
829 static void hook_api_callsync_result_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export, int status, struct json_object *object, const char *error, const char *info)
830 {
831         _hook_api_(export, "    ...callsync... %d [%s] -> %s (%s)", status, error?:"success", json_object_to_json_string_ext(object, JSON_C_TO_STRING_NOSLASHESCAPE), info?:"");
832 }
833
834 static void hook_api_new_api_before_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export, const char *api, const char *info, int noconcurrency)
835 {
836         _hook_api_(export, "new_api.before %s (%s)%s ...", api, info?:"", noconcurrency?" no-concurrency" : "");
837 }
838
839 static void hook_api_new_api_after_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export, int result, const char *api)
840 {
841         _hook_api_(export, "... new_api.after %s -> %s (%d)", api, result >= 0 ? "OK" : "ERROR", result);
842 }
843
844 static void hook_api_api_set_verbs_v2_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export, int result, const struct afb_verb_v2 *verbs)
845 {
846         _hook_api_(export, "set_verbs_v2 -> %s (%d)", result >= 0 ? "OK" : "ERROR", result);
847 }
848
849 static void hook_api_api_set_verbs_v3_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export, int result, const struct afb_verb_v3 *verbs)
850 {
851         _hook_api_(export, "set_verbs_v3 -> %s (%d)", result >= 0 ? "OK" : "ERROR", result);
852 }
853
854 static void hook_api_api_add_verb_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export, int result, const char *verb, const char *info, int glob)
855 {
856         _hook_api_(export, "add_verb(%s%s [%s]) -> %s (%d)", verb, glob?" (GLOB)":"", info?:"", result >= 0 ? "OK" : "ERROR", result);
857 }
858
859 static void hook_api_api_del_verb_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export, int result, const char *verb)
860 {
861         _hook_api_(export, "del_verb(%s) -> %s (%d)", verb, result >= 0 ? "OK" : "ERROR", result);
862 }
863
864 static void hook_api_api_set_on_event_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export, int result)
865 {
866         _hook_api_(export, "set_on_event -> %s (%d)", result >= 0 ? "OK" : "ERROR", result);
867 }
868
869 static void hook_api_api_set_on_init_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export, int result)
870 {
871         _hook_api_(export, "set_on_init -> %s (%d)", result >= 0 ? "OK" : "ERROR", result);
872 }
873
874 static void hook_api_api_seal_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export)
875 {
876         _hook_api_(export, "seal");
877 }
878
879 static void hook_api_event_handler_add_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export, int result, const char *pattern)
880 {
881         _hook_api_(export, "event_handler_add(%s) -> %s (%d)", pattern, result >= 0 ? "OK" : "ERROR", result);
882 }
883
884 static void hook_api_event_handler_del_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export, int result, const char *pattern)
885 {
886         _hook_api_(export, "event_handler_del(%s) -> %s (%d)", pattern, result >= 0 ? "OK" : "ERROR", result);
887 }
888
889 static void hook_api_class_provide_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export, int result, const char *name)
890 {
891         _hook_api_(export, "class_provide(%s) -> %s (%d)", name, result >= 0 ? "OK" : "ERROR", result);
892 }
893
894 static void hook_api_class_require_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export, int result, const char *name)
895 {
896         _hook_api_(export, "class_require(%s) -> %s (%d)", name, result >= 0 ? "OK" : "ERROR", result);
897 }
898
899 static void hook_api_delete_api_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export, int result)
900 {
901         _hook_api_(export, "delete_api -> %s (%d)", result >= 0 ? "OK" : "ERROR", result);
902 }
903
904 static void hook_api_on_event_handler_before_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export, const char *event, int event_x2, struct json_object *object, const char *pattern)
905 {
906         _hook_api_(export, "on_event_handler[%s].before(%s, %d, %s)", pattern, event, event_x2, json_object_to_json_string_ext(object, JSON_C_TO_STRING_NOSLASHESCAPE));
907 }
908
909 static void hook_api_on_event_handler_after_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export, const char *event, int event_x2, struct json_object *object, const char *pattern)
910 {
911         _hook_api_(export, "on_event_handler[%s].after(%s, %d, %s)", pattern, event, event_x2, json_object_to_json_string_ext(object, JSON_C_TO_STRING_NOSLASHESCAPE));
912 }
913
914 static void hook_api_settings_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export, struct json_object *object)
915 {
916         _hook_api_(export, "settings -> %s", json_object_to_json_string_ext(object, JSON_C_TO_STRING_NOSLASHESCAPE));
917 }
918
919 static struct afb_hook_api_itf hook_api_default_itf = {
920         .hook_api_event_broadcast_before = hook_api_event_broadcast_before_cb,
921         .hook_api_event_broadcast_after = hook_api_event_broadcast_after_cb,
922         .hook_api_get_event_loop = hook_api_get_event_loop_cb,
923         .hook_api_get_user_bus = hook_api_get_user_bus_cb,
924         .hook_api_get_system_bus = hook_api_get_system_bus_cb,
925         .hook_api_vverbose = hook_api_vverbose_cb,
926         .hook_api_event_make = hook_api_event_make_cb,
927         .hook_api_rootdir_get_fd = hook_api_rootdir_get_fd_cb,
928         .hook_api_rootdir_open_locale = hook_api_rootdir_open_locale_cb,
929         .hook_api_queue_job = hook_api_queue_job_cb,
930         .hook_api_legacy_unstore_req = hook_api_unstore_req_cb,
931         .hook_api_require_api = hook_api_require_api_cb,
932         .hook_api_require_api_result = hook_api_require_api_result_cb,
933         .hook_api_add_alias = hook_api_add_alias_cb,
934         .hook_api_start_before = hook_api_start_before_cb,
935         .hook_api_start_after = hook_api_start_after_cb,
936         .hook_api_on_event_before = hook_api_on_event_before_cb,
937         .hook_api_on_event_after = hook_api_on_event_after_cb,
938         .hook_api_call = hook_api_call_cb,
939         .hook_api_call_result = hook_api_call_result_cb,
940         .hook_api_callsync = hook_api_callsync_cb,
941         .hook_api_callsync_result = hook_api_callsync_result_cb,
942         .hook_api_new_api_before = hook_api_new_api_before_cb,
943         .hook_api_new_api_after = hook_api_new_api_after_cb,
944         .hook_api_api_set_verbs_v2 = hook_api_api_set_verbs_v2_cb,
945         .hook_api_api_set_verbs_v3 = hook_api_api_set_verbs_v3_cb,
946         .hook_api_api_add_verb = hook_api_api_add_verb_cb,
947         .hook_api_api_del_verb = hook_api_api_del_verb_cb,
948         .hook_api_api_set_on_event = hook_api_api_set_on_event_cb,
949         .hook_api_api_set_on_init = hook_api_api_set_on_init_cb,
950         .hook_api_api_seal = hook_api_api_seal_cb,
951         .hook_api_event_handler_add = hook_api_event_handler_add_cb,
952         .hook_api_event_handler_del = hook_api_event_handler_del_cb,
953         .hook_api_class_provide = hook_api_class_provide_cb,
954         .hook_api_class_require = hook_api_class_require_cb,
955         .hook_api_delete_api = hook_api_delete_api_cb,
956         .hook_api_on_event_handler_before = hook_api_on_event_handler_before_cb,
957         .hook_api_on_event_handler_after = hook_api_on_event_handler_after_cb,
958         .hook_api_settings = hook_api_settings_cb,
959 };
960
961 /******************************************************************************
962  * section: hooks for tracing daemon interface (export)
963  *****************************************************************************/
964
965 #define _HOOK_API_2_(flag,func,...)   \
966         struct afb_hook_api *hook; \
967         struct afb_hookid hookid; \
968         const char *apiname = afb_export_apiname(export); \
969         pthread_rwlock_rdlock(&rwlock); \
970         init_hookid(&hookid); \
971         hook = list_of_api_hooks; \
972         while (hook) { \
973                 if (hook->refcount \
974                  && hook->itf->hook_api_##func \
975                  && (hook->flags & afb_hook_flag_api_##flag) != 0 \
976                  && MATCH_API(hook->api, apiname)) { \
977                         hook->itf->hook_api_##func(hook->closure, &hookid, __VA_ARGS__); \
978                 } \
979                 hook = hook->next; \
980         } \
981         pthread_rwlock_unlock(&rwlock);
982
983 #define _HOOK_API_(what,...)   _HOOK_API_2_(what,what,__VA_ARGS__)
984
985 void afb_hook_api_event_broadcast_before(const struct afb_export *export, const char *name, struct json_object *object)
986 {
987         _HOOK_API_2_(event_broadcast, event_broadcast_before, export, name, object);
988 }
989
990 int afb_hook_api_event_broadcast_after(const struct afb_export *export, const char *name, struct json_object *object, int result)
991 {
992         _HOOK_API_2_(event_broadcast, event_broadcast_after, export, name, object, result);
993         return result;
994 }
995
996 struct sd_event *afb_hook_api_get_event_loop(const struct afb_export *export, struct sd_event *result)
997 {
998         _HOOK_API_(get_event_loop, export, result);
999         return result;
1000 }
1001
1002 struct sd_bus *afb_hook_api_get_user_bus(const struct afb_export *export, struct sd_bus *result)
1003 {
1004         _HOOK_API_(get_user_bus, export, result);
1005         return result;
1006 }
1007
1008 struct sd_bus *afb_hook_api_get_system_bus(const struct afb_export *export, struct sd_bus *result)
1009 {
1010         _HOOK_API_(get_system_bus, export, result);
1011         return result;
1012 }
1013
1014 void afb_hook_api_vverbose(const struct afb_export *export, int level, const char *file, int line, const char *function, const char *fmt, va_list args)
1015 {
1016         _HOOK_API_(vverbose, export, level, file, line, function, fmt, args);
1017 }
1018
1019 struct afb_event_x2 *afb_hook_api_event_make(const struct afb_export *export, const char *name, struct afb_event_x2 *result)
1020 {
1021         _HOOK_API_(event_make, export, name, result);
1022         return result;
1023 }
1024
1025 int afb_hook_api_rootdir_get_fd(const struct afb_export *export, int result)
1026 {
1027         _HOOK_API_(rootdir_get_fd, export, result);
1028         return result;
1029 }
1030
1031 int afb_hook_api_rootdir_open_locale(const struct afb_export *export, const char *filename, int flags, const char *locale, int result)
1032 {
1033         _HOOK_API_(rootdir_open_locale, export, filename, flags, locale, result);
1034         return result;
1035 }
1036
1037 int afb_hook_api_queue_job(const struct afb_export *export, void (*callback)(int signum, void *arg), void *argument, void *group, int timeout, int result)
1038 {
1039         _HOOK_API_(queue_job, export, callback, argument, group, timeout, result);
1040         return result;
1041 }
1042
1043 void afb_hook_api_legacy_unstore_req(const struct afb_export *export, struct afb_stored_req *sreq)
1044 {
1045         _HOOK_API_(legacy_unstore_req, export, sreq);
1046 }
1047
1048 void afb_hook_api_require_api(const struct afb_export *export, const char *name, int initialized)
1049 {
1050         _HOOK_API_(require_api, export, name, initialized);
1051 }
1052
1053 int afb_hook_api_require_api_result(const struct afb_export *export, const char *name, int initialized, int result)
1054 {
1055         _HOOK_API_2_(require_api, require_api_result, export, name, initialized, result);
1056         return result;
1057 }
1058
1059 int afb_hook_api_add_alias(const struct afb_export *export, const char *api, const char *alias, int result)
1060 {
1061         _HOOK_API_(add_alias, export, api, alias, result);
1062         return result;
1063 }
1064
1065 void afb_hook_api_start_before(const struct afb_export *export)
1066 {
1067         _HOOK_API_2_(start, start_before, export);
1068 }
1069
1070 int afb_hook_api_start_after(const struct afb_export *export, int status)
1071 {
1072         _HOOK_API_2_(start, start_after, export, status);
1073         return status;
1074 }
1075
1076 void afb_hook_api_on_event_before(const struct afb_export *export, const char *event, int event_x2, struct json_object *object)
1077 {
1078         _HOOK_API_2_(on_event, on_event_before, export, event, event_x2, object);
1079 }
1080
1081 void afb_hook_api_on_event_after(const struct afb_export *export, const char *event, int event_x2, struct json_object *object)
1082 {
1083         _HOOK_API_2_(on_event, on_event_after, export, event, event_x2, object);
1084 }
1085
1086 void afb_hook_api_call(const struct afb_export *export, const char *api, const char *verb, struct json_object *args)
1087 {
1088         _HOOK_API_(call, export, api, verb, args);
1089 }
1090
1091 void afb_hook_api_call_result(const struct afb_export *export, struct json_object *object, const char*error, const char *info)
1092 {
1093         _HOOK_API_2_(call, call_result, export, object, error, info);
1094
1095 }
1096
1097 void afb_hook_api_callsync(const struct afb_export *export, const char *api, const char *verb, struct json_object *args)
1098 {
1099         _HOOK_API_(callsync, export, api, verb, args);
1100 }
1101
1102 int afb_hook_api_callsync_result(const struct afb_export *export, int status, struct json_object *object, const char *error, const char *info)
1103 {
1104         _HOOK_API_2_(callsync, callsync_result, export, status, object, error, info);
1105         return status;
1106 }
1107
1108 void afb_hook_api_new_api_before(const struct afb_export *export, const char *api, const char *info, int noconcurrency)
1109 {
1110         _HOOK_API_2_(new_api, new_api_before, export, api, info, noconcurrency);
1111 }
1112
1113 int afb_hook_api_new_api_after(const struct afb_export *export, int result, const char *api)
1114 {
1115         _HOOK_API_2_(new_api, new_api_after, export, result, api);
1116         return result;
1117 }
1118
1119 int afb_hook_api_api_set_verbs_v2(const struct afb_export *export, int result, const struct afb_verb_v2 *verbs)
1120 {
1121         _HOOK_API_2_(api_set_verbs, api_set_verbs_v2, export, result, verbs);
1122         return result;
1123 }
1124
1125 int afb_hook_api_api_set_verbs_v3(const struct afb_export *export, int result, const struct afb_verb_v3 *verbs)
1126 {
1127         _HOOK_API_2_(api_set_verbs, api_set_verbs_v3, export, result, verbs);
1128         return result;
1129 }
1130
1131 int afb_hook_api_api_add_verb(const struct afb_export *export, int result, const char *verb, const char *info, int glob)
1132 {
1133         _HOOK_API_(api_add_verb, export, result, verb, info, glob);
1134         return result;
1135 }
1136
1137 int afb_hook_api_api_del_verb(const struct afb_export *export, int result, const char *verb)
1138 {
1139         _HOOK_API_(api_del_verb, export, result, verb);
1140         return result;
1141 }
1142
1143 int afb_hook_api_api_set_on_event(const struct afb_export *export, int result)
1144 {
1145         _HOOK_API_(api_set_on_event, export, result);
1146         return result;
1147 }
1148
1149 int afb_hook_api_api_set_on_init(const struct afb_export *export, int result)
1150 {
1151         _HOOK_API_(api_set_on_init, export, result);
1152         return result;
1153 }
1154
1155 void afb_hook_api_api_seal(const struct afb_export *export)
1156 {
1157         _HOOK_API_(api_seal, export);
1158 }
1159
1160 int afb_hook_api_event_handler_add(const struct afb_export *export, int result, const char *pattern)
1161 {
1162         _HOOK_API_(event_handler_add, export, result, pattern);
1163         return result;
1164 }
1165 int afb_hook_api_event_handler_del(const struct afb_export *export, int result, const char *pattern)
1166 {
1167         _HOOK_API_(event_handler_del, export, result, pattern);
1168         return result;
1169 }
1170 int afb_hook_api_class_provide(const struct afb_export *export, int result, const char *name)
1171 {
1172         _HOOK_API_(class_provide, export, result, name);
1173         return result;
1174 }
1175 int afb_hook_api_class_require(const struct afb_export *export, int result, const char *name)
1176 {
1177         _HOOK_API_(class_require, export, result, name);
1178         return result;
1179 }
1180
1181 int afb_hook_api_delete_api(const struct afb_export *export, int result)
1182 {
1183         _HOOK_API_(delete_api, export, result);
1184         return result;
1185 }
1186
1187 void afb_hook_api_on_event_handler_before(const struct afb_export *export, const char *event, int event_x2, struct json_object *object, const char *pattern)
1188 {
1189         _HOOK_API_2_(on_event_handler, on_event_handler_before, export, event, event_x2, object, pattern);
1190 }
1191
1192 void afb_hook_api_on_event_handler_after(const struct afb_export *export, const char *event, int event_x2, struct json_object *object, const char *pattern)
1193 {
1194         _HOOK_API_2_(on_event_handler, on_event_handler_after, export, event, event_x2, object, pattern);
1195 }
1196
1197 struct json_object *afb_hook_api_settings(const struct afb_export *export, struct json_object *object)
1198 {
1199         _HOOK_API_(settings, export, object);
1200         return object;
1201 }
1202
1203 /******************************************************************************
1204  * section: hooking export
1205  *****************************************************************************/
1206
1207 int afb_hook_flags_api(const char *api)
1208 {
1209         int flags;
1210         struct afb_hook_api *hook;
1211
1212         flags = 0;
1213         pthread_rwlock_rdlock(&rwlock);
1214         hook = list_of_api_hooks;
1215         while (hook) {
1216                 if (!api || MATCH_API(hook->api, api))
1217                         flags |= hook->flags;
1218                 hook = hook->next;
1219         }
1220         pthread_rwlock_unlock(&rwlock);
1221         return flags;
1222 }
1223
1224 struct afb_hook_api *afb_hook_create_api(const char *api, int flags, struct afb_hook_api_itf *itf, void *closure)
1225 {
1226         struct afb_hook_api *hook;
1227
1228         /* alloc the result */
1229         hook = calloc(1, sizeof *hook);
1230         if (hook == NULL)
1231                 return NULL;
1232
1233         /* get a copy of the names */
1234         hook->api = api ? strdup(api) : NULL;
1235         if (api && !hook->api) {
1236                 free(hook);
1237                 return NULL;
1238         }
1239
1240         /* initialise the rest */
1241         hook->refcount = 1;
1242         hook->flags = flags;
1243         hook->itf = itf ? itf : &hook_api_default_itf;
1244         hook->closure = closure;
1245
1246         /* record the hook */
1247         pthread_mutex_lock(&mutex);
1248         hook->next = list_of_api_hooks;
1249         list_of_api_hooks = hook;
1250         pthread_mutex_unlock(&mutex);
1251
1252         /* returns it */
1253         return hook;
1254 }
1255
1256 struct afb_hook_api *afb_hook_addref_api(struct afb_hook_api *hook)
1257 {
1258         __atomic_add_fetch(&hook->refcount, 1, __ATOMIC_RELAXED);
1259         return hook;
1260 }
1261
1262 static void hook_clean_api()
1263 {
1264         struct afb_hook_api **prv, *hook, *head;
1265
1266         if (pthread_rwlock_trywrlock(&rwlock) == 0) {
1267                 /* unlink under mutex */
1268                 head = NULL;
1269                 pthread_mutex_lock(&mutex);
1270                 prv = &list_of_api_hooks;
1271                 while ((hook = *prv)) {
1272                         if (hook->refcount)
1273                                 prv = &(*prv)->next;
1274                         else {
1275                                 *prv = hook->next;
1276                                 hook->next = head;
1277                                 head = hook;
1278                         }
1279                 }
1280                 pthread_mutex_unlock(&mutex);
1281                 pthread_rwlock_unlock(&rwlock);
1282
1283                 /* free found hooks */
1284                 while((hook = head)) {
1285                         head = hook->next;
1286                         free(hook->api);
1287                         free(hook);
1288                 }
1289         }
1290 }
1291
1292 void afb_hook_unref_api(struct afb_hook_api *hook)
1293 {
1294         if (hook && !__atomic_sub_fetch(&hook->refcount, 1, __ATOMIC_RELAXED))
1295                 hook_clean_api();
1296 }
1297
1298 /******************************************************************************
1299  * section: default callbacks for tracing service interface (evt)
1300  *****************************************************************************/
1301
1302 static void _hook_evt_(const char *evt, int id, const char *format, ...)
1303 {
1304         va_list ap;
1305         va_start(ap, format);
1306         _hook_("evt-%s:%d", format, ap, evt, id);
1307         va_end(ap);
1308 }
1309
1310 static void hook_evt_create_cb(void *closure, const struct afb_hookid *hookid, const char *evt, int id)
1311 {
1312         _hook_evt_(evt, id, "create");
1313 }
1314
1315 static void hook_evt_push_before_cb(void *closure, const struct afb_hookid *hookid, const char *evt, int id, struct json_object *obj)
1316 {
1317         _hook_evt_(evt, id, "push.before(%s)", json_object_to_json_string_ext(obj, JSON_C_TO_STRING_NOSLASHESCAPE));
1318 }
1319
1320
1321 static void hook_evt_push_after_cb(void *closure, const struct afb_hookid *hookid, const char *evt, int id, struct json_object *obj, int result)
1322 {
1323         _hook_evt_(evt, id, "push.after(%s) -> %d", json_object_to_json_string_ext(obj, JSON_C_TO_STRING_NOSLASHESCAPE), result);
1324 }
1325
1326 static void hook_evt_broadcast_before_cb(void *closure, const struct afb_hookid *hookid, const char *evt, int id, struct json_object *obj)
1327 {
1328         _hook_evt_(evt, id, "broadcast.before(%s)", json_object_to_json_string_ext(obj, JSON_C_TO_STRING_NOSLASHESCAPE));
1329 }
1330
1331 static void hook_evt_broadcast_after_cb(void *closure, const struct afb_hookid *hookid, const char *evt, int id, struct json_object *obj, int result)
1332 {
1333         _hook_evt_(evt, id, "broadcast.after(%s) -> %d", json_object_to_json_string_ext(obj, JSON_C_TO_STRING_NOSLASHESCAPE), result);
1334 }
1335
1336 static void hook_evt_name_cb(void *closure, const struct afb_hookid *hookid, const char *evt, int id, const char *result)
1337 {
1338         _hook_evt_(evt, id, "name -> %s", result);
1339 }
1340
1341 static void hook_evt_addref_cb(void *closure, const struct afb_hookid *hookid, const char *evt, int id)
1342 {
1343         _hook_evt_(evt, id, "addref");
1344 }
1345
1346 static void hook_evt_unref_cb(void *closure, const struct afb_hookid *hookid, const char *evt, int id)
1347 {
1348         _hook_evt_(evt, id, "unref");
1349 }
1350
1351 static struct afb_hook_evt_itf hook_evt_default_itf = {
1352         .hook_evt_create = hook_evt_create_cb,
1353         .hook_evt_push_before = hook_evt_push_before_cb,
1354         .hook_evt_push_after = hook_evt_push_after_cb,
1355         .hook_evt_broadcast_before = hook_evt_broadcast_before_cb,
1356         .hook_evt_broadcast_after = hook_evt_broadcast_after_cb,
1357         .hook_evt_name = hook_evt_name_cb,
1358         .hook_evt_addref = hook_evt_addref_cb,
1359         .hook_evt_unref = hook_evt_unref_cb
1360 };
1361
1362 /******************************************************************************
1363  * section: hooks for tracing events interface (evt)
1364  *****************************************************************************/
1365
1366 #define _HOOK_EVT_(what,...)   \
1367         struct afb_hook_evt *hook; \
1368         struct afb_hookid hookid; \
1369         pthread_rwlock_rdlock(&rwlock); \
1370         init_hookid(&hookid); \
1371         hook = list_of_evt_hooks; \
1372         while (hook) { \
1373                 if (hook->refcount \
1374                  && hook->itf->hook_evt_##what \
1375                  && (hook->flags & afb_hook_flag_evt_##what) != 0 \
1376                  && MATCH_EVENT(hook->pattern, evt)) { \
1377                         hook->itf->hook_evt_##what(hook->closure, &hookid, __VA_ARGS__); \
1378                 } \
1379                 hook = hook->next; \
1380         } \
1381         pthread_rwlock_unlock(&rwlock);
1382
1383 void afb_hook_evt_create(const char *evt, int id)
1384 {
1385         _HOOK_EVT_(create, evt, id);
1386 }
1387
1388 void afb_hook_evt_push_before(const char *evt, int id, struct json_object *obj)
1389 {
1390         _HOOK_EVT_(push_before, evt, id, obj);
1391 }
1392
1393 int afb_hook_evt_push_after(const char *evt, int id, struct json_object *obj, int result)
1394 {
1395         _HOOK_EVT_(push_after, evt, id, obj, result);
1396         return result;
1397 }
1398
1399 void afb_hook_evt_broadcast_before(const char *evt, int id, struct json_object *obj)
1400 {
1401         _HOOK_EVT_(broadcast_before, evt, id, obj);
1402 }
1403
1404 int afb_hook_evt_broadcast_after(const char *evt, int id, struct json_object *obj, int result)
1405 {
1406         _HOOK_EVT_(broadcast_after, evt, id, obj, result);
1407         return result;
1408 }
1409
1410 void afb_hook_evt_name(const char *evt, int id, const char *result)
1411 {
1412         _HOOK_EVT_(name, evt, id, result);
1413 }
1414
1415 void afb_hook_evt_addref(const char *evt, int id)
1416 {
1417         _HOOK_EVT_(addref, evt, id);
1418 }
1419
1420 void afb_hook_evt_unref(const char *evt, int id)
1421 {
1422         _HOOK_EVT_(unref, evt, id);
1423 }
1424
1425 /******************************************************************************
1426  * section: hooking services (evt)
1427  *****************************************************************************/
1428
1429 int afb_hook_flags_evt(const char *name)
1430 {
1431         int flags;
1432         struct afb_hook_evt *hook;
1433
1434         flags = 0;
1435         pthread_rwlock_rdlock(&rwlock);
1436         hook = list_of_evt_hooks;
1437         while (hook) {
1438                 if (!name || MATCH_EVENT(hook->pattern, name))
1439                         flags |= hook->flags;
1440                 hook = hook->next;
1441         }
1442         pthread_rwlock_unlock(&rwlock);
1443         return flags;
1444 }
1445
1446 struct afb_hook_evt *afb_hook_create_evt(const char *pattern, int flags, struct afb_hook_evt_itf *itf, void *closure)
1447 {
1448         struct afb_hook_evt *hook;
1449
1450         /* alloc the result */
1451         hook = calloc(1, sizeof *hook);
1452         if (hook == NULL)
1453                 return NULL;
1454
1455         /* get a copy of the names */
1456         hook->pattern = pattern ? strdup(pattern) : NULL;
1457         if (pattern && !hook->pattern) {
1458                 free(hook);
1459                 return NULL;
1460         }
1461
1462         /* initialise the rest */
1463         hook->refcount = 1;
1464         hook->flags = flags;
1465         hook->itf = itf ? itf : &hook_evt_default_itf;
1466         hook->closure = closure;
1467
1468         /* record the hook */
1469         pthread_mutex_lock(&mutex);
1470         hook->next = list_of_evt_hooks;
1471         list_of_evt_hooks = hook;
1472         pthread_mutex_unlock(&mutex);
1473
1474         /* returns it */
1475         return hook;
1476 }
1477
1478 struct afb_hook_evt *afb_hook_addref_evt(struct afb_hook_evt *hook)
1479 {
1480         __atomic_add_fetch(&hook->refcount, 1, __ATOMIC_RELAXED);
1481         return hook;
1482 }
1483
1484 static void hook_clean_evt()
1485 {
1486         struct afb_hook_evt **prv, *hook, *head;
1487
1488         if (pthread_rwlock_trywrlock(&rwlock) == 0) {
1489                 /* unlink under mutex */
1490                 head = NULL;
1491                 pthread_mutex_lock(&mutex);
1492                 prv = &list_of_evt_hooks;
1493                 while ((hook = *prv)) {
1494                         if (hook->refcount)
1495                                 prv = &(*prv)->next;
1496                         else {
1497                                 *prv = hook->next;
1498                                 hook->next = head;
1499                                 head = hook;
1500                         }
1501                 }
1502                 pthread_mutex_unlock(&mutex);
1503                 pthread_rwlock_unlock(&rwlock);
1504
1505                 /* free found hooks */
1506                 while((hook = head)) {
1507                         head = hook->next;
1508                         free(hook->pattern);
1509                         free(hook);
1510                 }
1511         }
1512 }
1513
1514 void afb_hook_unref_evt(struct afb_hook_evt *hook)
1515 {
1516         if (hook && !__atomic_sub_fetch(&hook->refcount, 1, __ATOMIC_RELAXED))
1517                 hook_clean_evt();
1518 }
1519
1520 /******************************************************************************
1521  * section: default callbacks for sessions (session)
1522  *****************************************************************************/
1523
1524 static void _hook_session_(struct afb_session *session, const char *format, ...)
1525 {
1526         va_list ap;
1527         va_start(ap, format);
1528         _hook_("session-%s", format, ap, afb_session_uuid(session));
1529         va_end(ap);
1530 }
1531
1532 static void hook_session_create_cb(void *closure, const struct afb_hookid *hookid, struct afb_session *session)
1533 {
1534         _hook_session_(session, "create -> token=%s", afb_session_token(session));
1535 }
1536
1537 static void hook_session_close_cb(void *closure, const struct afb_hookid *hookid, struct afb_session *session)
1538 {
1539         _hook_session_(session, "close");
1540 }
1541
1542 static void hook_session_destroy_cb(void *closure, const struct afb_hookid *hookid, struct afb_session *session)
1543 {
1544         _hook_session_(session, "destroy");
1545 }
1546
1547 static void hook_session_renew_cb(void *closure, const struct afb_hookid *hookid, struct afb_session *session)
1548 {
1549         _hook_session_(session, "renew -> token=%s", afb_session_token(session));
1550 }
1551
1552 static void hook_session_addref_cb(void *closure, const struct afb_hookid *hookid, struct afb_session *session)
1553 {
1554         _hook_session_(session, "addref");
1555 }
1556
1557 static void hook_session_unref_cb(void *closure, const struct afb_hookid *hookid, struct afb_session *session)
1558 {
1559         _hook_session_(session, "unref");
1560 }
1561
1562 static struct afb_hook_session_itf hook_session_default_itf = {
1563         .hook_session_create = hook_session_create_cb,
1564         .hook_session_close = hook_session_close_cb,
1565         .hook_session_destroy = hook_session_destroy_cb,
1566         .hook_session_renew = hook_session_renew_cb,
1567         .hook_session_addref = hook_session_addref_cb,
1568         .hook_session_unref = hook_session_unref_cb
1569 };
1570
1571 /******************************************************************************
1572  * section: hooks for tracing sessions (session)
1573  *****************************************************************************/
1574
1575 #define _HOOK_SESSION_(what,...)   \
1576         struct afb_hook_session *hook; \
1577         struct afb_hookid hookid; \
1578         const char *sessid = 0; \
1579         pthread_rwlock_rdlock(&rwlock); \
1580         init_hookid(&hookid); \
1581         hook = list_of_session_hooks; \
1582         while (hook) { \
1583                 if (hook->refcount \
1584                  && hook->itf->hook_session_##what \
1585                  && (hook->flags & afb_hook_flag_session_##what) != 0 \
1586                  && MATCH_SESSION(hook->pattern, (sessid?:(sessid=afb_session_uuid(session))))) { \
1587                         hook->itf->hook_session_##what(hook->closure, &hookid, __VA_ARGS__); \
1588                 } \
1589                 hook = hook->next; \
1590         } \
1591         pthread_rwlock_unlock(&rwlock);
1592
1593 void afb_hook_session_create(struct afb_session *session)
1594 {
1595         _HOOK_SESSION_(create, session);
1596 }
1597
1598 void afb_hook_session_close(struct afb_session *session)
1599 {
1600         _HOOK_SESSION_(close, session);
1601 }
1602
1603 void afb_hook_session_destroy(struct afb_session *session)
1604 {
1605         _HOOK_SESSION_(destroy, session);
1606 }
1607
1608 void afb_hook_session_renew(struct afb_session *session)
1609 {
1610         _HOOK_SESSION_(renew, session);
1611 }
1612
1613 void afb_hook_session_addref(struct afb_session *session)
1614 {
1615         _HOOK_SESSION_(addref, session);
1616 }
1617
1618 void afb_hook_session_unref(struct afb_session *session)
1619 {
1620         _HOOK_SESSION_(unref, session);
1621 }
1622
1623
1624 /******************************************************************************
1625  * section: hooking sessions (session)
1626  *****************************************************************************/
1627
1628 struct afb_hook_session *afb_hook_create_session(const char *pattern, int flags, struct afb_hook_session_itf *itf, void *closure)
1629 {
1630         struct afb_hook_session *hook;
1631
1632         /* alloc the result */
1633         hook = calloc(1, sizeof *hook);
1634         if (hook == NULL)
1635                 return NULL;
1636
1637         /* get a copy of the names */
1638         hook->pattern = pattern ? strdup(pattern) : NULL;
1639         if (pattern && !hook->pattern) {
1640                 free(hook);
1641                 return NULL;
1642         }
1643
1644         /* initialise the rest */
1645         hook->refcount = 1;
1646         hook->flags = flags;
1647         hook->itf = itf ? itf : &hook_session_default_itf;
1648         hook->closure = closure;
1649
1650         /* record the hook */
1651         pthread_mutex_lock(&mutex);
1652         hook->next = list_of_session_hooks;
1653         list_of_session_hooks = hook;
1654         pthread_mutex_unlock(&mutex);
1655
1656         /* returns it */
1657         return hook;
1658 }
1659
1660 struct afb_hook_session *afb_hook_addref_session(struct afb_hook_session *hook)
1661 {
1662         __atomic_add_fetch(&hook->refcount, 1, __ATOMIC_RELAXED);
1663         return hook;
1664 }
1665
1666 static void hook_clean_session()
1667 {
1668         struct afb_hook_session **prv, *hook, *head;
1669
1670         if (pthread_rwlock_trywrlock(&rwlock) == 0) {
1671                 /* unlink under mutex */
1672                 head = NULL;
1673                 pthread_mutex_lock(&mutex);
1674                 prv = &list_of_session_hooks;
1675                 while ((hook = *prv)) {
1676                         if (hook->refcount)
1677                                 prv = &(*prv)->next;
1678                         else {
1679                                 *prv = hook->next;
1680                                 hook->next = head;
1681                                 head = hook;
1682                         }
1683                 }
1684                 pthread_mutex_unlock(&mutex);
1685                 pthread_rwlock_unlock(&rwlock);
1686
1687                 /* free found hooks */
1688                 while((hook = head)) {
1689                         head = hook->next;
1690                         free(hook->pattern);
1691                         free(hook);
1692                 }
1693         }
1694 }
1695
1696 void afb_hook_unref_session(struct afb_hook_session *hook)
1697 {
1698         if (hook && !__atomic_sub_fetch(&hook->refcount, 1, __ATOMIC_RELAXED))
1699                 hook_clean_session();
1700 }
1701
1702 /******************************************************************************
1703  * section: default callbacks for globals (global)
1704  *****************************************************************************/
1705
1706 static void _hook_global_(const char *format, ...)
1707 {
1708         va_list ap;
1709         va_start(ap, format);
1710         _hook_("global", format, ap);
1711         va_end(ap);
1712 }
1713
1714 static void hook_global_vverbose_cb(void *closure, const struct afb_hookid *hookid, int level, const char *file, int line, const char *func, const char *fmt, va_list args)
1715 {
1716         int len;
1717         char *msg;
1718         va_list ap;
1719
1720         va_copy(ap, args);
1721         len = vasprintf(&msg, fmt, ap);
1722         va_end(ap);
1723
1724         if (len < 0)
1725                 _hook_global_("vverbose(%d:%s, %s, %d, %s) -> %s ? ? ?", level, verbose_name_of_level(level), file, line, func, fmt);
1726         else {
1727                 _hook_global_("vverbose(%d:%s, %s, %d, %s) -> %s", level, verbose_name_of_level(level), file, line, func, msg);
1728                 free(msg);
1729         }
1730 }
1731
1732 static struct afb_hook_global_itf hook_global_default_itf = {
1733         .hook_global_vverbose = hook_global_vverbose_cb
1734 };
1735
1736 /******************************************************************************
1737  * section: hooks for tracing globals (global)
1738  *****************************************************************************/
1739
1740 #define _HOOK_GLOBAL_(what,...)   \
1741         struct afb_hook_global *hook; \
1742         struct afb_hookid hookid; \
1743         pthread_rwlock_rdlock(&rwlock); \
1744         init_hookid(&hookid); \
1745         hook = list_of_global_hooks; \
1746         while (hook) { \
1747                 if (hook->refcount \
1748                  && hook->itf->hook_global_##what \
1749                  && (hook->flags & afb_hook_flag_global_##what) != 0) { \
1750                         hook->itf->hook_global_##what(hook->closure, &hookid, __VA_ARGS__); \
1751                 } \
1752                 hook = hook->next; \
1753         } \
1754         pthread_rwlock_unlock(&rwlock);
1755
1756 static void afb_hook_global_vverbose(int level, const char *file, int line, const char *func, const char *fmt, va_list args)
1757 {
1758         _HOOK_GLOBAL_(vverbose, level, file ?: "?", line, func ?: "?", fmt ?: "", args);
1759 }
1760
1761 /******************************************************************************
1762  * section: hooking globals (global)
1763  *****************************************************************************/
1764
1765 static void update_global()
1766 {
1767         struct afb_hook_global *hook;
1768         int flags = 0;
1769
1770         pthread_rwlock_rdlock(&rwlock);
1771         hook = list_of_global_hooks;
1772         while (hook) {
1773                 if (hook->refcount)
1774                         flags = hook->flags;
1775                 hook = hook->next;
1776         }
1777         verbose_observer = (flags & afb_hook_flag_global_vverbose) ? afb_hook_global_vverbose : NULL;
1778         pthread_rwlock_unlock(&rwlock);
1779 }
1780
1781 struct afb_hook_global *afb_hook_create_global(int flags, struct afb_hook_global_itf *itf, void *closure)
1782 {
1783         struct afb_hook_global *hook;
1784
1785         /* alloc the result */
1786         hook = calloc(1, sizeof *hook);
1787         if (hook == NULL)
1788                 return NULL;
1789
1790         /* initialise the rest */
1791         hook->refcount = 1;
1792         hook->flags = flags;
1793         hook->itf = itf ? itf : &hook_global_default_itf;
1794         hook->closure = closure;
1795
1796         /* record the hook */
1797         pthread_mutex_lock(&mutex);
1798         hook->next = list_of_global_hooks;
1799         list_of_global_hooks = hook;
1800         pthread_mutex_unlock(&mutex);
1801
1802         /* update hooking */
1803         update_global();
1804
1805         /* returns it */
1806         return hook;
1807 }
1808
1809 struct afb_hook_global *afb_hook_addref_global(struct afb_hook_global *hook)
1810 {
1811         __atomic_add_fetch(&hook->refcount, 1, __ATOMIC_RELAXED);
1812         return hook;
1813 }
1814
1815 static void hook_clean_global()
1816 {
1817         struct afb_hook_global **prv, *hook, *head;
1818
1819         if (pthread_rwlock_trywrlock(&rwlock) == 0) {
1820                 /* unlink under mutex */
1821                 head = NULL;
1822                 pthread_mutex_lock(&mutex);
1823                 prv = &list_of_global_hooks;
1824                 while ((hook = *prv)) {
1825                         if (hook->refcount)
1826                                 prv = &(*prv)->next;
1827                         else {
1828                                 *prv = hook->next;
1829                                 hook->next = head;
1830                                 head = hook;
1831                         }
1832                 }
1833                 pthread_mutex_unlock(&mutex);
1834                 pthread_rwlock_unlock(&rwlock);
1835
1836                 /* free found hooks */
1837                 while((hook = head)) {
1838                         head = hook->next;
1839                         free(hook);
1840                 }
1841         }
1842 }
1843
1844 void afb_hook_unref_global(struct afb_hook_global *hook)
1845 {
1846         if (hook && !__atomic_sub_fetch(&hook->refcount, 1, __ATOMIC_RELAXED)) {
1847                 /* update hooking */
1848                 update_global();
1849                 hook_clean_global();
1850         }
1851 }
1852
1853 #endif /* WITH_AFB_HOOK *******************************************************/