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