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