Update date of copyright notices
[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 <fnmatch.h>
27 #include <sys/uio.h>
28
29 #include <json-c/json.h>
30
31 #include <afb/afb-req.h>
32 #include <afb/afb-event.h>
33
34 #include "afb-context.h"
35 #include "afb-hook.h"
36 #include "afb-session.h"
37 #include "afb-cred.h"
38 #include "afb-xreq.h"
39 #include "afb-export.h"
40 #include "afb-evt.h"
41 #include "afb-api.h"
42 #include "verbose.h"
43
44 /**
45  * Definition of a hook for xreq
46  */
47 struct afb_hook_xreq {
48         struct afb_hook_xreq *next; /**< next hook */
49         unsigned refcount; /**< reference count */
50         unsigned flags; /**< hook flags */
51         char *api; /**< api hooked or NULL for any */
52         char *verb; /**< verb hooked or NULL for any */
53         struct afb_session *session; /**< session hooked or NULL if any */
54         struct afb_hook_xreq_itf *itf; /**< interface of hook */
55         void *closure; /**< closure for callbacks */
56 };
57
58 /**
59  * Definition of a hook for export
60  */
61 struct afb_hook_ditf {
62         struct afb_hook_ditf *next; /**< next hook */
63         unsigned refcount; /**< reference count */
64         unsigned flags; /**< hook flags */
65         char *api; /**< api hooked or NULL for any */
66         struct afb_hook_ditf_itf *itf; /**< interface of hook */
67         void *closure; /**< closure for callbacks */
68 };
69
70 /**
71  * Definition of a hook for export
72  */
73 struct afb_hook_svc {
74         struct afb_hook_svc *next; /**< next hook */
75         unsigned refcount; /**< reference count */
76         unsigned flags; /**< hook flags */
77         char *api; /**< api hooked or NULL for any */
78         struct afb_hook_svc_itf *itf; /**< interface of hook */
79         void *closure; /**< closure for callbacks */
80 };
81
82 /**
83  * Definition of a hook for evt
84  */
85 struct afb_hook_evt {
86         struct afb_hook_evt *next; /**< next hook */
87         unsigned refcount; /**< reference count */
88         unsigned flags; /**< hook flags */
89         char *pattern; /**< event pattern name hooked or NULL for any */
90         struct afb_hook_evt_itf *itf; /**< interface of hook */
91         void *closure; /**< closure for callbacks */
92 };
93
94 /**
95  * Definition of a hook for session
96  */
97 struct afb_hook_session {
98         struct afb_hook_session *next; /**< next hook */
99         unsigned refcount; /**< reference count */
100         unsigned flags; /**< hook flags */
101         char *pattern; /**< event pattern name hooked or NULL for any */
102         struct afb_hook_session_itf *itf; /**< interface of hook */
103         void *closure; /**< closure for callbacks */
104 };
105
106 /**
107  * Definition of a hook for global
108  */
109 struct afb_hook_global {
110         struct afb_hook_global *next; /**< next hook */
111         unsigned refcount; /**< reference count */
112         unsigned flags; /**< hook flags */
113         struct afb_hook_global_itf *itf; /**< interface of hook */
114         void *closure; /**< closure for callbacks */
115 };
116
117 /* synchronisation across threads */
118 static pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
119
120 /* list of hooks for xreq */
121 static struct afb_hook_xreq *list_of_xreq_hooks = NULL;
122
123 /* list of hooks for export */
124 static struct afb_hook_ditf *list_of_ditf_hooks = NULL;
125
126 /* list of hooks for export */
127 static struct afb_hook_svc *list_of_svc_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_MONOTONIC, &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 #if 0 /* old behaviour: use NOTICE */
176 static void _hook_(const char *fmt1, const char *fmt2, va_list arg2, ...)
177 {
178         char *tag, *data, *mem1, *mem2, buf1[256], buf2[2000];
179         va_list arg1;
180
181         data = _pbuf_(fmt2, arg2, &mem2, buf2, sizeof buf2, NULL);
182
183         va_start(arg1, arg2);
184         tag = _pbuf_(fmt1, arg1, &mem1, buf1, sizeof buf1, NULL);
185         va_end(arg1);
186
187         NOTICE("[HOOK %s] %s", tag, data);
188
189         free(mem1);
190         free(mem2);
191 }
192 #else /* new behaviour: emits directly to stderr */
193 static void _hook_(const char *fmt1, const char *fmt2, va_list arg2, ...)
194 {
195         static const char chars[] = "HOOK: [] \n";
196         char *mem1, *mem2, buf1[256], buf2[2000];
197         struct iovec iov[5];
198         va_list arg1;
199
200         iov[0].iov_base = (void*)&chars[0];
201         iov[0].iov_len = 7;
202
203         va_start(arg1, arg2);
204         iov[1].iov_base = _pbuf_(fmt1, arg1, &mem1, buf1, sizeof buf1, &iov[1].iov_len);
205         va_end(arg1);
206
207         iov[2].iov_base = (void*)&chars[7];
208         iov[2].iov_len = 2;
209
210         iov[3].iov_base = _pbuf_(fmt2, arg2, &mem2, buf2, sizeof buf2, &iov[3].iov_len);
211
212         iov[4].iov_base = (void*)&chars[9];
213         iov[4].iov_len = 1;
214
215         writev(2, iov, 5);
216
217         free(mem1);
218         free(mem2);
219 }
220 #endif
221
222 static void _hook_xreq_(const struct afb_xreq *xreq, const char *format, ...)
223 {
224         va_list ap;
225         va_start(ap, format);
226         _hook_("xreq-%06d:%s/%s", format, ap, xreq->hookindex, xreq->request.api, xreq->request.verb);
227         va_end(ap);
228 }
229
230 static void hook_xreq_begin_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq)
231 {
232         if (!xreq->cred)
233                 _hook_xreq_(xreq, "BEGIN");
234         else
235                 _hook_xreq_(xreq, "BEGIN uid=%d=%s gid=%d pid=%d label=%s id=%s",
236                         (int)xreq->cred->uid,
237                         xreq->cred->user,
238                         (int)xreq->cred->gid,
239                         (int)xreq->cred->pid,
240                         xreq->cred->label?:"(null)",
241                         xreq->cred->id?:"(null)"
242                 );
243 }
244
245 static void hook_xreq_end_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq)
246 {
247         _hook_xreq_(xreq, "END");
248 }
249
250 static void hook_xreq_json_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq, struct json_object *obj)
251 {
252         _hook_xreq_(xreq, "json() -> %s", json_object_to_json_string(obj));
253 }
254
255 static void hook_xreq_get_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq, const char *name, struct afb_arg arg)
256 {
257         _hook_xreq_(xreq, "get(%s) -> { name: %s, value: %s, path: %s }", name, arg.name, arg.value, arg.path);
258 }
259
260 static void hook_xreq_success_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq, struct json_object *obj, const char *info)
261 {
262         _hook_xreq_(xreq, "success(%s, %s)", json_object_to_json_string(obj), info);
263 }
264
265 static void hook_xreq_fail_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq, const char *status, const char *info)
266 {
267         _hook_xreq_(xreq, "fail(%s, %s)", status, info);
268 }
269
270 static void hook_xreq_context_get_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq, void *value)
271 {
272         _hook_xreq_(xreq, "context_get() -> %p", value);
273 }
274
275 static void hook_xreq_context_set_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq, void *value, void (*free_value)(void*))
276 {
277         _hook_xreq_(xreq, "context_set(%p, %p)", value, free_value);
278 }
279
280 static void hook_xreq_addref_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq)
281 {
282         _hook_xreq_(xreq, "addref()");
283 }
284
285 static void hook_xreq_unref_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq)
286 {
287         _hook_xreq_(xreq, "unref()");
288 }
289
290 static void hook_xreq_session_close_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq)
291 {
292         _hook_xreq_(xreq, "session_close()");
293 }
294
295 static void hook_xreq_session_set_LOA_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq, unsigned level, int result)
296 {
297         _hook_xreq_(xreq, "session_set_LOA(%u) -> %d", level, result);
298 }
299
300 static void hook_xreq_subscribe_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq, struct afb_eventid *eventid, int result)
301 {
302         _hook_xreq_(xreq, "subscribe(%s:%d) -> %d", afb_evt_eventid_fullname(eventid), afb_evt_eventid_id(eventid), result);
303 }
304
305 static void hook_xreq_unsubscribe_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq, struct afb_eventid *eventid, int result)
306 {
307         _hook_xreq_(xreq, "unsubscribe(%s:%d) -> %d", afb_evt_eventid_fullname(eventid), afb_evt_eventid_id(eventid), result);
308 }
309
310 static void hook_xreq_subcall_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq, const char *api, const char *verb, struct json_object *args)
311 {
312         _hook_xreq_(xreq, "subcall(%s/%s, %s) ...", api, verb, json_object_to_json_string(args));
313 }
314
315 static void hook_xreq_subcall_result_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq, int status, struct json_object *result)
316 {
317         _hook_xreq_(xreq, "    ...subcall... -> %d: %s", status, json_object_to_json_string(result));
318 }
319
320 static void hook_xreq_subcallsync_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq, const char *api, const char *verb, struct json_object *args)
321 {
322         _hook_xreq_(xreq, "subcallsync(%s/%s, %s) ...", api, verb, json_object_to_json_string(args));
323 }
324
325 static void hook_xreq_subcallsync_result_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq, int status, struct json_object *result)
326 {
327         _hook_xreq_(xreq, "    ...subcallsync... -> %d: %s", status, json_object_to_json_string(result));
328 }
329
330 static void hook_xreq_vverbose_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq, int level, const char *file, int line, const char *func, const char *fmt, va_list args)
331 {
332         int len;
333         char *msg;
334         va_list ap;
335
336         va_copy(ap, args);
337         len = vasprintf(&msg, fmt, ap);
338         va_end(ap);
339
340         if (len < 0)
341                 _hook_xreq_(xreq, "vverbose(%d, %s, %d, %s) -> %s ? ? ?", level, file, line, func, fmt);
342         else {
343                 _hook_xreq_(xreq, "vverbose(%d, %s, %d, %s) -> %s", level, file, line, func, msg);
344                 free(msg);
345         }
346 }
347
348 static void hook_xreq_store_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq, struct afb_stored_req *sreq)
349 {
350         _hook_xreq_(xreq, "store() -> %p", sreq);
351 }
352
353 static void hook_xreq_unstore_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq)
354 {
355         _hook_xreq_(xreq, "unstore()");
356 }
357
358 static void hook_xreq_subcall_req_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq, const char *api, const char *verb, struct json_object *args)
359 {
360         _hook_xreq_(xreq, "subcall_req(%s/%s, %s) ...", api, verb, json_object_to_json_string(args));
361 }
362
363 static void hook_xreq_subcall_req_result_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq, int status, struct json_object *result)
364 {
365         _hook_xreq_(xreq, "    ...subcall_req... -> %d: %s", status, json_object_to_json_string(result));
366 }
367
368 static void hook_xreq_has_permission_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq, const char *permission, int result)
369 {
370         _hook_xreq_(xreq, "has_permission(%s) -> %d", permission, result);
371 }
372
373 static void hook_xreq_get_application_id_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq, char *result)
374 {
375         _hook_xreq_(xreq, "get_application_id() -> %s", result);
376 }
377
378 static void hook_xreq_context_make_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq, int replace, void *(*create_value)(void*), void (*free_value)(void*), void *create_closure, void *result)
379 {
380         _hook_xreq_(xreq, "context_make(replace=%s, %p, %p, %p) -> %p", replace?"yes":"no", create_value, free_value, create_closure, result);
381 }
382
383 static void hook_xreq_get_uid_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq, int result)
384 {
385         _hook_xreq_(xreq, "get_uid() -> %d", result);
386 }
387
388 static struct afb_hook_xreq_itf hook_xreq_default_itf = {
389         .hook_xreq_begin = hook_xreq_begin_default_cb,
390         .hook_xreq_end = hook_xreq_end_default_cb,
391         .hook_xreq_json = hook_xreq_json_default_cb,
392         .hook_xreq_get = hook_xreq_get_default_cb,
393         .hook_xreq_success = hook_xreq_success_default_cb,
394         .hook_xreq_fail = hook_xreq_fail_default_cb,
395         .hook_xreq_context_get = hook_xreq_context_get_default_cb,
396         .hook_xreq_context_set = hook_xreq_context_set_default_cb,
397         .hook_xreq_addref = hook_xreq_addref_default_cb,
398         .hook_xreq_unref = hook_xreq_unref_default_cb,
399         .hook_xreq_session_close = hook_xreq_session_close_default_cb,
400         .hook_xreq_session_set_LOA = hook_xreq_session_set_LOA_default_cb,
401         .hook_xreq_subscribe = hook_xreq_subscribe_default_cb,
402         .hook_xreq_unsubscribe = hook_xreq_unsubscribe_default_cb,
403         .hook_xreq_subcall = hook_xreq_subcall_default_cb,
404         .hook_xreq_subcall_result = hook_xreq_subcall_result_default_cb,
405         .hook_xreq_subcallsync = hook_xreq_subcallsync_default_cb,
406         .hook_xreq_subcallsync_result = hook_xreq_subcallsync_result_default_cb,
407         .hook_xreq_vverbose = hook_xreq_vverbose_default_cb,
408         .hook_xreq_store = hook_xreq_store_default_cb,
409         .hook_xreq_unstore = hook_xreq_unstore_default_cb,
410         .hook_xreq_subcall_req = hook_xreq_subcall_req_default_cb,
411         .hook_xreq_subcall_req_result = hook_xreq_subcall_req_result_default_cb,
412         .hook_xreq_has_permission = hook_xreq_has_permission_default_cb,
413         .hook_xreq_get_application_id = hook_xreq_get_application_id_default_cb,
414         .hook_xreq_context_make = hook_xreq_context_make_default_cb,
415         .hook_xreq_get_uid = hook_xreq_get_uid_default_cb,
416 };
417
418 /******************************************************************************
419  * section: hooks for tracing requests
420  *****************************************************************************/
421
422 #define _HOOK_XREQ_(what,...)   \
423         struct afb_hook_xreq *hook; \
424         struct afb_hookid hookid; \
425         pthread_rwlock_rdlock(&rwlock); \
426         init_hookid(&hookid); \
427         hook = list_of_xreq_hooks; \
428         while (hook) { \
429                 if (hook->itf->hook_xreq_##what \
430                  && (hook->flags & afb_hook_flag_req_##what) != 0 \
431                  && (!hook->session || hook->session == xreq->context.session) \
432                  && (!hook->api || !strcasecmp(hook->api, xreq->request.api)) \
433                  && (!hook->verb || !strcasecmp(hook->verb, xreq->request.verb))) { \
434                         hook->itf->hook_xreq_##what(hook->closure, &hookid, __VA_ARGS__); \
435                 } \
436                 hook = hook->next; \
437         } \
438         pthread_rwlock_unlock(&rwlock);
439
440
441 void afb_hook_xreq_begin(const struct afb_xreq *xreq)
442 {
443         _HOOK_XREQ_(begin, xreq);
444 }
445
446 void afb_hook_xreq_end(const struct afb_xreq *xreq)
447 {
448         _HOOK_XREQ_(end, xreq);
449 }
450
451 struct json_object *afb_hook_xreq_json(const struct afb_xreq *xreq, struct json_object *obj)
452 {
453         _HOOK_XREQ_(json, xreq, obj);
454         return obj;
455 }
456
457 struct afb_arg afb_hook_xreq_get(const struct afb_xreq *xreq, const char *name, struct afb_arg arg)
458 {
459         _HOOK_XREQ_(get, xreq, name, arg);
460         return arg;
461 }
462
463 void afb_hook_xreq_success(const struct afb_xreq *xreq, struct json_object *obj, const char *info)
464 {
465         _HOOK_XREQ_(success, xreq, obj, info);
466 }
467
468 void afb_hook_xreq_fail(const struct afb_xreq *xreq, const char *status, const char *info)
469 {
470         _HOOK_XREQ_(fail, xreq, status, info);
471 }
472
473 void *afb_hook_xreq_context_get(const struct afb_xreq *xreq, void *value)
474 {
475         _HOOK_XREQ_(context_get, xreq, value);
476         return value;
477 }
478
479 void afb_hook_xreq_context_set(const struct afb_xreq *xreq, void *value, void (*free_value)(void*))
480 {
481         _HOOK_XREQ_(context_set, xreq, value, free_value);
482 }
483
484 void afb_hook_xreq_addref(const struct afb_xreq *xreq)
485 {
486         _HOOK_XREQ_(addref, xreq);
487 }
488
489 void afb_hook_xreq_unref(const struct afb_xreq *xreq)
490 {
491         _HOOK_XREQ_(unref, xreq);
492 }
493
494 void afb_hook_xreq_session_close(const struct afb_xreq *xreq)
495 {
496         _HOOK_XREQ_(session_close, xreq);
497 }
498
499 int afb_hook_xreq_session_set_LOA(const struct afb_xreq *xreq, unsigned level, int result)
500 {
501         _HOOK_XREQ_(session_set_LOA, xreq, level, result);
502         return result;
503 }
504
505 int afb_hook_xreq_subscribe(const struct afb_xreq *xreq, struct afb_eventid *eventid, int result)
506 {
507         _HOOK_XREQ_(subscribe, xreq, eventid, result);
508         return result;
509 }
510
511 int afb_hook_xreq_unsubscribe(const struct afb_xreq *xreq, struct afb_eventid *eventid, int result)
512 {
513         _HOOK_XREQ_(unsubscribe, xreq, eventid, result);
514         return result;
515 }
516
517 void afb_hook_xreq_subcall(const struct afb_xreq *xreq, const char *api, const char *verb, struct json_object *args)
518 {
519         _HOOK_XREQ_(subcall, xreq, api, verb, args);
520 }
521
522 void afb_hook_xreq_subcall_result(const struct afb_xreq *xreq, int status, struct json_object *result)
523 {
524         _HOOK_XREQ_(subcall_result, xreq, status, result);
525 }
526
527 void afb_hook_xreq_subcallsync(const struct afb_xreq *xreq, const char *api, const char *verb, struct json_object *args)
528 {
529         _HOOK_XREQ_(subcallsync, xreq, api, verb, args);
530 }
531
532 int afb_hook_xreq_subcallsync_result(const struct afb_xreq *xreq, int status, struct json_object *result)
533 {
534         _HOOK_XREQ_(subcallsync_result, xreq, status, result);
535         return status;
536 }
537
538 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)
539 {
540         _HOOK_XREQ_(vverbose, xreq, level, file ?: "?", line, func ?: "?", fmt, args);
541 }
542
543 void afb_hook_xreq_store(const struct afb_xreq *xreq, struct afb_stored_req *sreq)
544 {
545         _HOOK_XREQ_(store, xreq, sreq);
546 }
547
548 void afb_hook_xreq_unstore(const struct afb_xreq *xreq)
549 {
550         _HOOK_XREQ_(unstore, xreq);
551 }
552
553 void afb_hook_xreq_subcall_req(const struct afb_xreq *xreq, const char *api, const char *verb, struct json_object *args)
554 {
555         _HOOK_XREQ_(subcall_req, xreq, api, verb, args);
556 }
557
558 void afb_hook_xreq_subcall_req_result(const struct afb_xreq *xreq, int status, struct json_object *result)
559 {
560         _HOOK_XREQ_(subcall_req_result, xreq, status, result);
561 }
562
563 int afb_hook_xreq_has_permission(const struct afb_xreq *xreq, const char *permission, int result)
564 {
565         _HOOK_XREQ_(has_permission, xreq, permission, result);
566         return result;
567 }
568
569 char *afb_hook_xreq_get_application_id(const struct afb_xreq *xreq, char *result)
570 {
571         _HOOK_XREQ_(get_application_id, xreq, result);
572         return result;
573 }
574
575 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)
576 {
577         _HOOK_XREQ_(context_make, xreq, replace, create_value, free_value, create_closure, result);
578         return result;
579 }
580
581 int afb_hook_xreq_get_uid(const struct afb_xreq *xreq, int result)
582 {
583         _HOOK_XREQ_(get_uid, xreq, result);
584         return result;
585 }
586
587 /******************************************************************************
588  * section: hooking xreqs
589  *****************************************************************************/
590
591 void afb_hook_init_xreq(struct afb_xreq *xreq)
592 {
593         static int reqindex;
594
595         int f, flags;
596         int add;
597         struct afb_hook_xreq *hook;
598
599         /* scan hook list to get the expected flags */
600         flags = 0;
601         if (afb_api_is_hookable(xreq->request.api)) {
602                 pthread_rwlock_rdlock(&rwlock);
603                 hook = list_of_xreq_hooks;
604                 while (hook) {
605                         f = hook->flags & afb_hook_flags_req_all;
606                         add = f != 0
607                            && (!hook->session || hook->session == xreq->context.session)
608                            && (!hook->api || !strcasecmp(hook->api, xreq->request.api))
609                            && (!hook->verb || !strcasecmp(hook->verb, xreq->request.verb));
610                         if (add)
611                                 flags |= f;
612                         hook = hook->next;
613                 }
614                 pthread_rwlock_unlock(&rwlock);
615         }
616
617         /* store the hooking data */
618         xreq->hookflags = flags;
619         if (flags) {
620                 pthread_rwlock_wrlock(&rwlock);
621                 if (++reqindex < 0)
622                         reqindex = 1;
623                 xreq->hookindex = reqindex;
624                 pthread_rwlock_unlock(&rwlock);
625         }
626 }
627
628 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)
629 {
630         struct afb_hook_xreq *hook;
631
632         /* alloc the result */
633         hook = calloc(1, sizeof *hook);
634         if (hook == NULL)
635                 return NULL;
636
637         /* get a copy of the names */
638         hook->api = api ? strdup(api) : NULL;
639         hook->verb = verb ? strdup(verb) : NULL;
640         if ((api && !hook->api) || (verb && !hook->verb)) {
641                 free(hook->api);
642                 free(hook->verb);
643                 free(hook);
644                 return NULL;
645         }
646
647         /* initialise the rest */
648         hook->session = session;
649         if (session)
650                 afb_session_addref(session);
651         hook->refcount = 1;
652         hook->flags = flags;
653         hook->itf = itf ? itf : &hook_xreq_default_itf;
654         hook->closure = closure;
655
656         /* record the hook */
657         pthread_rwlock_wrlock(&rwlock);
658         hook->next = list_of_xreq_hooks;
659         list_of_xreq_hooks = hook;
660         pthread_rwlock_unlock(&rwlock);
661
662         /* returns it */
663         return hook;
664 }
665
666 struct afb_hook_xreq *afb_hook_addref_xreq(struct afb_hook_xreq *hook)
667 {
668         pthread_rwlock_wrlock(&rwlock);
669         hook->refcount++;
670         pthread_rwlock_unlock(&rwlock);
671         return hook;
672 }
673
674 void afb_hook_unref_xreq(struct afb_hook_xreq *hook)
675 {
676         struct afb_hook_xreq **prv;
677
678         if (hook) {
679                 pthread_rwlock_wrlock(&rwlock);
680                 if (--hook->refcount)
681                         hook = NULL;
682                 else {
683                         /* unlink */
684                         prv = &list_of_xreq_hooks;
685                         while (*prv && *prv != hook)
686                                 prv = &(*prv)->next;
687                         if(*prv)
688                                 *prv = hook->next;
689                 }
690                 pthread_rwlock_unlock(&rwlock);
691                 if (hook) {
692                         /* free */
693                         free(hook->api);
694                         free(hook->verb);
695                         if (hook->session)
696                                 afb_session_unref(hook->session);
697                         free(hook);
698                 }
699         }
700 }
701
702 /******************************************************************************
703  * section: default callbacks for tracing daemon interface
704  *****************************************************************************/
705
706 static void _hook_ditf_(const struct afb_export *export, const char *format, ...)
707 {
708         va_list ap;
709         va_start(ap, format);
710         _hook_("export-%s", format, ap, afb_export_apiname(export));
711         va_end(ap);
712 }
713
714 static void hook_ditf_event_broadcast_before_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export, const char *name, struct json_object *object)
715 {
716         _hook_ditf_(export, "event_broadcast.before(%s, %s)....", name, json_object_to_json_string(object));
717 }
718
719 static void hook_ditf_event_broadcast_after_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export, const char *name, struct json_object *object, int result)
720 {
721         _hook_ditf_(export, "event_broadcast.after(%s, %s) -> %d", name, json_object_to_json_string(object), result);
722 }
723
724 static void hook_ditf_get_event_loop_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export, struct sd_event *result)
725 {
726         _hook_ditf_(export, "get_event_loop() -> %p", result);
727 }
728
729 static void hook_ditf_get_user_bus_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export, struct sd_bus *result)
730 {
731         _hook_ditf_(export, "get_user_bus() -> %p", result);
732 }
733
734 static void hook_ditf_get_system_bus_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export, struct sd_bus *result)
735 {
736         _hook_ditf_(export, "get_system_bus() -> %p", result);
737 }
738
739 static void hook_ditf_vverbose_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export, int level, const char *file, int line, const char *function, const char *fmt, va_list args)
740 {
741         int len;
742         char *msg;
743         va_list ap;
744
745         va_copy(ap, args);
746         len = vasprintf(&msg, fmt, ap);
747         va_end(ap);
748
749         if (len < 0)
750                 _hook_ditf_(export, "vverbose(%d, %s, %d, %s) -> %s ? ? ?", level, file, line, function, fmt);
751         else {
752                 _hook_ditf_(export, "vverbose(%d, %s, %d, %s) -> %s", level, file, line, function, msg);
753                 free(msg);
754         }
755 }
756
757 static void hook_ditf_event_make_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export, const char *name, struct afb_eventid *result)
758 {
759         _hook_ditf_(export, "event_make(%s) -> %s:%d", name, afb_evt_eventid_fullname(result), afb_evt_eventid_id(result));
760 }
761
762 static void hook_ditf_rootdir_get_fd_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export, int result)
763 {
764         char path[PATH_MAX];
765         if (result < 0)
766                 _hook_ditf_(export, "rootdir_get_fd() -> %d, %m", result);
767         else {
768                 sprintf(path, "/proc/self/fd/%d", result);
769                 readlink(path, path, sizeof path);
770                 _hook_ditf_(export, "rootdir_get_fd() -> %d = %s", result, path);
771         }
772 }
773
774 static void hook_ditf_rootdir_open_locale_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export, const char *filename, int flags, const char *locale, int result)
775 {
776         char path[PATH_MAX];
777         if (!locale)
778                 locale = "(null)";
779         if (result < 0)
780                 _hook_ditf_(export, "rootdir_open_locale(%s, %d, %s) -> %d, %m", filename, flags, locale, result);
781         else {
782                 sprintf(path, "/proc/self/fd/%d", result);
783                 readlink(path, path, sizeof path);
784                 _hook_ditf_(export, "rootdir_open_locale(%s, %d, %s) -> %d = %s", filename, flags, locale, result, path);
785         }
786 }
787
788 static void hook_ditf_queue_job_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export, void (*callback)(int signum, void *arg), void *argument, void *group, int timeout, int result)
789 {
790         _hook_ditf_(export, "queue_job(%p, %p, %p, %d) -> %d", callback, argument, group, timeout, result);
791 }
792
793 static void hook_ditf_unstore_req_cb(void *closure, const struct afb_hookid *hookid,  const struct afb_export *export, struct afb_stored_req *sreq)
794 {
795         _hook_ditf_(export, "unstore_req(%p)", sreq);
796 }
797
798 static void hook_ditf_require_api_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export, const char *name, int initialized)
799 {
800         _hook_ditf_(export, "require_api(%s, %d)...", name, initialized);
801 }
802
803 static void hook_ditf_require_api_result_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export, const char *name, int initialized, int result)
804 {
805         _hook_ditf_(export, "...require_api(%s, %d) -> %d", name, initialized, result);
806 }
807
808 static void hook_ditf_rename_api_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export, const char *oldname, const char *newname, int result)
809 {
810         _hook_ditf_(export, "rename_api(%s -> %s) -> %d", oldname, newname, result);
811 }
812
813 static struct afb_hook_ditf_itf hook_ditf_default_itf = {
814         .hook_ditf_event_broadcast_before = hook_ditf_event_broadcast_before_cb,
815         .hook_ditf_event_broadcast_after = hook_ditf_event_broadcast_after_cb,
816         .hook_ditf_get_event_loop = hook_ditf_get_event_loop_cb,
817         .hook_ditf_get_user_bus = hook_ditf_get_user_bus_cb,
818         .hook_ditf_get_system_bus = hook_ditf_get_system_bus_cb,
819         .hook_ditf_vverbose = hook_ditf_vverbose_cb,
820         .hook_ditf_event_make = hook_ditf_event_make_cb,
821         .hook_ditf_rootdir_get_fd = hook_ditf_rootdir_get_fd_cb,
822         .hook_ditf_rootdir_open_locale = hook_ditf_rootdir_open_locale_cb,
823         .hook_ditf_queue_job = hook_ditf_queue_job_cb,
824         .hook_ditf_unstore_req = hook_ditf_unstore_req_cb,
825         .hook_ditf_require_api = hook_ditf_require_api_cb,
826         .hook_ditf_require_api_result = hook_ditf_require_api_result_cb,
827         .hook_ditf_rename_api = hook_ditf_rename_api_cb
828 };
829
830 /******************************************************************************
831  * section: hooks for tracing daemon interface (export)
832  *****************************************************************************/
833
834 #define _HOOK_DITF_(what,...)   \
835         struct afb_hook_ditf *hook; \
836         struct afb_hookid hookid; \
837         const char *apiname = afb_export_apiname(export); \
838         pthread_rwlock_rdlock(&rwlock); \
839         init_hookid(&hookid); \
840         hook = list_of_ditf_hooks; \
841         while (hook) { \
842                 if (hook->itf->hook_ditf_##what \
843                  && (hook->flags & afb_hook_flag_ditf_##what) != 0 \
844                  && (!hook->api || !strcasecmp(hook->api, apiname))) { \
845                         hook->itf->hook_ditf_##what(hook->closure, &hookid, __VA_ARGS__); \
846                 } \
847                 hook = hook->next; \
848         } \
849         pthread_rwlock_unlock(&rwlock);
850
851 void afb_hook_ditf_event_broadcast_before(const struct afb_export *export, const char *name, struct json_object *object)
852 {
853         _HOOK_DITF_(event_broadcast_before, export, name, object);
854 }
855
856 int afb_hook_ditf_event_broadcast_after(const struct afb_export *export, const char *name, struct json_object *object, int result)
857 {
858         _HOOK_DITF_(event_broadcast_after, export, name, object, result);
859         return result;
860 }
861
862 struct sd_event *afb_hook_ditf_get_event_loop(const struct afb_export *export, struct sd_event *result)
863 {
864         _HOOK_DITF_(get_event_loop, export, result);
865         return result;
866 }
867
868 struct sd_bus *afb_hook_ditf_get_user_bus(const struct afb_export *export, struct sd_bus *result)
869 {
870         _HOOK_DITF_(get_user_bus, export, result);
871         return result;
872 }
873
874 struct sd_bus *afb_hook_ditf_get_system_bus(const struct afb_export *export, struct sd_bus *result)
875 {
876         _HOOK_DITF_(get_system_bus, export, result);
877         return result;
878 }
879
880 void afb_hook_ditf_vverbose(const struct afb_export *export, int level, const char *file, int line, const char *function, const char *fmt, va_list args)
881 {
882         _HOOK_DITF_(vverbose, export, level, file, line, function, fmt, args);
883 }
884
885 struct afb_eventid *afb_hook_ditf_event_make(const struct afb_export *export, const char *name, struct afb_eventid *result)
886 {
887         _HOOK_DITF_(event_make, export, name, result);
888         return result;
889 }
890
891 int afb_hook_ditf_rootdir_get_fd(const struct afb_export *export, int result)
892 {
893         _HOOK_DITF_(rootdir_get_fd, export, result);
894         return result;
895 }
896
897 int afb_hook_ditf_rootdir_open_locale(const struct afb_export *export, const char *filename, int flags, const char *locale, int result)
898 {
899         _HOOK_DITF_(rootdir_open_locale, export, filename, flags, locale, result);
900         return result;
901 }
902
903 int afb_hook_ditf_queue_job(const struct afb_export *export, void (*callback)(int signum, void *arg), void *argument, void *group, int timeout, int result)
904 {
905         _HOOK_DITF_(queue_job, export, callback, argument, group, timeout, result);
906         return result;
907 }
908
909 void afb_hook_ditf_unstore_req(const struct afb_export *export, struct afb_stored_req *sreq)
910 {
911         _HOOK_DITF_(unstore_req, export, sreq);
912 }
913
914 void afb_hook_ditf_require_api(const struct afb_export *export, const char *name, int initialized)
915 {
916         _HOOK_DITF_(require_api, export, name, initialized);
917 }
918
919 int afb_hook_ditf_require_api_result(const struct afb_export *export, const char *name, int initialized, int result)
920 {
921         _HOOK_DITF_(require_api_result, export, name, initialized, result);
922         return result;
923 }
924
925 int afb_hook_ditf_rename_api(const struct afb_export *export, const char *oldname, const char *newname, int result)
926 {
927         _HOOK_DITF_(rename_api, export, oldname, newname, result);
928         return result;
929 }
930
931 /******************************************************************************
932  * section: hooking export
933  *****************************************************************************/
934
935 int afb_hook_flags_ditf(const char *api)
936 {
937         int flags;
938         struct afb_hook_ditf *hook;
939
940         flags = 0;
941         if (!api || afb_api_is_hookable(api)) {
942                 pthread_rwlock_rdlock(&rwlock);
943                 hook = list_of_ditf_hooks;
944                 while (hook) {
945                         if (!api || !hook->api || !strcasecmp(hook->api, api))
946                                 flags |= hook->flags;
947                         hook = hook->next;
948                 }
949                 pthread_rwlock_unlock(&rwlock);
950         }
951         return flags;
952 }
953
954 struct afb_hook_ditf *afb_hook_create_ditf(const char *api, int flags, struct afb_hook_ditf_itf *itf, void *closure)
955 {
956         struct afb_hook_ditf *hook;
957
958         /* alloc the result */
959         hook = calloc(1, sizeof *hook);
960         if (hook == NULL)
961                 return NULL;
962
963         /* get a copy of the names */
964         hook->api = api ? strdup(api) : NULL;
965         if (api && !hook->api) {
966                 free(hook);
967                 return NULL;
968         }
969
970         /* initialise the rest */
971         hook->refcount = 1;
972         hook->flags = flags;
973         hook->itf = itf ? itf : &hook_ditf_default_itf;
974         hook->closure = closure;
975
976         /* record the hook */
977         pthread_rwlock_wrlock(&rwlock);
978         hook->next = list_of_ditf_hooks;
979         list_of_ditf_hooks = hook;
980         pthread_rwlock_unlock(&rwlock);
981
982         /* returns it */
983         return hook;
984 }
985
986 struct afb_hook_ditf *afb_hook_addref_ditf(struct afb_hook_ditf *hook)
987 {
988         pthread_rwlock_wrlock(&rwlock);
989         hook->refcount++;
990         pthread_rwlock_unlock(&rwlock);
991         return hook;
992 }
993
994 void afb_hook_unref_ditf(struct afb_hook_ditf *hook)
995 {
996         struct afb_hook_ditf **prv;
997
998         if (hook) {
999                 pthread_rwlock_wrlock(&rwlock);
1000                 if (--hook->refcount)
1001                         hook = NULL;
1002                 else {
1003                         /* unlink */
1004                         prv = &list_of_ditf_hooks;
1005                         while (*prv && *prv != hook)
1006                                 prv = &(*prv)->next;
1007                         if(*prv)
1008                                 *prv = hook->next;
1009                 }
1010                 pthread_rwlock_unlock(&rwlock);
1011                 if (hook) {
1012                         /* free */
1013                         free(hook->api);
1014                         free(hook);
1015                 }
1016         }
1017 }
1018
1019 /******************************************************************************
1020  * section: default callbacks for tracing service interface (export)
1021  *****************************************************************************/
1022
1023 static void _hook_svc_(const struct afb_export *export, const char *format, ...)
1024 {
1025         va_list ap;
1026         va_start(ap, format);
1027         _hook_("export-%s", format, ap, afb_export_apiname(export));
1028         va_end(ap);
1029 }
1030
1031 static void hook_svc_start_before_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export)
1032 {
1033         _hook_svc_(export, "start.before");
1034 }
1035
1036 static void hook_svc_start_after_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export, int status)
1037 {
1038         _hook_svc_(export, "start.after -> %d", status);
1039 }
1040
1041 static void hook_svc_on_event_before_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export, const char *event, int eventid, struct json_object *object)
1042 {
1043         _hook_svc_(export, "on_event.before(%s, %d, %s)", event, eventid, json_object_to_json_string(object));
1044 }
1045
1046 static void hook_svc_on_event_after_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export, const char *event, int eventid, struct json_object *object)
1047 {
1048         _hook_svc_(export, "on_event.after(%s, %d, %s)", event, eventid, json_object_to_json_string(object));
1049 }
1050
1051 static void hook_svc_call_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export, const char *api, const char *verb, struct json_object *args)
1052 {
1053         _hook_svc_(export, "call(%s/%s, %s) ...", api, verb, json_object_to_json_string(args));
1054 }
1055
1056 static void hook_svc_call_result_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export, int status, struct json_object *result)
1057 {
1058         _hook_svc_(export, "    ...call... -> %d: %s", status, json_object_to_json_string(result));
1059 }
1060
1061 static void hook_svc_callsync_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export, const char *api, const char *verb, struct json_object *args)
1062 {
1063         _hook_svc_(export, "callsync(%s/%s, %s) ...", api, verb, json_object_to_json_string(args));
1064 }
1065
1066 static void hook_svc_callsync_result_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_export *export, int status, struct json_object *result)
1067 {
1068         _hook_svc_(export, "    ...callsync... -> %d: %s", status, json_object_to_json_string(result));
1069 }
1070
1071 static struct afb_hook_svc_itf hook_svc_default_itf = {
1072         .hook_svc_start_before = hook_svc_start_before_default_cb,
1073         .hook_svc_start_after = hook_svc_start_after_default_cb,
1074         .hook_svc_on_event_before = hook_svc_on_event_before_default_cb,
1075         .hook_svc_on_event_after = hook_svc_on_event_after_default_cb,
1076         .hook_svc_call = hook_svc_call_default_cb,
1077         .hook_svc_call_result = hook_svc_call_result_default_cb,
1078         .hook_svc_callsync = hook_svc_callsync_default_cb,
1079         .hook_svc_callsync_result = hook_svc_callsync_result_default_cb
1080 };
1081
1082 /******************************************************************************
1083  * section: hooks for tracing service interface (export)
1084  *****************************************************************************/
1085
1086 #define _HOOK_SVC_(what,...)   \
1087         struct afb_hook_svc *hook; \
1088         struct afb_hookid hookid; \
1089         const char *apiname = afb_export_apiname(export); \
1090         pthread_rwlock_rdlock(&rwlock); \
1091         init_hookid(&hookid); \
1092         hook = list_of_svc_hooks; \
1093         while (hook) { \
1094                 if (hook->itf->hook_svc_##what \
1095                  && (hook->flags & afb_hook_flag_svc_##what) != 0 \
1096                  && (!hook->api || !strcasecmp(hook->api, apiname))) { \
1097                         hook->itf->hook_svc_##what(hook->closure, &hookid, __VA_ARGS__); \
1098                 } \
1099                 hook = hook->next; \
1100         } \
1101         pthread_rwlock_unlock(&rwlock);
1102
1103 void afb_hook_svc_start_before(const struct afb_export *export)
1104 {
1105         _HOOK_SVC_(start_before, export);
1106 }
1107
1108 int afb_hook_svc_start_after(const struct afb_export *export, int status)
1109 {
1110         _HOOK_SVC_(start_after, export, status);
1111         return status;
1112 }
1113
1114 void afb_hook_svc_on_event_before(const struct afb_export *export, const char *event, int eventid, struct json_object *object)
1115 {
1116         _HOOK_SVC_(on_event_before, export, event, eventid, object);
1117 }
1118
1119 void afb_hook_svc_on_event_after(const struct afb_export *export, const char *event, int eventid, struct json_object *object)
1120 {
1121         _HOOK_SVC_(on_event_after, export, event, eventid, object);
1122 }
1123
1124 void afb_hook_svc_call(const struct afb_export *export, const char *api, const char *verb, struct json_object *args)
1125 {
1126         _HOOK_SVC_(call, export, api, verb, args);
1127 }
1128
1129 void afb_hook_svc_call_result(const struct afb_export *export, int status, struct json_object *result)
1130 {
1131         _HOOK_SVC_(call_result, export, status, result);
1132 }
1133
1134 void afb_hook_svc_callsync(const struct afb_export *export, const char *api, const char *verb, struct json_object *args)
1135 {
1136         _HOOK_SVC_(callsync, export, api, verb, args);
1137 }
1138
1139 int afb_hook_svc_callsync_result(const struct afb_export *export, int status, struct json_object *result)
1140 {
1141         _HOOK_SVC_(callsync_result, export, status, result);
1142         return status;
1143 }
1144
1145 /******************************************************************************
1146  * section: hooking services (export)
1147  *****************************************************************************/
1148
1149 int afb_hook_flags_svc(const char *api)
1150 {
1151         int flags;
1152         struct afb_hook_svc *hook;
1153
1154         flags = 0;
1155         if (!api || afb_api_is_hookable(api)) {
1156                 pthread_rwlock_rdlock(&rwlock);
1157                 hook = list_of_svc_hooks;
1158                 while (hook) {
1159                         if (!api || !hook->api || !strcasecmp(hook->api, api))
1160                                 flags |= hook->flags;
1161                         hook = hook->next;
1162                 }
1163                 pthread_rwlock_unlock(&rwlock);
1164         }
1165         return flags;
1166 }
1167
1168 struct afb_hook_svc *afb_hook_create_svc(const char *api, int flags, struct afb_hook_svc_itf *itf, void *closure)
1169 {
1170         struct afb_hook_svc *hook;
1171
1172         /* alloc the result */
1173         hook = calloc(1, sizeof *hook);
1174         if (hook == NULL)
1175                 return NULL;
1176
1177         /* get a copy of the names */
1178         hook->api = api ? strdup(api) : NULL;
1179         if (api && !hook->api) {
1180                 free(hook);
1181                 return NULL;
1182         }
1183
1184         /* initialise the rest */
1185         hook->refcount = 1;
1186         hook->flags = flags;
1187         hook->itf = itf ? itf : &hook_svc_default_itf;
1188         hook->closure = closure;
1189
1190         /* record the hook */
1191         pthread_rwlock_wrlock(&rwlock);
1192         hook->next = list_of_svc_hooks;
1193         list_of_svc_hooks = hook;
1194         pthread_rwlock_unlock(&rwlock);
1195
1196         /* returns it */
1197         return hook;
1198 }
1199
1200 struct afb_hook_svc *afb_hook_addref_svc(struct afb_hook_svc *hook)
1201 {
1202         pthread_rwlock_wrlock(&rwlock);
1203         hook->refcount++;
1204         pthread_rwlock_unlock(&rwlock);
1205         return hook;
1206 }
1207
1208 void afb_hook_unref_svc(struct afb_hook_svc *hook)
1209 {
1210         struct afb_hook_svc **prv;
1211
1212         if (hook) {
1213                 pthread_rwlock_wrlock(&rwlock);
1214                 if (--hook->refcount)
1215                         hook = NULL;
1216                 else {
1217                         /* unlink */
1218                         prv = &list_of_svc_hooks;
1219                         while (*prv && *prv != hook)
1220                                 prv = &(*prv)->next;
1221                         if(*prv)
1222                                 *prv = hook->next;
1223                 }
1224                 pthread_rwlock_unlock(&rwlock);
1225                 if (hook) {
1226                         /* free */
1227                         free(hook->api);
1228                         free(hook);
1229                 }
1230         }
1231 }
1232
1233 /******************************************************************************
1234  * section: default callbacks for tracing service interface (evt)
1235  *****************************************************************************/
1236
1237 static void _hook_evt_(const char *evt, int id, const char *format, ...)
1238 {
1239         va_list ap;
1240         va_start(ap, format);
1241         _hook_("evt-%s:%d", format, ap, evt, id);
1242         va_end(ap);
1243 }
1244
1245 static void hook_evt_create_default_cb(void *closure, const struct afb_hookid *hookid, const char *evt, int id)
1246 {
1247         _hook_evt_(evt, id, "create");
1248 }
1249
1250 static void hook_evt_push_before_default_cb(void *closure, const struct afb_hookid *hookid, const char *evt, int id, struct json_object *obj)
1251 {
1252         _hook_evt_(evt, id, "push.before(%s)", json_object_to_json_string(obj));
1253 }
1254
1255
1256 static void hook_evt_push_after_default_cb(void *closure, const struct afb_hookid *hookid, const char *evt, int id, struct json_object *obj, int result)
1257 {
1258         _hook_evt_(evt, id, "push.after(%s) -> %d", json_object_to_json_string(obj), result);
1259 }
1260
1261 static void hook_evt_broadcast_before_default_cb(void *closure, const struct afb_hookid *hookid, const char *evt, int id, struct json_object *obj)
1262 {
1263         _hook_evt_(evt, id, "broadcast.before(%s)", json_object_to_json_string(obj));
1264 }
1265
1266 static void hook_evt_broadcast_after_default_cb(void *closure, const struct afb_hookid *hookid, const char *evt, int id, struct json_object *obj, int result)
1267 {
1268         _hook_evt_(evt, id, "broadcast.after(%s) -> %d", json_object_to_json_string(obj), result);
1269 }
1270
1271 static void hook_evt_name_default_cb(void *closure, const struct afb_hookid *hookid, const char *evt, int id, const char *result)
1272 {
1273         _hook_evt_(evt, id, "name -> %s", result);
1274 }
1275
1276 static void hook_evt_addref_default_cb(void *closure, const struct afb_hookid *hookid, const char *evt, int id)
1277 {
1278         _hook_evt_(evt, id, "addref");
1279 }
1280
1281 static void hook_evt_unref_default_cb(void *closure, const struct afb_hookid *hookid, const char *evt, int id)
1282 {
1283         _hook_evt_(evt, id, "unref");
1284 }
1285
1286 static struct afb_hook_evt_itf hook_evt_default_itf = {
1287         .hook_evt_create = hook_evt_create_default_cb,
1288         .hook_evt_push_before = hook_evt_push_before_default_cb,
1289         .hook_evt_push_after = hook_evt_push_after_default_cb,
1290         .hook_evt_broadcast_before = hook_evt_broadcast_before_default_cb,
1291         .hook_evt_broadcast_after = hook_evt_broadcast_after_default_cb,
1292         .hook_evt_name = hook_evt_name_default_cb,
1293         .hook_evt_addref = hook_evt_addref_default_cb,
1294         .hook_evt_unref = hook_evt_unref_default_cb
1295 };
1296
1297 /******************************************************************************
1298  * section: hooks for tracing events interface (evt)
1299  *****************************************************************************/
1300
1301 #define _HOOK_EVT_(what,...)   \
1302         struct afb_hook_evt *hook; \
1303         struct afb_hookid hookid; \
1304         pthread_rwlock_rdlock(&rwlock); \
1305         init_hookid(&hookid); \
1306         hook = list_of_evt_hooks; \
1307         while (hook) { \
1308                 if (hook->itf->hook_evt_##what \
1309                  && (hook->flags & afb_hook_flag_evt_##what) != 0 \
1310                  && (!hook->pattern || !fnmatch(hook->pattern, evt, FNM_CASEFOLD))) { \
1311                         hook->itf->hook_evt_##what(hook->closure, &hookid, __VA_ARGS__); \
1312                 } \
1313                 hook = hook->next; \
1314         } \
1315         pthread_rwlock_unlock(&rwlock);
1316
1317 void afb_hook_evt_create(const char *evt, int id)
1318 {
1319         _HOOK_EVT_(create, evt, id);
1320 }
1321
1322 void afb_hook_evt_push_before(const char *evt, int id, struct json_object *obj)
1323 {
1324         _HOOK_EVT_(push_before, evt, id, obj);
1325 }
1326
1327 int afb_hook_evt_push_after(const char *evt, int id, struct json_object *obj, int result)
1328 {
1329         _HOOK_EVT_(push_after, evt, id, obj, result);
1330         return result;
1331 }
1332
1333 void afb_hook_evt_broadcast_before(const char *evt, int id, struct json_object *obj)
1334 {
1335         _HOOK_EVT_(broadcast_before, evt, id, obj);
1336 }
1337
1338 int afb_hook_evt_broadcast_after(const char *evt, int id, struct json_object *obj, int result)
1339 {
1340         _HOOK_EVT_(broadcast_after, evt, id, obj, result);
1341         return result;
1342 }
1343
1344 void afb_hook_evt_name(const char *evt, int id, const char *result)
1345 {
1346         _HOOK_EVT_(name, evt, id, result);
1347 }
1348
1349 void afb_hook_evt_addref(const char *evt, int id)
1350 {
1351         _HOOK_EVT_(addref, evt, id);
1352 }
1353
1354 void afb_hook_evt_unref(const char *evt, int id)
1355 {
1356         _HOOK_EVT_(unref, evt, id);
1357 }
1358
1359 /******************************************************************************
1360  * section: hooking services (evt)
1361  *****************************************************************************/
1362
1363 int afb_hook_flags_evt(const char *name)
1364 {
1365         int flags;
1366         struct afb_hook_evt *hook;
1367
1368         flags = 0;
1369         if (!name || afb_api_is_hookable(name)) {
1370                 pthread_rwlock_rdlock(&rwlock);
1371                 hook = list_of_evt_hooks;
1372                 while (hook) {
1373                         if (!name || !hook->pattern || !fnmatch(hook->pattern, name, FNM_CASEFOLD))
1374                                 flags |= hook->flags;
1375                         hook = hook->next;
1376                 }
1377                 pthread_rwlock_unlock(&rwlock);
1378         }
1379         return flags;
1380 }
1381
1382 struct afb_hook_evt *afb_hook_create_evt(const char *pattern, int flags, struct afb_hook_evt_itf *itf, void *closure)
1383 {
1384         struct afb_hook_evt *hook;
1385
1386         /* alloc the result */
1387         hook = calloc(1, sizeof *hook);
1388         if (hook == NULL)
1389                 return NULL;
1390
1391         /* get a copy of the names */
1392         hook->pattern = pattern ? strdup(pattern) : NULL;
1393         if (pattern && !hook->pattern) {
1394                 free(hook);
1395                 return NULL;
1396         }
1397
1398         /* initialise the rest */
1399         hook->refcount = 1;
1400         hook->flags = flags;
1401         hook->itf = itf ? itf : &hook_evt_default_itf;
1402         hook->closure = closure;
1403
1404         /* record the hook */
1405         pthread_rwlock_wrlock(&rwlock);
1406         hook->next = list_of_evt_hooks;
1407         list_of_evt_hooks = hook;
1408         pthread_rwlock_unlock(&rwlock);
1409
1410         /* returns it */
1411         return hook;
1412 }
1413
1414 struct afb_hook_evt *afb_hook_addref_evt(struct afb_hook_evt *hook)
1415 {
1416         pthread_rwlock_wrlock(&rwlock);
1417         hook->refcount++;
1418         pthread_rwlock_unlock(&rwlock);
1419         return hook;
1420 }
1421
1422 void afb_hook_unref_evt(struct afb_hook_evt *hook)
1423 {
1424         struct afb_hook_evt **prv;
1425
1426         if (hook) {
1427                 pthread_rwlock_wrlock(&rwlock);
1428                 if (--hook->refcount)
1429                         hook = NULL;
1430                 else {
1431                         /* unlink */
1432                         prv = &list_of_evt_hooks;
1433                         while (*prv && *prv != hook)
1434                                 prv = &(*prv)->next;
1435                         if(*prv)
1436                                 *prv = hook->next;
1437                 }
1438                 pthread_rwlock_unlock(&rwlock);
1439                 if (hook) {
1440                         /* free */
1441                         free(hook->pattern);
1442                         free(hook);
1443                 }
1444         }
1445 }
1446
1447 /******************************************************************************
1448  * section: default callbacks for sessions (session)
1449  *****************************************************************************/
1450
1451 static void _hook_session_(struct afb_session *session, const char *format, ...)
1452 {
1453         va_list ap;
1454         va_start(ap, format);
1455         _hook_("session-%s", format, ap, afb_session_uuid(session));
1456         va_end(ap);
1457 }
1458
1459 static void hook_session_create_default_cb(void *closure, const struct afb_hookid *hookid, struct afb_session *session)
1460 {
1461         _hook_session_(session, "create -> token=%s", afb_session_token(session));
1462 }
1463
1464 static void hook_session_close_default_cb(void *closure, const struct afb_hookid *hookid, struct afb_session *session)
1465 {
1466         _hook_session_(session, "close");
1467 }
1468
1469 static void hook_session_destroy_default_cb(void *closure, const struct afb_hookid *hookid, struct afb_session *session)
1470 {
1471         _hook_session_(session, "destroy");
1472 }
1473
1474 static void hook_session_renew_default_cb(void *closure, const struct afb_hookid *hookid, struct afb_session *session)
1475 {
1476         _hook_session_(session, "renew -> token=%s", afb_session_token(session));
1477 }
1478
1479 static void hook_session_addref_default_cb(void *closure, const struct afb_hookid *hookid, struct afb_session *session)
1480 {
1481         _hook_session_(session, "addref");
1482 }
1483
1484 static void hook_session_unref_default_cb(void *closure, const struct afb_hookid *hookid, struct afb_session *session)
1485 {
1486         _hook_session_(session, "unref");
1487 }
1488
1489 static struct afb_hook_session_itf hook_session_default_itf = {
1490         .hook_session_create = hook_session_create_default_cb,
1491         .hook_session_close = hook_session_close_default_cb,
1492         .hook_session_destroy = hook_session_destroy_default_cb,
1493         .hook_session_renew = hook_session_renew_default_cb,
1494         .hook_session_addref = hook_session_addref_default_cb,
1495         .hook_session_unref = hook_session_unref_default_cb
1496 };
1497
1498 /******************************************************************************
1499  * section: hooks for tracing sessions (session)
1500  *****************************************************************************/
1501
1502 #define _HOOK_SESSION_(what,...)   \
1503         struct afb_hook_session *hook; \
1504         struct afb_hookid hookid; \
1505         const char *sessid = 0; \
1506         pthread_rwlock_rdlock(&rwlock); \
1507         init_hookid(&hookid); \
1508         hook = list_of_session_hooks; \
1509         while (hook) { \
1510                 if (hook->itf->hook_session_##what \
1511                  && (hook->flags & afb_hook_flag_session_##what) != 0 \
1512                  && (!hook->pattern || !fnmatch(hook->pattern, (sessid?:(sessid=afb_session_uuid(session))), FNM_CASEFOLD))) { \
1513                         hook->itf->hook_session_##what(hook->closure, &hookid, __VA_ARGS__); \
1514                 } \
1515                 hook = hook->next; \
1516         } \
1517         pthread_rwlock_unlock(&rwlock);
1518
1519 void afb_hook_session_create(struct afb_session *session)
1520 {
1521         _HOOK_SESSION_(create, session);
1522 }
1523
1524 void afb_hook_session_close(struct afb_session *session)
1525 {
1526         _HOOK_SESSION_(close, session);
1527 }
1528
1529 void afb_hook_session_destroy(struct afb_session *session)
1530 {
1531         _HOOK_SESSION_(destroy, session);
1532 }
1533
1534 void afb_hook_session_renew(struct afb_session *session)
1535 {
1536         _HOOK_SESSION_(renew, session);
1537 }
1538
1539 void afb_hook_session_addref(struct afb_session *session)
1540 {
1541         _HOOK_SESSION_(addref, session);
1542 }
1543
1544 void afb_hook_session_unref(struct afb_session *session)
1545 {
1546         _HOOK_SESSION_(unref, session);
1547 }
1548
1549
1550 /******************************************************************************
1551  * section: hooking sessions (session)
1552  *****************************************************************************/
1553
1554 struct afb_hook_session *afb_hook_create_session(const char *pattern, int flags, struct afb_hook_session_itf *itf, void *closure)
1555 {
1556         struct afb_hook_session *hook;
1557
1558         /* alloc the result */
1559         hook = calloc(1, sizeof *hook);
1560         if (hook == NULL)
1561                 return NULL;
1562
1563         /* get a copy of the names */
1564         hook->pattern = pattern ? strdup(pattern) : NULL;
1565         if (pattern && !hook->pattern) {
1566                 free(hook);
1567                 return NULL;
1568         }
1569
1570         /* initialise the rest */
1571         hook->refcount = 1;
1572         hook->flags = flags;
1573         hook->itf = itf ? itf : &hook_session_default_itf;
1574         hook->closure = closure;
1575
1576         /* record the hook */
1577         pthread_rwlock_wrlock(&rwlock);
1578         hook->next = list_of_session_hooks;
1579         list_of_session_hooks = hook;
1580         pthread_rwlock_unlock(&rwlock);
1581
1582         /* returns it */
1583         return hook;
1584 }
1585
1586 struct afb_hook_session *afb_hook_addref_session(struct afb_hook_session *hook)
1587 {
1588         pthread_rwlock_wrlock(&rwlock);
1589         hook->refcount++;
1590         pthread_rwlock_unlock(&rwlock);
1591         return hook;
1592 }
1593
1594 void afb_hook_unref_session(struct afb_hook_session *hook)
1595 {
1596         struct afb_hook_session **prv;
1597
1598         if (hook) {
1599                 pthread_rwlock_wrlock(&rwlock);
1600                 if (--hook->refcount)
1601                         hook = NULL;
1602                 else {
1603                         /* unlink */
1604                         prv = &list_of_session_hooks;
1605                         while (*prv && *prv != hook)
1606                                 prv = &(*prv)->next;
1607                         if(*prv)
1608                                 *prv = hook->next;
1609                 }
1610                 pthread_rwlock_unlock(&rwlock);
1611                 if (hook) {
1612                         /* free */
1613                         free(hook->pattern);
1614                         free(hook);
1615                 }
1616         }
1617 }
1618
1619 /******************************************************************************
1620  * section: default callbacks for globals (global)
1621  *****************************************************************************/
1622
1623 static void _hook_global_(const char *format, ...)
1624 {
1625         va_list ap;
1626         va_start(ap, format);
1627         _hook_("global", format, ap);
1628         va_end(ap);
1629 }
1630
1631 static void hook_global_vverbose_default_cb(void *closure, const struct afb_hookid *hookid, int level, const char *file, int line, const char *func, const char *fmt, va_list args)
1632 {
1633         int len;
1634         char *msg;
1635         va_list ap;
1636
1637         va_copy(ap, args);
1638         len = vasprintf(&msg, fmt, ap);
1639         va_end(ap);
1640
1641         if (len < 0)
1642                 _hook_global_("vverbose(%d, %s, %d, %s) -> %s ? ? ?", level, file, line, func, fmt);
1643         else {
1644                 _hook_global_("vverbose(%d, %s, %d, %s) -> %s", level, file, line, func, msg);
1645                 free(msg);
1646         }
1647 }
1648
1649 static struct afb_hook_global_itf hook_global_default_itf = {
1650         .hook_global_vverbose = hook_global_vverbose_default_cb
1651 };
1652
1653 /******************************************************************************
1654  * section: hooks for tracing globals (global)
1655  *****************************************************************************/
1656
1657 #define _HOOK_GLOBAL_(what,...)   \
1658         struct afb_hook_global *hook; \
1659         struct afb_hookid hookid; \
1660         pthread_rwlock_rdlock(&rwlock); \
1661         init_hookid(&hookid); \
1662         hook = list_of_global_hooks; \
1663         while (hook) { \
1664                 if (hook->itf->hook_global_##what \
1665                  && (hook->flags & afb_hook_flag_global_##what) != 0) { \
1666                         hook->itf->hook_global_##what(hook->closure, &hookid, __VA_ARGS__); \
1667                 } \
1668                 hook = hook->next; \
1669         } \
1670         pthread_rwlock_unlock(&rwlock);
1671
1672 static void afb_hook_global_vverbose(int level, const char *file, int line, const char *func, const char *fmt, va_list args)
1673 {
1674         _HOOK_GLOBAL_(vverbose, level, file ?: "?", line, func ?: "?", fmt ?: "", args);
1675 }
1676
1677 /******************************************************************************
1678  * section: hooking globals (global)
1679  *****************************************************************************/
1680
1681 static void update_global()
1682 {
1683         struct afb_hook_global *hook;
1684         int flags = 0;
1685
1686         pthread_rwlock_rdlock(&rwlock);
1687         hook = list_of_global_hooks;
1688         while (hook) {
1689                 flags = hook->flags;
1690                 hook = hook->next;
1691         }
1692         verbose_observer = (flags & afb_hook_flag_global_vverbose) ? afb_hook_global_vverbose : NULL;
1693         pthread_rwlock_unlock(&rwlock);
1694 }
1695
1696 struct afb_hook_global *afb_hook_create_global(int flags, struct afb_hook_global_itf *itf, void *closure)
1697 {
1698         struct afb_hook_global *hook;
1699
1700         /* alloc the result */
1701         hook = calloc(1, sizeof *hook);
1702         if (hook == NULL)
1703                 return NULL;
1704
1705         /* initialise the rest */
1706         hook->refcount = 1;
1707         hook->flags = flags;
1708         hook->itf = itf ? itf : &hook_global_default_itf;
1709         hook->closure = closure;
1710
1711         /* record the hook */
1712         pthread_rwlock_wrlock(&rwlock);
1713         hook->next = list_of_global_hooks;
1714         list_of_global_hooks = hook;
1715         pthread_rwlock_unlock(&rwlock);
1716
1717         /* update hooking */
1718         update_global();
1719
1720         /* returns it */
1721         return hook;
1722 }
1723
1724 struct afb_hook_global *afb_hook_addref_global(struct afb_hook_global *hook)
1725 {
1726         pthread_rwlock_wrlock(&rwlock);
1727         hook->refcount++;
1728         pthread_rwlock_unlock(&rwlock);
1729         return hook;
1730 }
1731
1732 void afb_hook_unref_global(struct afb_hook_global *hook)
1733 {
1734         struct afb_hook_global **prv;
1735
1736         if (hook) {
1737                 pthread_rwlock_wrlock(&rwlock);
1738                 if (--hook->refcount)
1739                         hook = NULL;
1740                 else {
1741                         /* unlink */
1742                         prv = &list_of_global_hooks;
1743                         while (*prv && *prv != hook)
1744                                 prv = &(*prv)->next;
1745                         if(*prv)
1746                                 *prv = hook->next;
1747                 }
1748                 pthread_rwlock_unlock(&rwlock);
1749                 if (hook) {
1750                         /* free */
1751                         free(hook);
1752
1753                         /* update hooking */
1754                         update_global();
1755                 }
1756         }
1757 }
1758