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