Add hooking of daemon interface
[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
27 #include <json-c/json.h>
28
29 #include <afb/afb-req-itf.h>
30 #include <afb/afb-event-itf.h>
31
32 #include "afb-context.h"
33 #include "afb-hook.h"
34 #include "afb-session.h"
35 #include "afb-xreq.h"
36 #include "afb-ditf.h"
37 #include "verbose.h"
38
39 /**
40  * Definition of a hook for xreq
41  */
42 struct afb_hook_xreq {
43         struct afb_hook_xreq *next; /**< next hook */
44         unsigned refcount; /**< reference count */
45         char *api; /**< api hooked or NULL for any */
46         char *verb; /**< verb hooked or NULL for any */
47         struct afb_session *session; /**< session hooked or NULL if any */
48         unsigned flags; /**< hook flags */
49         struct afb_hook_xreq_itf *itf; /**< interface of hook */
50         void *closure; /**< closure for callbacks */
51 };
52
53 /**
54  * Definition of a hook for ditf
55  */
56 struct afb_hook_ditf {
57         struct afb_hook_ditf *next; /**< next hook */
58         unsigned refcount; /**< reference count */
59         char *api; /**< api hooked or NULL for any */
60         unsigned flags; /**< hook flags */
61         struct afb_hook_ditf_itf *itf; /**< interface of hook */
62         void *closure; /**< closure for callbacks */
63 };
64
65 /* synchronisation across threads */
66 static pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
67
68 /* list of hooks for xreq */
69 static struct afb_hook_xreq *list_of_xreq_hooks = NULL;
70
71 /* list of hooks for ditf */
72 static struct afb_hook_ditf *list_of_ditf_hooks = NULL;
73
74 /******************************************************************************
75  * section: default callbacks for tracing requests
76  *****************************************************************************/
77
78 static void _hook_xreq_(const struct afb_xreq *xreq, const char *format, ...)
79 {
80         int len;
81         char *buffer;
82         va_list ap;
83
84         va_start(ap, format);
85         len = vasprintf(&buffer, format, ap);
86         va_end(ap);
87
88         if (len < 0)
89                 NOTICE("hook xreq-%06d:%s/%s allocation error", xreq->hookindex, xreq->api, xreq->verb);
90         else {
91                 NOTICE("hook xreq-%06d:%s/%s %s", xreq->hookindex, xreq->api, xreq->verb, buffer);
92                 free(buffer);
93         }
94 }
95
96 static void hook_xreq_begin_default_cb(void * closure, const struct afb_xreq *xreq)
97 {
98         _hook_xreq_(xreq, "BEGIN");
99 }
100
101 static void hook_xreq_end_default_cb(void * closure, const struct afb_xreq *xreq)
102 {
103         _hook_xreq_(xreq, "END");
104 }
105
106 static void hook_xreq_json_default_cb(void * closure, const struct afb_xreq *xreq, struct json_object *obj)
107 {
108         _hook_xreq_(xreq, "json() -> %s", json_object_to_json_string(obj));
109 }
110
111 static void hook_xreq_get_default_cb(void * closure, const struct afb_xreq *xreq, const char *name, struct afb_arg arg)
112 {
113         _hook_xreq_(xreq, "get(%s) -> { name: %s, value: %s, path: %s }", name, arg.name, arg.value, arg.path);
114 }
115
116 static void hook_xreq_success_default_cb(void * closure, const struct afb_xreq *xreq, struct json_object *obj, const char *info)
117 {
118         _hook_xreq_(xreq, "success(%s, %s)", json_object_to_json_string(obj), info);
119 }
120
121 static void hook_xreq_fail_default_cb(void * closure, const struct afb_xreq *xreq, const char *status, const char *info)
122 {
123         _hook_xreq_(xreq, "fail(%s, %s)", status, info);
124 }
125
126 static void hook_xreq_raw_default_cb(void * closure, const struct afb_xreq *xreq, const char *buffer, size_t size)
127 {
128         _hook_xreq_(xreq, "raw() -> %.*s", (int)size, buffer);
129 }
130
131 static void hook_xreq_send_default_cb(void * closure, const struct afb_xreq *xreq, const char *buffer, size_t size)
132 {
133         _hook_xreq_(xreq, "send(%.*s)", (int)size, buffer);
134 }
135
136 static void hook_xreq_context_get_default_cb(void * closure, const struct afb_xreq *xreq, void *value)
137 {
138         _hook_xreq_(xreq, "context_get() -> %p", value);
139 }
140
141 static void hook_xreq_context_set_default_cb(void * closure, const struct afb_xreq *xreq, void *value, void (*free_value)(void*))
142 {
143         _hook_xreq_(xreq, "context_set(%p, %p)", value, free_value);
144 }
145
146 static void hook_xreq_addref_default_cb(void * closure, const struct afb_xreq *xreq)
147 {
148         _hook_xreq_(xreq, "addref()");
149 }
150
151 static void hook_xreq_unref_default_cb(void * closure, const struct afb_xreq *xreq)
152 {
153         _hook_xreq_(xreq, "unref()");
154 }
155
156 static void hook_xreq_session_close_default_cb(void * closure, const struct afb_xreq *xreq)
157 {
158         _hook_xreq_(xreq, "session_close()");
159 }
160
161 static void hook_xreq_session_set_LOA_default_cb(void * closure, const struct afb_xreq *xreq, unsigned level, int result)
162 {
163         _hook_xreq_(xreq, "session_set_LOA(%u) -> %d", level, result);
164 }
165
166 static void hook_xreq_subscribe_default_cb(void * closure, const struct afb_xreq *xreq, struct afb_event event, int result)
167 {
168         _hook_xreq_(xreq, "subscribe(%s:%p) -> %d", afb_event_name(event), event.closure, result);
169 }
170
171 static void hook_xreq_unsubscribe_default_cb(void * closure, const struct afb_xreq *xreq, struct afb_event event, int result)
172 {
173         _hook_xreq_(xreq, "unsubscribe(%s:%p) -> %d", afb_event_name(event), event.closure, result);
174 }
175
176 static void hook_xreq_subcall_default_cb(void * closure, const struct afb_xreq *xreq, const char *api, const char *verb, struct json_object *args)
177 {
178         _hook_xreq_(xreq, "subcall(%s/%s, %s) ...", api, verb, json_object_to_json_string(args));
179 }
180
181 static void hook_xreq_subcall_result_default_cb(void * closure, const struct afb_xreq *xreq, int status, struct json_object *result)
182 {
183         _hook_xreq_(xreq, "    ...subcall... -> %d: %s", status, json_object_to_json_string(result));
184 }
185
186 static void hook_xreq_subcallsync_default_cb(void * closure, const struct afb_xreq *xreq, const char *api, const char *verb, struct json_object *args)
187 {
188         _hook_xreq_(xreq, "subcallsync(%s/%s, %s) ...", api, verb, json_object_to_json_string(args));
189 }
190
191 static void hook_xreq_subcallsync_result_default_cb(void * closure, const struct afb_xreq *xreq, int status, struct json_object *result)
192 {
193         _hook_xreq_(xreq, "    ...subcallsync... -> %d: %s", status, json_object_to_json_string(result));
194 }
195
196 static struct afb_hook_xreq_itf hook_xreq_default_itf = {
197         .hook_xreq_begin = hook_xreq_begin_default_cb,
198         .hook_xreq_end = hook_xreq_end_default_cb,
199         .hook_xreq_json = hook_xreq_json_default_cb,
200         .hook_xreq_get = hook_xreq_get_default_cb,
201         .hook_xreq_success = hook_xreq_success_default_cb,
202         .hook_xreq_fail = hook_xreq_fail_default_cb,
203         .hook_xreq_raw = hook_xreq_raw_default_cb,
204         .hook_xreq_send = hook_xreq_send_default_cb,
205         .hook_xreq_context_get = hook_xreq_context_get_default_cb,
206         .hook_xreq_context_set = hook_xreq_context_set_default_cb,
207         .hook_xreq_addref = hook_xreq_addref_default_cb,
208         .hook_xreq_unref = hook_xreq_unref_default_cb,
209         .hook_xreq_session_close = hook_xreq_session_close_default_cb,
210         .hook_xreq_session_set_LOA = hook_xreq_session_set_LOA_default_cb,
211         .hook_xreq_subscribe = hook_xreq_subscribe_default_cb,
212         .hook_xreq_unsubscribe = hook_xreq_unsubscribe_default_cb,
213         .hook_xreq_subcall = hook_xreq_subcall_default_cb,
214         .hook_xreq_subcall_result = hook_xreq_subcall_result_default_cb,
215         .hook_xreq_subcallsync = hook_xreq_subcallsync_default_cb,
216         .hook_xreq_subcallsync_result = hook_xreq_subcallsync_result_default_cb,
217 };
218
219 /******************************************************************************
220  * section: hooks for tracing requests
221  *****************************************************************************/
222
223 #define _HOOK_XREQ_(what,...)   \
224         struct afb_hook_xreq *hook; \
225         pthread_rwlock_rdlock(&rwlock); \
226         hook = list_of_xreq_hooks; \
227         while (hook) { \
228                 if (hook->itf->hook_xreq_##what \
229                  && (hook->flags & afb_hook_flag_req_##what) != 0 \
230                  && (!hook->session || hook->session == xreq->context.session) \
231                  && (!hook->api || !strcasecmp(hook->api, xreq->api)) \
232                  && (!hook->verb || !strcasecmp(hook->verb, xreq->verb))) { \
233                         hook->itf->hook_xreq_##what(hook->closure, __VA_ARGS__); \
234                 } \
235                 hook = hook->next; \
236         } \
237         pthread_rwlock_unlock(&rwlock);
238
239
240 void afb_hook_xreq_begin(const struct afb_xreq *xreq)
241 {
242         _HOOK_XREQ_(begin, xreq);
243 }
244
245 void afb_hook_xreq_end(const struct afb_xreq *xreq)
246 {
247         _HOOK_XREQ_(end, xreq);
248 }
249
250 struct json_object *afb_hook_xreq_json(const struct afb_xreq *xreq, struct json_object *obj)
251 {
252         _HOOK_XREQ_(json, xreq, obj);
253         return obj;
254 }
255
256 struct afb_arg afb_hook_xreq_get(const struct afb_xreq *xreq, const char *name, struct afb_arg arg)
257 {
258         _HOOK_XREQ_(get, xreq, name, arg);
259         return arg;
260 }
261
262 void afb_hook_xreq_success(const struct afb_xreq *xreq, struct json_object *obj, const char *info)
263 {
264         _HOOK_XREQ_(success, xreq, obj, info);
265 }
266
267 void afb_hook_xreq_fail(const struct afb_xreq *xreq, const char *status, const char *info)
268 {
269         _HOOK_XREQ_(fail, xreq, status, info);
270 }
271
272 const char *afb_hook_xreq_raw(const struct afb_xreq *xreq, const char *buffer, size_t size)
273 {
274         _HOOK_XREQ_(raw, xreq, buffer, size);
275         return buffer;
276 }
277
278 void afb_hook_xreq_send(const struct afb_xreq *xreq, const char *buffer, size_t size)
279 {
280         _HOOK_XREQ_(send, xreq, buffer, size);
281 }
282
283 void *afb_hook_xreq_context_get(const struct afb_xreq *xreq, void *value)
284 {
285         _HOOK_XREQ_(context_get, xreq, value);
286         return value;
287 }
288
289 void afb_hook_xreq_context_set(const struct afb_xreq *xreq, void *value, void (*free_value)(void*))
290 {
291         _HOOK_XREQ_(context_set, xreq, value, free_value);
292 }
293
294 void afb_hook_xreq_addref(const struct afb_xreq *xreq)
295 {
296         _HOOK_XREQ_(addref, xreq);
297 }
298
299 void afb_hook_xreq_unref(const struct afb_xreq *xreq)
300 {
301         _HOOK_XREQ_(unref, xreq);
302 }
303
304 void afb_hook_xreq_session_close(const struct afb_xreq *xreq)
305 {
306         _HOOK_XREQ_(session_close, xreq);
307 }
308
309 int afb_hook_xreq_session_set_LOA(const struct afb_xreq *xreq, unsigned level, int result)
310 {
311         _HOOK_XREQ_(session_set_LOA, xreq, level, result);
312         return result;
313 }
314
315 int afb_hook_xreq_subscribe(const struct afb_xreq *xreq, struct afb_event event, int result)
316 {
317         _HOOK_XREQ_(subscribe, xreq, event, result);
318         return result;
319 }
320
321 int afb_hook_xreq_unsubscribe(const struct afb_xreq *xreq, struct afb_event event, int result)
322 {
323         _HOOK_XREQ_(unsubscribe, xreq, event, result);
324         return result;
325 }
326
327 void afb_hook_xreq_subcall(const struct afb_xreq *xreq, const char *api, const char *verb, struct json_object *args)
328 {
329         _HOOK_XREQ_(subcall, xreq, api, verb, args);
330 }
331
332 void afb_hook_xreq_subcall_result(const struct afb_xreq *xreq, int status, struct json_object *result)
333 {
334         _HOOK_XREQ_(subcall_result, xreq, status, result);
335 }
336
337 void afb_hook_xreq_subcallsync(const struct afb_xreq *xreq, const char *api, const char *verb, struct json_object *args)
338 {
339         _HOOK_XREQ_(subcallsync, xreq, api, verb, args);
340 }
341
342 int afb_hook_xreq_subcallsync_result(const struct afb_xreq *xreq, int status, struct json_object *result)
343 {
344         _HOOK_XREQ_(subcallsync_result, xreq, status, result);
345         return status;
346 }
347
348 /******************************************************************************
349  * section: 
350  *****************************************************************************/
351
352 void afb_hook_init_xreq(struct afb_xreq *xreq)
353 {
354         static int reqindex;
355
356         int f, flags;
357         int add;
358         struct afb_hook_xreq *hook;
359
360         /* scan hook list to get the expected flags */
361         flags = 0;
362         pthread_rwlock_rdlock(&rwlock);
363         hook = list_of_xreq_hooks;
364         while (hook) {
365                 f = hook->flags & afb_hook_flags_req_all;
366                 add = f != 0
367                    && (!hook->session || hook->session == xreq->context.session)
368                    && (!hook->api || !strcasecmp(hook->api, xreq->api))
369                    && (!hook->verb || !strcasecmp(hook->verb, xreq->verb));
370                 if (add)
371                         flags |= f;
372                 hook = hook->next;
373         }
374         pthread_rwlock_unlock(&rwlock);
375
376         /* store the hooking data */
377         xreq->hookflags = flags;
378         if (flags) {
379                 pthread_rwlock_wrlock(&rwlock);
380                 if (++reqindex < 0)
381                         reqindex = 1;
382                 xreq->hookindex = reqindex;
383                 pthread_rwlock_unlock(&rwlock);
384         }
385 }
386
387 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)
388 {
389         struct afb_hook_xreq *hook;
390
391         /* alloc the result */
392         hook = calloc(1, sizeof *hook);
393         if (hook == NULL)
394                 return NULL;
395
396         /* get a copy of the names */
397         hook->api = api ? strdup(api) : NULL;
398         hook->verb = verb ? strdup(verb) : NULL;
399         if ((api && !hook->api) || (verb && !hook->verb)) {
400                 free(hook->api);
401                 free(hook->verb);
402                 free(hook);
403                 return NULL;
404         }
405
406         /* initialise the rest */
407         hook->session = session;
408         if (session)
409                 afb_session_addref(session);
410         hook->refcount = 1;
411         hook->flags = flags;
412         hook->itf = itf ? itf : &hook_xreq_default_itf;
413         hook->closure = closure;
414
415         /* record the hook */
416         pthread_rwlock_wrlock(&rwlock);
417         hook->next = list_of_xreq_hooks;
418         list_of_xreq_hooks = hook;
419         pthread_rwlock_unlock(&rwlock);
420
421         /* returns it */
422         return hook;
423 }
424
425 struct afb_hook_xreq *afb_hook_addref_xreq(struct afb_hook_xreq *hook)
426 {
427         pthread_rwlock_wrlock(&rwlock);
428         hook->refcount++;
429         pthread_rwlock_unlock(&rwlock);
430         return hook;
431 }
432
433 void afb_hook_unref_xreq(struct afb_hook_xreq *hook)
434 {
435         struct afb_hook_xreq **prv;
436
437         if (hook) {
438                 pthread_rwlock_wrlock(&rwlock);
439                 if (--hook->refcount)
440                         hook = NULL;
441                 else {
442                         /* unlink */
443                         prv = &list_of_xreq_hooks;
444                         while (*prv && *prv != hook)
445                                 prv = &(*prv)->next;
446                         if(*prv)
447                                 *prv = hook->next;
448                 }
449                 pthread_rwlock_unlock(&rwlock);
450                 if (hook) {
451                         /* free */
452                         free(hook->api);
453                         free(hook->verb);
454                         if (hook->session)
455                                 afb_session_unref(hook->session);
456                         free(hook);
457                 }
458         }
459 }
460
461 /******************************************************************************
462  * section: default callbacks for tracing daemon interface
463  *****************************************************************************/
464
465 static void _hook_ditf_(const struct afb_ditf *ditf, const char *format, ...)
466 {
467         int len;
468         char *buffer;
469         va_list ap;
470
471         va_start(ap, format);
472         len = vasprintf(&buffer, format, ap);
473         va_end(ap);
474
475         if (len < 0)
476                 NOTICE("hook ditf-%s allocation error for %s", ditf->prefix, format);
477         else {
478                 NOTICE("hook ditf-%s %s", ditf->prefix, buffer);
479                 free(buffer);
480         }
481 }
482
483 static void hook_ditf_event_broadcast_before_cb(void *closure, const struct afb_ditf *ditf, const char *name, struct json_object *object)
484 {
485         _hook_ditf_(ditf, "event_broadcast.before(%s, %s)....", name, json_object_to_json_string(object));
486 }
487
488 static void hook_ditf_event_broadcast_after_cb(void *closure, const struct afb_ditf *ditf, const char *name, struct json_object *object, int result)
489 {
490         _hook_ditf_(ditf, "event_broadcast.after(%s, %s) -> %d", name, json_object_to_json_string(object), result);
491 }
492
493 static void hook_ditf_get_event_loop_cb(void *closure, const struct afb_ditf *ditf, struct sd_event *result)
494 {
495         _hook_ditf_(ditf, "get_event_loop() -> %p", result);
496 }
497
498 static void hook_ditf_get_user_bus_cb(void *closure, const struct afb_ditf *ditf, struct sd_bus *result)
499 {
500         _hook_ditf_(ditf, "get_user_bus() -> %p", result);
501 }
502
503 static void hook_ditf_get_system_bus_cb(void *closure, const struct afb_ditf *ditf, struct sd_bus *result)
504 {
505         _hook_ditf_(ditf, "get_system_bus() -> %p", result);
506 }
507
508 static void hook_ditf_vverbose_cb(void*closure, const struct afb_ditf *ditf, int level, const char *file, int line, const char *function, const char *fmt, va_list args)
509 {
510         int len;
511         char *msg;
512         va_list ap;
513
514         va_copy(ap, args);
515         len = vasprintf(&msg, fmt, ap);
516         va_end(ap);
517
518         if (len < 0)
519                 _hook_ditf_(ditf, "vverbose(%d, %s, %d, %s) -> %s ? ? ?", level, file, line, function, fmt);
520         else {
521                 _hook_ditf_(ditf, "vverbose(%d, %s, %d, %s) -> %s", level, file, line, function, msg);
522                 free(msg);
523         }
524 }
525
526 static void hook_ditf_event_make_cb(void *closure, const struct afb_ditf *ditf, const char *name, struct afb_event result)
527 {
528         _hook_ditf_(ditf, "event_make(%s) -> %s:%p", name, afb_event_name(result), result.closure);
529 }
530
531 static void hook_ditf_rootdir_get_fd_cb(void *closure, const struct afb_ditf *ditf, int result)
532 {
533         char path[PATH_MAX];
534         if (result < 0)
535                 _hook_ditf_(ditf, "rootdir_get_fd() -> %d, %m", result);
536         else {
537                 sprintf(path, "/proc/self/fd/%d", result);
538                 readlink(path, path, sizeof path);
539                 _hook_ditf_(ditf, "rootdir_get_fd() -> %d = %s", result, path);
540         }
541 }
542
543 static void hook_ditf_rootdir_open_locale_cb(void *closure, const struct afb_ditf *ditf, const char *filename, int flags, const char *locale, int result)
544 {
545         char path[PATH_MAX];
546         if (!locale)
547                 locale = "(null)";
548         if (result < 0)
549                 _hook_ditf_(ditf, "rootdir_open_locale(%s, %d, %s) -> %d, %m", filename, flags, locale, result);
550         else {
551                 sprintf(path, "/proc/self/fd/%d", result);
552                 readlink(path, path, sizeof path);
553                 _hook_ditf_(ditf, "rootdir_open_locale(%s, %d, %s) -> %d = %s", filename, flags, locale, result, path);
554         }
555 }
556
557
558 static struct afb_hook_ditf_itf hook_ditf_default_itf = {
559         .hook_ditf_event_broadcast_before = hook_ditf_event_broadcast_before_cb,
560         .hook_ditf_event_broadcast_after = hook_ditf_event_broadcast_after_cb,
561         .hook_ditf_get_event_loop = hook_ditf_get_event_loop_cb,
562         .hook_ditf_get_user_bus = hook_ditf_get_user_bus_cb,
563         .hook_ditf_get_system_bus = hook_ditf_get_system_bus_cb,
564         .hook_ditf_vverbose = hook_ditf_vverbose_cb,
565         .hook_ditf_event_make = hook_ditf_event_make_cb,
566         .hook_ditf_rootdir_get_fd = hook_ditf_rootdir_get_fd_cb,
567         .hook_ditf_rootdir_open_locale = hook_ditf_rootdir_open_locale_cb,
568 };
569
570 /******************************************************************************
571  * section: hooks for tracing requests
572  *****************************************************************************/
573
574 #define _HOOK_DITF_(what,...)   \
575         struct afb_hook_ditf *hook; \
576         pthread_rwlock_rdlock(&rwlock); \
577         hook = list_of_ditf_hooks; \
578         while (hook) { \
579                 if (hook->itf->hook_ditf_##what \
580                  && (hook->flags & afb_hook_flag_ditf_##what) != 0 \
581                  && (!hook->api || !strcasecmp(hook->api, ditf->prefix))) { \
582                         hook->itf->hook_ditf_##what(hook->closure, __VA_ARGS__); \
583                 } \
584                 hook = hook->next; \
585         } \
586         pthread_rwlock_unlock(&rwlock);
587
588 void afb_hook_ditf_event_broadcast_before(const struct afb_ditf *ditf, const char *name, struct json_object *object)
589 {
590         _HOOK_DITF_(event_broadcast_before, ditf, name, object);
591 }
592
593 int afb_hook_ditf_event_broadcast_after(const struct afb_ditf *ditf, const char *name, struct json_object *object, int result)
594 {
595         _HOOK_DITF_(event_broadcast_after, ditf, name, object, result);
596         return result;
597 }
598
599 struct sd_event *afb_hook_ditf_get_event_loop(const struct afb_ditf *ditf, struct sd_event *result)
600 {
601         _HOOK_DITF_(get_event_loop, ditf, result);
602         return result;
603 }
604
605 struct sd_bus *afb_hook_ditf_get_user_bus(const struct afb_ditf *ditf, struct sd_bus *result)
606 {
607         _HOOK_DITF_(get_user_bus, ditf, result);
608         return result;
609 }
610
611 struct sd_bus *afb_hook_ditf_get_system_bus(const struct afb_ditf *ditf, struct sd_bus *result)
612 {
613         _HOOK_DITF_(get_system_bus, ditf, result);
614         return result;
615 }
616
617 void afb_hook_ditf_vverbose(const struct afb_ditf *ditf, int level, const char *file, int line, const char *function, const char *fmt, va_list args)
618 {
619         _HOOK_DITF_(vverbose, ditf, level, file, line, function, fmt, args);
620 }
621
622 struct afb_event afb_hook_ditf_event_make(const struct afb_ditf *ditf, const char *name, struct afb_event result)
623 {
624         _HOOK_DITF_(event_make, ditf, name, result);
625         return result;
626 }
627
628 int afb_hook_ditf_rootdir_get_fd(const struct afb_ditf *ditf, int result)
629 {
630         _HOOK_DITF_(rootdir_get_fd, ditf, result);
631         return result;
632 }
633
634 int afb_hook_ditf_rootdir_open_locale(const struct afb_ditf *ditf, const char *filename, int flags, const char *locale, int result)
635 {
636         _HOOK_DITF_(rootdir_open_locale, ditf, filename, flags, locale, result);
637         return result;
638 }
639
640
641
642 /******************************************************************************
643  * section: 
644  *****************************************************************************/
645
646 int afb_hook_flags_ditf(const char *api)
647 {
648         int flags;
649         struct afb_hook_ditf *hook;
650
651         pthread_rwlock_rdlock(&rwlock);
652         flags = 0;
653         hook = list_of_ditf_hooks;
654         while (hook) {
655                 if (!api || !hook->api || !strcasecmp(hook->api, api))
656                         flags |= hook->flags;
657                 hook = hook->next;
658         }
659         pthread_rwlock_unlock(&rwlock);
660         return flags;
661 }
662
663 struct afb_hook_ditf *afb_hook_create_ditf(const char *api, int flags, struct afb_hook_ditf_itf *itf, void *closure)
664 {
665         struct afb_hook_ditf *hook;
666
667         /* alloc the result */
668         hook = calloc(1, sizeof *hook);
669         if (hook == NULL)
670                 return NULL;
671
672         /* get a copy of the names */
673         hook->api = api ? strdup(api) : NULL;
674         if (api && !hook->api) {
675                 free(hook);
676                 return NULL;
677         }
678
679         /* initialise the rest */
680         hook->refcount = 1;
681         hook->flags = flags;
682         hook->itf = itf ? itf : &hook_ditf_default_itf;
683         hook->closure = closure;
684
685         /* record the hook */
686         pthread_rwlock_wrlock(&rwlock);
687         hook->next = list_of_ditf_hooks;
688         list_of_ditf_hooks = hook;
689         pthread_rwlock_unlock(&rwlock);
690
691         /* returns it */
692         return hook;
693 }
694
695 struct afb_hook_ditf *afb_hook_addref_ditf(struct afb_hook_ditf *hook)
696 {
697         pthread_rwlock_wrlock(&rwlock);
698         hook->refcount++;
699         pthread_rwlock_unlock(&rwlock);
700         return hook;
701 }
702
703 void afb_hook_unref_ditf(struct afb_hook_ditf *hook)
704 {
705         struct afb_hook_ditf **prv;
706
707         if (hook) {
708                 pthread_rwlock_wrlock(&rwlock);
709                 if (--hook->refcount)
710                         hook = NULL;
711                 else {
712                         /* unlink */
713                         prv = &list_of_ditf_hooks;
714                         while (*prv && *prv != hook)
715                                 prv = &(*prv)->next;
716                         if(*prv)
717                                 *prv = hook->next;
718                 }
719                 pthread_rwlock_unlock(&rwlock);
720                 if (hook) {
721                         /* free */
722                         free(hook->api);
723                         free(hook);
724                 }
725         }
726 }
727