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