6afcd2fb68c53616913631ba73b46e1281a3dc47
[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 "afb-svc.h"
39 #include "verbose.h"
40
41 /**
42  * Definition of a hook for xreq
43  */
44 struct afb_hook_xreq {
45         struct afb_hook_xreq *next; /**< next hook */
46         unsigned refcount; /**< reference count */
47         char *api; /**< api hooked or NULL for any */
48         char *verb; /**< verb hooked or NULL for any */
49         struct afb_session *session; /**< session hooked or NULL if any */
50         unsigned flags; /**< hook flags */
51         struct afb_hook_xreq_itf *itf; /**< interface of hook */
52         void *closure; /**< closure for callbacks */
53 };
54
55 /**
56  * Definition of a hook for ditf
57  */
58 struct afb_hook_ditf {
59         struct afb_hook_ditf *next; /**< next hook */
60         unsigned refcount; /**< reference count */
61         char *api; /**< api hooked or NULL for any */
62         unsigned flags; /**< hook flags */
63         struct afb_hook_ditf_itf *itf; /**< interface of hook */
64         void *closure; /**< closure for callbacks */
65 };
66
67 /**
68  * Definition of a hook for svc
69  */
70 struct afb_hook_svc {
71         struct afb_hook_svc *next; /**< next hook */
72         unsigned refcount; /**< reference count */
73         char *api; /**< api hooked or NULL for any */
74         unsigned flags; /**< hook flags */
75         struct afb_hook_svc_itf *itf; /**< interface of hook */
76         void *closure; /**< closure for callbacks */
77 };
78
79 /* synchronisation across threads */
80 static pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
81
82 /* list of hooks for xreq */
83 static struct afb_hook_xreq *list_of_xreq_hooks = NULL;
84
85 /* list of hooks for ditf */
86 static struct afb_hook_ditf *list_of_ditf_hooks = NULL;
87
88 /* list of hooks for svc */
89 static struct afb_hook_svc *list_of_svc_hooks = NULL;
90
91 /******************************************************************************
92  * section: default callbacks for tracing requests
93  *****************************************************************************/
94
95 static void _hook_xreq_(const struct afb_xreq *xreq, const char *format, ...)
96 {
97         int len;
98         char *buffer;
99         va_list ap;
100
101         va_start(ap, format);
102         len = vasprintf(&buffer, format, ap);
103         va_end(ap);
104
105         if (len < 0)
106                 NOTICE("hook xreq-%06d:%s/%s allocation error", xreq->hookindex, xreq->api, xreq->verb);
107         else {
108                 NOTICE("hook xreq-%06d:%s/%s %s", xreq->hookindex, xreq->api, xreq->verb, buffer);
109                 free(buffer);
110         }
111 }
112
113 static void hook_xreq_begin_default_cb(void * closure, const struct afb_xreq *xreq)
114 {
115         if (!xreq->cred)
116                 _hook_xreq_(xreq, "BEGIN");
117         else
118                 _hook_xreq_(xreq, "BEGIN uid=%d=%s gid=%d pid=%d label=%s id=%s",
119                         (int)xreq->cred->uid,
120                         xreq->cred->user,
121                         (int)xreq->cred->gid,
122                         (int)xreq->cred->pid,
123                         xreq->cred->label?:"(null)",
124                         xreq->cred->id?:"(null)"
125                 );
126 }
127
128 static void hook_xreq_end_default_cb(void * closure, const struct afb_xreq *xreq)
129 {
130         _hook_xreq_(xreq, "END");
131 }
132
133 static void hook_xreq_json_default_cb(void * closure, const struct afb_xreq *xreq, struct json_object *obj)
134 {
135         _hook_xreq_(xreq, "json() -> %s", json_object_to_json_string(obj));
136 }
137
138 static void hook_xreq_get_default_cb(void * closure, const struct afb_xreq *xreq, const char *name, struct afb_arg arg)
139 {
140         _hook_xreq_(xreq, "get(%s) -> { name: %s, value: %s, path: %s }", name, arg.name, arg.value, arg.path);
141 }
142
143 static void hook_xreq_success_default_cb(void * closure, const struct afb_xreq *xreq, struct json_object *obj, const char *info)
144 {
145         _hook_xreq_(xreq, "success(%s, %s)", json_object_to_json_string(obj), info);
146 }
147
148 static void hook_xreq_fail_default_cb(void * closure, const struct afb_xreq *xreq, const char *status, const char *info)
149 {
150         _hook_xreq_(xreq, "fail(%s, %s)", status, info);
151 }
152
153 static void hook_xreq_context_get_default_cb(void * closure, const struct afb_xreq *xreq, void *value)
154 {
155         _hook_xreq_(xreq, "context_get() -> %p", value);
156 }
157
158 static void hook_xreq_context_set_default_cb(void * closure, const struct afb_xreq *xreq, void *value, void (*free_value)(void*))
159 {
160         _hook_xreq_(xreq, "context_set(%p, %p)", value, free_value);
161 }
162
163 static void hook_xreq_addref_default_cb(void * closure, const struct afb_xreq *xreq)
164 {
165         _hook_xreq_(xreq, "addref()");
166 }
167
168 static void hook_xreq_unref_default_cb(void * closure, const struct afb_xreq *xreq)
169 {
170         _hook_xreq_(xreq, "unref()");
171 }
172
173 static void hook_xreq_session_close_default_cb(void * closure, const struct afb_xreq *xreq)
174 {
175         _hook_xreq_(xreq, "session_close()");
176 }
177
178 static void hook_xreq_session_set_LOA_default_cb(void * closure, const struct afb_xreq *xreq, unsigned level, int result)
179 {
180         _hook_xreq_(xreq, "session_set_LOA(%u) -> %d", level, result);
181 }
182
183 static void hook_xreq_subscribe_default_cb(void * closure, const struct afb_xreq *xreq, struct afb_event event, int result)
184 {
185         _hook_xreq_(xreq, "subscribe(%s:%p) -> %d", afb_event_name(event), event.closure, result);
186 }
187
188 static void hook_xreq_unsubscribe_default_cb(void * closure, const struct afb_xreq *xreq, struct afb_event event, int result)
189 {
190         _hook_xreq_(xreq, "unsubscribe(%s:%p) -> %d", afb_event_name(event), event.closure, result);
191 }
192
193 static void hook_xreq_subcall_default_cb(void * closure, const struct afb_xreq *xreq, const char *api, const char *verb, struct json_object *args)
194 {
195         _hook_xreq_(xreq, "subcall(%s/%s, %s) ...", api, verb, json_object_to_json_string(args));
196 }
197
198 static void hook_xreq_subcall_result_default_cb(void * closure, const struct afb_xreq *xreq, int status, struct json_object *result)
199 {
200         _hook_xreq_(xreq, "    ...subcall... -> %d: %s", status, json_object_to_json_string(result));
201 }
202
203 static void hook_xreq_subcallsync_default_cb(void * closure, const struct afb_xreq *xreq, const char *api, const char *verb, struct json_object *args)
204 {
205         _hook_xreq_(xreq, "subcallsync(%s/%s, %s) ...", api, verb, json_object_to_json_string(args));
206 }
207
208 static void hook_xreq_subcallsync_result_default_cb(void * closure, const struct afb_xreq *xreq, int status, struct json_object *result)
209 {
210         _hook_xreq_(xreq, "    ...subcallsync... -> %d: %s", status, json_object_to_json_string(result));
211 }
212
213 static void hook_xreq_vverbose_default_cb(void * closure, const struct afb_xreq *xreq, int level, const char *file, int line, const char *func, const char *fmt, va_list args)
214 {
215         int len;
216         char *msg;
217         va_list ap;
218
219         va_copy(ap, args);
220         len = vasprintf(&msg, fmt, ap);
221         va_end(ap);
222
223         if (len < 0)
224                 _hook_xreq_(xreq, "vverbose(%d, %s, %d, %s) -> %s ? ? ?", level, file, line, func, fmt);
225         else {
226                 _hook_xreq_(xreq, "vverbose(%d, %s, %d, %s) -> %s", level, file, line, func, msg);
227                 free(msg);
228         }
229 }
230
231 static struct afb_hook_xreq_itf hook_xreq_default_itf = {
232         .hook_xreq_begin = hook_xreq_begin_default_cb,
233         .hook_xreq_end = hook_xreq_end_default_cb,
234         .hook_xreq_json = hook_xreq_json_default_cb,
235         .hook_xreq_get = hook_xreq_get_default_cb,
236         .hook_xreq_success = hook_xreq_success_default_cb,
237         .hook_xreq_fail = hook_xreq_fail_default_cb,
238         .hook_xreq_context_get = hook_xreq_context_get_default_cb,
239         .hook_xreq_context_set = hook_xreq_context_set_default_cb,
240         .hook_xreq_addref = hook_xreq_addref_default_cb,
241         .hook_xreq_unref = hook_xreq_unref_default_cb,
242         .hook_xreq_session_close = hook_xreq_session_close_default_cb,
243         .hook_xreq_session_set_LOA = hook_xreq_session_set_LOA_default_cb,
244         .hook_xreq_subscribe = hook_xreq_subscribe_default_cb,
245         .hook_xreq_unsubscribe = hook_xreq_unsubscribe_default_cb,
246         .hook_xreq_subcall = hook_xreq_subcall_default_cb,
247         .hook_xreq_subcall_result = hook_xreq_subcall_result_default_cb,
248         .hook_xreq_subcallsync = hook_xreq_subcallsync_default_cb,
249         .hook_xreq_subcallsync_result = hook_xreq_subcallsync_result_default_cb,
250         .hook_xreq_vverbose = hook_xreq_vverbose_default_cb
251 };
252
253 /******************************************************************************
254  * section: hooks for tracing requests
255  *****************************************************************************/
256
257 #define _HOOK_XREQ_(what,...)   \
258         struct afb_hook_xreq *hook; \
259         pthread_rwlock_rdlock(&rwlock); \
260         hook = list_of_xreq_hooks; \
261         while (hook) { \
262                 if (hook->itf->hook_xreq_##what \
263                  && (hook->flags & afb_hook_flag_req_##what) != 0 \
264                  && (!hook->session || hook->session == xreq->context.session) \
265                  && (!hook->api || !strcasecmp(hook->api, xreq->api)) \
266                  && (!hook->verb || !strcasecmp(hook->verb, xreq->verb))) { \
267                         hook->itf->hook_xreq_##what(hook->closure, __VA_ARGS__); \
268                 } \
269                 hook = hook->next; \
270         } \
271         pthread_rwlock_unlock(&rwlock);
272
273
274 void afb_hook_xreq_begin(const struct afb_xreq *xreq)
275 {
276         _HOOK_XREQ_(begin, xreq);
277 }
278
279 void afb_hook_xreq_end(const struct afb_xreq *xreq)
280 {
281         _HOOK_XREQ_(end, xreq);
282 }
283
284 struct json_object *afb_hook_xreq_json(const struct afb_xreq *xreq, struct json_object *obj)
285 {
286         _HOOK_XREQ_(json, xreq, obj);
287         return obj;
288 }
289
290 struct afb_arg afb_hook_xreq_get(const struct afb_xreq *xreq, const char *name, struct afb_arg arg)
291 {
292         _HOOK_XREQ_(get, xreq, name, arg);
293         return arg;
294 }
295
296 void afb_hook_xreq_success(const struct afb_xreq *xreq, struct json_object *obj, const char *info)
297 {
298         _HOOK_XREQ_(success, xreq, obj, info);
299 }
300
301 void afb_hook_xreq_fail(const struct afb_xreq *xreq, const char *status, const char *info)
302 {
303         _HOOK_XREQ_(fail, xreq, status, info);
304 }
305
306 void *afb_hook_xreq_context_get(const struct afb_xreq *xreq, void *value)
307 {
308         _HOOK_XREQ_(context_get, xreq, value);
309         return value;
310 }
311
312 void afb_hook_xreq_context_set(const struct afb_xreq *xreq, void *value, void (*free_value)(void*))
313 {
314         _HOOK_XREQ_(context_set, xreq, value, free_value);
315 }
316
317 void afb_hook_xreq_addref(const struct afb_xreq *xreq)
318 {
319         _HOOK_XREQ_(addref, xreq);
320 }
321
322 void afb_hook_xreq_unref(const struct afb_xreq *xreq)
323 {
324         _HOOK_XREQ_(unref, xreq);
325 }
326
327 void afb_hook_xreq_session_close(const struct afb_xreq *xreq)
328 {
329         _HOOK_XREQ_(session_close, xreq);
330 }
331
332 int afb_hook_xreq_session_set_LOA(const struct afb_xreq *xreq, unsigned level, int result)
333 {
334         _HOOK_XREQ_(session_set_LOA, xreq, level, result);
335         return result;
336 }
337
338 int afb_hook_xreq_subscribe(const struct afb_xreq *xreq, struct afb_event event, int result)
339 {
340         _HOOK_XREQ_(subscribe, xreq, event, result);
341         return result;
342 }
343
344 int afb_hook_xreq_unsubscribe(const struct afb_xreq *xreq, struct afb_event event, int result)
345 {
346         _HOOK_XREQ_(unsubscribe, xreq, event, result);
347         return result;
348 }
349
350 void afb_hook_xreq_subcall(const struct afb_xreq *xreq, const char *api, const char *verb, struct json_object *args)
351 {
352         _HOOK_XREQ_(subcall, xreq, api, verb, args);
353 }
354
355 void afb_hook_xreq_subcall_result(const struct afb_xreq *xreq, int status, struct json_object *result)
356 {
357         _HOOK_XREQ_(subcall_result, xreq, status, result);
358 }
359
360 void afb_hook_xreq_subcallsync(const struct afb_xreq *xreq, const char *api, const char *verb, struct json_object *args)
361 {
362         _HOOK_XREQ_(subcallsync, xreq, api, verb, args);
363 }
364
365 int afb_hook_xreq_subcallsync_result(const struct afb_xreq *xreq, int status, struct json_object *result)
366 {
367         _HOOK_XREQ_(subcallsync_result, xreq, status, result);
368         return status;
369 }
370
371 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)
372 {
373         _HOOK_XREQ_(vverbose, xreq, level, file ?: "?", line, func ?: "?", fmt, args);
374 }
375
376 /******************************************************************************
377  * section: hooking xreqs
378  *****************************************************************************/
379
380 void afb_hook_init_xreq(struct afb_xreq *xreq)
381 {
382         static int reqindex;
383
384         int f, flags;
385         int add;
386         struct afb_hook_xreq *hook;
387
388         /* scan hook list to get the expected flags */
389         flags = 0;
390         pthread_rwlock_rdlock(&rwlock);
391         hook = list_of_xreq_hooks;
392         while (hook) {
393                 f = hook->flags & afb_hook_flags_req_all;
394                 add = f != 0
395                    && (!hook->session || hook->session == xreq->context.session)
396                    && (!hook->api || !strcasecmp(hook->api, xreq->api))
397                    && (!hook->verb || !strcasecmp(hook->verb, xreq->verb));
398                 if (add)
399                         flags |= f;
400                 hook = hook->next;
401         }
402         pthread_rwlock_unlock(&rwlock);
403
404         /* store the hooking data */
405         xreq->hookflags = flags;
406         if (flags) {
407                 pthread_rwlock_wrlock(&rwlock);
408                 if (++reqindex < 0)
409                         reqindex = 1;
410                 xreq->hookindex = reqindex;
411                 pthread_rwlock_unlock(&rwlock);
412         }
413 }
414
415 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)
416 {
417         struct afb_hook_xreq *hook;
418
419         /* alloc the result */
420         hook = calloc(1, sizeof *hook);
421         if (hook == NULL)
422                 return NULL;
423
424         /* get a copy of the names */
425         hook->api = api ? strdup(api) : NULL;
426         hook->verb = verb ? strdup(verb) : NULL;
427         if ((api && !hook->api) || (verb && !hook->verb)) {
428                 free(hook->api);
429                 free(hook->verb);
430                 free(hook);
431                 return NULL;
432         }
433
434         /* initialise the rest */
435         hook->session = session;
436         if (session)
437                 afb_session_addref(session);
438         hook->refcount = 1;
439         hook->flags = flags;
440         hook->itf = itf ? itf : &hook_xreq_default_itf;
441         hook->closure = closure;
442
443         /* record the hook */
444         pthread_rwlock_wrlock(&rwlock);
445         hook->next = list_of_xreq_hooks;
446         list_of_xreq_hooks = hook;
447         pthread_rwlock_unlock(&rwlock);
448
449         /* returns it */
450         return hook;
451 }
452
453 struct afb_hook_xreq *afb_hook_addref_xreq(struct afb_hook_xreq *hook)
454 {
455         pthread_rwlock_wrlock(&rwlock);
456         hook->refcount++;
457         pthread_rwlock_unlock(&rwlock);
458         return hook;
459 }
460
461 void afb_hook_unref_xreq(struct afb_hook_xreq *hook)
462 {
463         struct afb_hook_xreq **prv;
464
465         if (hook) {
466                 pthread_rwlock_wrlock(&rwlock);
467                 if (--hook->refcount)
468                         hook = NULL;
469                 else {
470                         /* unlink */
471                         prv = &list_of_xreq_hooks;
472                         while (*prv && *prv != hook)
473                                 prv = &(*prv)->next;
474                         if(*prv)
475                                 *prv = hook->next;
476                 }
477                 pthread_rwlock_unlock(&rwlock);
478                 if (hook) {
479                         /* free */
480                         free(hook->api);
481                         free(hook->verb);
482                         if (hook->session)
483                                 afb_session_unref(hook->session);
484                         free(hook);
485                 }
486         }
487 }
488
489 /******************************************************************************
490  * section: default callbacks for tracing daemon interface
491  *****************************************************************************/
492
493 static void _hook_ditf_(const struct afb_ditf *ditf, const char *format, ...)
494 {
495         int len;
496         char *buffer;
497         va_list ap;
498
499         va_start(ap, format);
500         len = vasprintf(&buffer, format, ap);
501         va_end(ap);
502
503         if (len < 0)
504                 NOTICE("hook ditf-%s allocation error for %s", ditf->prefix, format);
505         else {
506                 NOTICE("hook ditf-%s %s", ditf->prefix, buffer);
507                 free(buffer);
508         }
509 }
510
511 static void hook_ditf_event_broadcast_before_cb(void *closure, const struct afb_ditf *ditf, const char *name, struct json_object *object)
512 {
513         _hook_ditf_(ditf, "event_broadcast.before(%s, %s)....", name, json_object_to_json_string(object));
514 }
515
516 static void hook_ditf_event_broadcast_after_cb(void *closure, const struct afb_ditf *ditf, const char *name, struct json_object *object, int result)
517 {
518         _hook_ditf_(ditf, "event_broadcast.after(%s, %s) -> %d", name, json_object_to_json_string(object), result);
519 }
520
521 static void hook_ditf_get_event_loop_cb(void *closure, const struct afb_ditf *ditf, struct sd_event *result)
522 {
523         _hook_ditf_(ditf, "get_event_loop() -> %p", result);
524 }
525
526 static void hook_ditf_get_user_bus_cb(void *closure, const struct afb_ditf *ditf, struct sd_bus *result)
527 {
528         _hook_ditf_(ditf, "get_user_bus() -> %p", result);
529 }
530
531 static void hook_ditf_get_system_bus_cb(void *closure, const struct afb_ditf *ditf, struct sd_bus *result)
532 {
533         _hook_ditf_(ditf, "get_system_bus() -> %p", result);
534 }
535
536 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)
537 {
538         int len;
539         char *msg;
540         va_list ap;
541
542         va_copy(ap, args);
543         len = vasprintf(&msg, fmt, ap);
544         va_end(ap);
545
546         if (len < 0)
547                 _hook_ditf_(ditf, "vverbose(%d, %s, %d, %s) -> %s ? ? ?", level, file, line, function, fmt);
548         else {
549                 _hook_ditf_(ditf, "vverbose(%d, %s, %d, %s) -> %s", level, file, line, function, msg);
550                 free(msg);
551         }
552 }
553
554 static void hook_ditf_event_make_cb(void *closure, const struct afb_ditf *ditf, const char *name, struct afb_event result)
555 {
556         _hook_ditf_(ditf, "event_make(%s) -> %s:%p", name, afb_event_name(result), result.closure);
557 }
558
559 static void hook_ditf_rootdir_get_fd_cb(void *closure, const struct afb_ditf *ditf, int result)
560 {
561         char path[PATH_MAX];
562         if (result < 0)
563                 _hook_ditf_(ditf, "rootdir_get_fd() -> %d, %m", result);
564         else {
565                 sprintf(path, "/proc/self/fd/%d", result);
566                 readlink(path, path, sizeof path);
567                 _hook_ditf_(ditf, "rootdir_get_fd() -> %d = %s", result, path);
568         }
569 }
570
571 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)
572 {
573         char path[PATH_MAX];
574         if (!locale)
575                 locale = "(null)";
576         if (result < 0)
577                 _hook_ditf_(ditf, "rootdir_open_locale(%s, %d, %s) -> %d, %m", filename, flags, locale, result);
578         else {
579                 sprintf(path, "/proc/self/fd/%d", result);
580                 readlink(path, path, sizeof path);
581                 _hook_ditf_(ditf, "rootdir_open_locale(%s, %d, %s) -> %d = %s", filename, flags, locale, result, path);
582         }
583 }
584
585 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)
586 {
587         _hook_ditf_(ditf, "queue_job(%p, %p, %p, %d) -> %d", callback, argument, group, timeout, result);
588 }
589
590 static struct afb_hook_ditf_itf hook_ditf_default_itf = {
591         .hook_ditf_event_broadcast_before = hook_ditf_event_broadcast_before_cb,
592         .hook_ditf_event_broadcast_after = hook_ditf_event_broadcast_after_cb,
593         .hook_ditf_get_event_loop = hook_ditf_get_event_loop_cb,
594         .hook_ditf_get_user_bus = hook_ditf_get_user_bus_cb,
595         .hook_ditf_get_system_bus = hook_ditf_get_system_bus_cb,
596         .hook_ditf_vverbose = hook_ditf_vverbose_cb,
597         .hook_ditf_event_make = hook_ditf_event_make_cb,
598         .hook_ditf_rootdir_get_fd = hook_ditf_rootdir_get_fd_cb,
599         .hook_ditf_rootdir_open_locale = hook_ditf_rootdir_open_locale_cb,
600         .hook_ditf_queue_job = hook_ditf_queue_job
601 };
602
603 /******************************************************************************
604  * section: hooks for tracing daemon interface (ditf)
605  *****************************************************************************/
606
607 #define _HOOK_DITF_(what,...)   \
608         struct afb_hook_ditf *hook; \
609         pthread_rwlock_rdlock(&rwlock); \
610         hook = list_of_ditf_hooks; \
611         while (hook) { \
612                 if (hook->itf->hook_ditf_##what \
613                  && (hook->flags & afb_hook_flag_ditf_##what) != 0 \
614                  && (!hook->api || !strcasecmp(hook->api, ditf->prefix))) { \
615                         hook->itf->hook_ditf_##what(hook->closure, __VA_ARGS__); \
616                 } \
617                 hook = hook->next; \
618         } \
619         pthread_rwlock_unlock(&rwlock);
620
621 void afb_hook_ditf_event_broadcast_before(const struct afb_ditf *ditf, const char *name, struct json_object *object)
622 {
623         _HOOK_DITF_(event_broadcast_before, ditf, name, object);
624 }
625
626 int afb_hook_ditf_event_broadcast_after(const struct afb_ditf *ditf, const char *name, struct json_object *object, int result)
627 {
628         _HOOK_DITF_(event_broadcast_after, ditf, name, object, result);
629         return result;
630 }
631
632 struct sd_event *afb_hook_ditf_get_event_loop(const struct afb_ditf *ditf, struct sd_event *result)
633 {
634         _HOOK_DITF_(get_event_loop, ditf, result);
635         return result;
636 }
637
638 struct sd_bus *afb_hook_ditf_get_user_bus(const struct afb_ditf *ditf, struct sd_bus *result)
639 {
640         _HOOK_DITF_(get_user_bus, ditf, result);
641         return result;
642 }
643
644 struct sd_bus *afb_hook_ditf_get_system_bus(const struct afb_ditf *ditf, struct sd_bus *result)
645 {
646         _HOOK_DITF_(get_system_bus, ditf, result);
647         return result;
648 }
649
650 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)
651 {
652         _HOOK_DITF_(vverbose, ditf, level, file, line, function, fmt, args);
653 }
654
655 struct afb_event afb_hook_ditf_event_make(const struct afb_ditf *ditf, const char *name, struct afb_event result)
656 {
657         _HOOK_DITF_(event_make, ditf, name, result);
658         return result;
659 }
660
661 int afb_hook_ditf_rootdir_get_fd(const struct afb_ditf *ditf, int result)
662 {
663         _HOOK_DITF_(rootdir_get_fd, ditf, result);
664         return result;
665 }
666
667 int afb_hook_ditf_rootdir_open_locale(const struct afb_ditf *ditf, const char *filename, int flags, const char *locale, int result)
668 {
669         _HOOK_DITF_(rootdir_open_locale, ditf, filename, flags, locale, result);
670         return result;
671 }
672
673 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)
674 {
675         _HOOK_DITF_(queue_job, ditf, callback, argument, group, timeout, result);
676         return result;
677 }
678
679 /******************************************************************************
680  * section: hooking ditf
681  *****************************************************************************/
682
683 int afb_hook_flags_ditf(const char *api)
684 {
685         int flags;
686         struct afb_hook_ditf *hook;
687
688         pthread_rwlock_rdlock(&rwlock);
689         flags = 0;
690         hook = list_of_ditf_hooks;
691         while (hook) {
692                 if (!api || !hook->api || !strcasecmp(hook->api, api))
693                         flags |= hook->flags;
694                 hook = hook->next;
695         }
696         pthread_rwlock_unlock(&rwlock);
697         return flags;
698 }
699
700 struct afb_hook_ditf *afb_hook_create_ditf(const char *api, int flags, struct afb_hook_ditf_itf *itf, void *closure)
701 {
702         struct afb_hook_ditf *hook;
703
704         /* alloc the result */
705         hook = calloc(1, sizeof *hook);
706         if (hook == NULL)
707                 return NULL;
708
709         /* get a copy of the names */
710         hook->api = api ? strdup(api) : NULL;
711         if (api && !hook->api) {
712                 free(hook);
713                 return NULL;
714         }
715
716         /* initialise the rest */
717         hook->refcount = 1;
718         hook->flags = flags;
719         hook->itf = itf ? itf : &hook_ditf_default_itf;
720         hook->closure = closure;
721
722         /* record the hook */
723         pthread_rwlock_wrlock(&rwlock);
724         hook->next = list_of_ditf_hooks;
725         list_of_ditf_hooks = hook;
726         pthread_rwlock_unlock(&rwlock);
727
728         /* returns it */
729         return hook;
730 }
731
732 struct afb_hook_ditf *afb_hook_addref_ditf(struct afb_hook_ditf *hook)
733 {
734         pthread_rwlock_wrlock(&rwlock);
735         hook->refcount++;
736         pthread_rwlock_unlock(&rwlock);
737         return hook;
738 }
739
740 void afb_hook_unref_ditf(struct afb_hook_ditf *hook)
741 {
742         struct afb_hook_ditf **prv;
743
744         if (hook) {
745                 pthread_rwlock_wrlock(&rwlock);
746                 if (--hook->refcount)
747                         hook = NULL;
748                 else {
749                         /* unlink */
750                         prv = &list_of_ditf_hooks;
751                         while (*prv && *prv != hook)
752                                 prv = &(*prv)->next;
753                         if(*prv)
754                                 *prv = hook->next;
755                 }
756                 pthread_rwlock_unlock(&rwlock);
757                 if (hook) {
758                         /* free */
759                         free(hook->api);
760                         free(hook);
761                 }
762         }
763 }
764
765 /******************************************************************************
766  * section: default callbacks for tracing service interface (svc)
767  *****************************************************************************/
768
769 static void _hook_svc_(const struct afb_svc *svc, const char *format, ...)
770 {
771         int len;
772         char *buffer;
773         va_list ap;
774
775         va_start(ap, format);
776         len = vasprintf(&buffer, format, ap);
777         va_end(ap);
778
779         if (len < 0)
780                 NOTICE("hook svc-%s allocation error for %s", svc->api, format);
781         else {
782                 NOTICE("hook svc-%s %s", svc->api, buffer);
783                 free(buffer);
784         }
785 }
786
787 static void hook_svc_start_before_default_cb(void *closure, const struct afb_svc *svc)
788 {
789         _hook_svc_(svc, "start.before");
790 }
791
792 static void hook_svc_start_after_default_cb(void *closure, const struct afb_svc *svc, int status)
793 {
794         _hook_svc_(svc, "start.after -> %d", status);
795 }
796
797 static void hook_svc_on_event_before_default_cb(void *closure, const struct afb_svc *svc, const char *event, int eventid, struct json_object *object)
798 {
799         _hook_svc_(svc, "on_event.before(%s, %d, %s)", event, eventid, json_object_to_json_string(object));
800 }
801
802 static void hook_svc_on_event_after_default_cb(void *closure, const struct afb_svc *svc, const char *event, int eventid, struct json_object *object)
803 {
804         _hook_svc_(svc, "on_event.after(%s, %d, %s)", event, eventid, json_object_to_json_string(object));
805 }
806
807 static void hook_svc_call_default_cb(void *closure, const struct afb_svc *svc, const char *api, const char *verb, struct json_object *args)
808 {
809         _hook_svc_(svc, "call(%s/%s, %s) ...", api, verb, json_object_to_json_string(args));
810 }
811
812 static void hook_svc_call_result_default_cb(void *closure, const struct afb_svc *svc, int status, struct json_object *result)
813 {
814         _hook_svc_(svc, "    ...call... -> %d: %s", status, json_object_to_json_string(result));
815 }
816
817 static void hook_svc_callsync_default_cb(void *closure, const struct afb_svc *svc, const char *api, const char *verb, struct json_object *args)
818 {
819         _hook_svc_(svc, "callsync(%s/%s, %s) ...", api, verb, json_object_to_json_string(args));
820 }
821
822 static void hook_svc_callsync_result_default_cb(void *closure, const struct afb_svc *svc, int status, struct json_object *result)
823 {
824         _hook_svc_(svc, "    ...callsync... -> %d: %s", status, json_object_to_json_string(result));
825 }
826
827 static struct afb_hook_svc_itf hook_svc_default_itf = {
828         .hook_svc_start_before = hook_svc_start_before_default_cb,
829         .hook_svc_start_after = hook_svc_start_after_default_cb,
830         .hook_svc_on_event_before = hook_svc_on_event_before_default_cb,
831         .hook_svc_on_event_after = hook_svc_on_event_after_default_cb,
832         .hook_svc_call = hook_svc_call_default_cb,
833         .hook_svc_call_result = hook_svc_call_result_default_cb,
834         .hook_svc_callsync = hook_svc_callsync_default_cb,
835         .hook_svc_callsync_result = hook_svc_callsync_result_default_cb
836 };
837
838 /******************************************************************************
839  * section: hooks for tracing service interface (svc)
840  *****************************************************************************/
841
842 #define _HOOK_SVC_(what,...)   \
843         struct afb_hook_svc *hook; \
844         pthread_rwlock_rdlock(&rwlock); \
845         hook = list_of_svc_hooks; \
846         while (hook) { \
847                 if (hook->itf->hook_svc_##what \
848                  && (hook->flags & afb_hook_flag_svc_##what) != 0 \
849                  && (!hook->api || !strcasecmp(hook->api, svc->api))) { \
850                         hook->itf->hook_svc_##what(hook->closure, __VA_ARGS__); \
851                 } \
852                 hook = hook->next; \
853         } \
854         pthread_rwlock_unlock(&rwlock);
855
856 void afb_hook_svc_start_before(const struct afb_svc *svc)
857 {
858         _HOOK_SVC_(start_before, svc);
859 }
860
861 int afb_hook_svc_start_after(const struct afb_svc *svc, int status)
862 {
863         _HOOK_SVC_(start_after, svc, status);
864         return status;
865 }
866
867 void afb_hook_svc_on_event_before(const struct afb_svc *svc, const char *event, int eventid, struct json_object *object)
868 {
869         _HOOK_SVC_(on_event_before, svc, event, eventid, object);
870 }
871
872 void afb_hook_svc_on_event_after(const struct afb_svc *svc, const char *event, int eventid, struct json_object *object)
873 {
874         _HOOK_SVC_(on_event_after, svc, event, eventid, object);
875 }
876
877 void afb_hook_svc_call(const struct afb_svc *svc, const char *api, const char *verb, struct json_object *args)
878 {
879         _HOOK_SVC_(call, svc, api, verb, args);
880 }
881
882 void afb_hook_svc_call_result(const struct afb_svc *svc, int status, struct json_object *result)
883 {
884         _HOOK_SVC_(call_result, svc, status, result);
885 }
886
887 void afb_hook_svc_callsync(const struct afb_svc *svc, const char *api, const char *verb, struct json_object *args)
888 {
889         _HOOK_SVC_(callsync, svc, api, verb, args);
890 }
891
892 int afb_hook_svc_callsync_result(const struct afb_svc *svc, int status, struct json_object *result)
893 {
894         _HOOK_SVC_(callsync_result, svc, status, result);
895         return status;
896 }
897
898 /******************************************************************************
899  * section: hooking services (svc)
900  *****************************************************************************/
901
902 int afb_hook_flags_svc(const char *api)
903 {
904         int flags;
905         struct afb_hook_svc *hook;
906
907         pthread_rwlock_rdlock(&rwlock);
908         flags = 0;
909         hook = list_of_svc_hooks;
910         while (hook) {
911                 if (!api || !hook->api || !strcasecmp(hook->api, api))
912                         flags |= hook->flags;
913                 hook = hook->next;
914         }
915         pthread_rwlock_unlock(&rwlock);
916         return flags;
917 }
918
919 struct afb_hook_svc *afb_hook_create_svc(const char *api, int flags, struct afb_hook_svc_itf *itf, void *closure)
920 {
921         struct afb_hook_svc *hook;
922
923         /* alloc the result */
924         hook = calloc(1, sizeof *hook);
925         if (hook == NULL)
926                 return NULL;
927
928         /* get a copy of the names */
929         hook->api = api ? strdup(api) : NULL;
930         if (api && !hook->api) {
931                 free(hook);
932                 return NULL;
933         }
934
935         /* initialise the rest */
936         hook->refcount = 1;
937         hook->flags = flags;
938         hook->itf = itf ? itf : &hook_svc_default_itf;
939         hook->closure = closure;
940
941         /* record the hook */
942         pthread_rwlock_wrlock(&rwlock);
943         hook->next = list_of_svc_hooks;
944         list_of_svc_hooks = hook;
945         pthread_rwlock_unlock(&rwlock);
946
947         /* returns it */
948         return hook;
949 }
950
951 struct afb_hook_svc *afb_hook_addref_svc(struct afb_hook_svc *hook)
952 {
953         pthread_rwlock_wrlock(&rwlock);
954         hook->refcount++;
955         pthread_rwlock_unlock(&rwlock);
956         return hook;
957 }
958
959 void afb_hook_unref_svc(struct afb_hook_svc *hook)
960 {
961         struct afb_hook_svc **prv;
962
963         if (hook) {
964                 pthread_rwlock_wrlock(&rwlock);
965                 if (--hook->refcount)
966                         hook = NULL;
967                 else {
968                         /* unlink */
969                         prv = &list_of_svc_hooks;
970                         while (*prv && *prv != hook)
971                                 prv = &(*prv)->next;
972                         if(*prv)
973                                 *prv = hook->next;
974                 }
975                 pthread_rwlock_unlock(&rwlock);
976                 if (hook) {
977                         /* free */
978                         free(hook->api);
979                         free(hook);
980                 }
981         }
982 }
983