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