Reactivate hooking of requests
[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 <stdio.h>
21 #include <stdarg.h>
22 #include <string.h>
23 #include <pthread.h>
24
25 #include <json-c/json.h>
26
27 #include <afb/afb-req-itf.h>
28 #include <afb/afb-event-itf.h>
29
30 #include "afb-context.h"
31 #include "afb-hook.h"
32 #include "afb-session.h"
33 #include "afb-xreq.h"
34 #include "verbose.h"
35
36 /**
37  * Definition of a hook
38  */
39 struct afb_hook {
40         struct afb_hook *next; /**< next hook */
41         unsigned refcount; /**< reference count */
42         char *api; /**< api hooked or NULL for any */
43         char *verb; /**< verb hooked or NULL for any */
44         struct afb_session *session; /**< session hooked or NULL if any */
45         unsigned flags; /**< hook flags */
46         struct afb_hook_xreq_itf *reqitf; /**< interface of hook */
47         void *closure; /**< closure for callbacks */
48 };
49
50 /*
51  * Structure for handling subcalls callbacks
52  */
53 struct hook_subcall {
54         struct afb_xreq *xreq; /* hookd request */
55         void (*callback)(void*, int, struct json_object*); /* client callback */
56         void *cb_closure; /* cient closure */
57 };
58
59 /* synchronisation across threads */
60 static pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
61
62 /* list of hooks */
63 static struct afb_hook *list_of_hooks = NULL;
64
65 /******************************************************************************
66  * section: default callbacks for tracing requests
67  *****************************************************************************/
68
69 static void _hook_xreq_(const struct afb_xreq *xreq, const char *format, ...)
70 {
71         int len;
72         char *buffer;
73         va_list ap;
74
75         va_start(ap, format);
76         len = vasprintf(&buffer, format, ap);
77         va_end(ap);
78
79         if (len < 0)
80                 NOTICE("hook xreq-%06d:%s/%s allocation error", xreq->hookindex, xreq->api, xreq->verb);
81         else {
82                 NOTICE("hook xreq-%06d:%s/%s %s", xreq->hookindex, xreq->api, xreq->verb, buffer);
83                 free(buffer);
84         }
85 }
86
87 static void hook_xreq_begin_default_cb(void * closure, const struct afb_xreq *xreq)
88 {
89         _hook_xreq_(xreq, "BEGIN");
90 }
91
92 static void hook_xreq_end_default_cb(void * closure, const struct afb_xreq *xreq)
93 {
94         _hook_xreq_(xreq, "END");
95 }
96
97 static void hook_xreq_json_default_cb(void * closure, const struct afb_xreq *xreq, struct json_object *obj)
98 {
99         _hook_xreq_(xreq, "json() -> %s", json_object_to_json_string(obj));
100 }
101
102 static void hook_xreq_get_default_cb(void * closure, const struct afb_xreq *xreq, const char *name, struct afb_arg arg)
103 {
104         _hook_xreq_(xreq, "get(%s) -> { name: %s, value: %s, path: %s }", name, arg.name, arg.value, arg.path);
105 }
106
107 static void hook_xreq_success_default_cb(void * closure, const struct afb_xreq *xreq, struct json_object *obj, const char *info)
108 {
109         _hook_xreq_(xreq, "success(%s, %s)", json_object_to_json_string(obj), info);
110 }
111
112 static void hook_xreq_fail_default_cb(void * closure, const struct afb_xreq *xreq, const char *status, const char *info)
113 {
114         _hook_xreq_(xreq, "fail(%s, %s)", status, info);
115 }
116
117 static void hook_xreq_raw_default_cb(void * closure, const struct afb_xreq *xreq, const char *buffer, size_t size)
118 {
119         _hook_xreq_(xreq, "raw() -> %.*s", (int)size, buffer);
120 }
121
122 static void hook_xreq_send_default_cb(void * closure, const struct afb_xreq *xreq, const char *buffer, size_t size)
123 {
124         _hook_xreq_(xreq, "send(%.*s)", (int)size, buffer);
125 }
126
127 static void hook_xreq_context_get_default_cb(void * closure, const struct afb_xreq *xreq, void *value)
128 {
129         _hook_xreq_(xreq, "context_get() -> %p", value);
130 }
131
132 static void hook_xreq_context_set_default_cb(void * closure, const struct afb_xreq *xreq, void *value, void (*free_value)(void*))
133 {
134         _hook_xreq_(xreq, "context_set(%p, %p)", value, free_value);
135 }
136
137 static void hook_xreq_addref_default_cb(void * closure, const struct afb_xreq *xreq)
138 {
139         _hook_xreq_(xreq, "addref()");
140 }
141
142 static void hook_xreq_unref_default_cb(void * closure, const struct afb_xreq *xreq)
143 {
144         _hook_xreq_(xreq, "unref()");
145 }
146
147 static void hook_xreq_session_close_default_cb(void * closure, const struct afb_xreq *xreq)
148 {
149         _hook_xreq_(xreq, "session_close()");
150 }
151
152 static void hook_xreq_session_set_LOA_default_cb(void * closure, const struct afb_xreq *xreq, unsigned level, int result)
153 {
154         _hook_xreq_(xreq, "session_set_LOA(%u) -> %d", level, result);
155 }
156
157 static void hook_xreq_subscribe_default_cb(void * closure, const struct afb_xreq *xreq, struct afb_event event, int result)
158 {
159         _hook_xreq_(xreq, "subscribe(%s:%p) -> %d", afb_event_name(event), event.closure, result);
160 }
161
162 static void hook_xreq_unsubscribe_default_cb(void * closure, const struct afb_xreq *xreq, struct afb_event event, int result)
163 {
164         _hook_xreq_(xreq, "unsubscribe(%s:%p) -> %d", afb_event_name(event), event.closure, result);
165 }
166
167 static void hook_xreq_subcall_default_cb(void * closure, const struct afb_xreq *xreq, const char *api, const char *verb, struct json_object *args)
168 {
169         _hook_xreq_(xreq, "subcall(%s/%s, %s) ...", api, verb, json_object_to_json_string(args));
170 }
171
172 static void hook_xreq_subcall_result_default_cb(void * closure, const struct afb_xreq *xreq, int status, struct json_object *result)
173 {
174         _hook_xreq_(xreq, "    ...subcall... -> %d: %s", status, json_object_to_json_string(result));
175 }
176
177 static struct afb_hook_xreq_itf hook_xreq_default_itf = {
178         .hook_xreq_begin = hook_xreq_begin_default_cb,
179         .hook_xreq_end = hook_xreq_end_default_cb,
180         .hook_xreq_json = hook_xreq_json_default_cb,
181         .hook_xreq_get = hook_xreq_get_default_cb,
182         .hook_xreq_success = hook_xreq_success_default_cb,
183         .hook_xreq_fail = hook_xreq_fail_default_cb,
184         .hook_xreq_raw = hook_xreq_raw_default_cb,
185         .hook_xreq_send = hook_xreq_send_default_cb,
186         .hook_xreq_context_get = hook_xreq_context_get_default_cb,
187         .hook_xreq_context_set = hook_xreq_context_set_default_cb,
188         .hook_xreq_addref = hook_xreq_addref_default_cb,
189         .hook_xreq_unref = hook_xreq_unref_default_cb,
190         .hook_xreq_session_close = hook_xreq_session_close_default_cb,
191         .hook_xreq_session_set_LOA = hook_xreq_session_set_LOA_default_cb,
192         .hook_xreq_subscribe = hook_xreq_subscribe_default_cb,
193         .hook_xreq_unsubscribe = hook_xreq_unsubscribe_default_cb,
194         .hook_xreq_subcall = hook_xreq_subcall_default_cb,
195         .hook_xreq_subcall_result = hook_xreq_subcall_result_default_cb,
196 };
197
198 /******************************************************************************
199  * section: hooks for tracing requests
200  *****************************************************************************/
201
202 #define _HOOK_XREQ_(what,...)   \
203         struct afb_hook *hook; \
204         pthread_rwlock_rdlock(&rwlock); \
205         hook = list_of_hooks; \
206         while (hook) { \
207                 if (hook->reqitf->hook_xreq_##what \
208                  && (hook->flags & afb_hook_flag_req_##what) != 0 \
209                  && (!hook->session || hook->session == xreq->context.session) \
210                  && (!hook->api || !strcasecmp(hook->api, xreq->api)) \
211                  && (!hook->verb || !strcasecmp(hook->verb, xreq->verb))) { \
212                         hook->reqitf->hook_xreq_##what(hook->closure, __VA_ARGS__); \
213                 } \
214                 hook = hook->next; \
215         } \
216         pthread_rwlock_unlock(&rwlock);
217
218
219 void afb_hook_xreq_begin(const struct afb_xreq *xreq)
220 {
221         _HOOK_XREQ_(begin, xreq);
222 }
223
224 void afb_hook_xreq_end(const struct afb_xreq *xreq)
225 {
226         _HOOK_XREQ_(end, xreq);
227 }
228
229 struct json_object *afb_hook_xreq_json(const struct afb_xreq *xreq, struct json_object *obj)
230 {
231         _HOOK_XREQ_(json, xreq, obj);
232         return obj;
233 }
234
235 struct afb_arg afb_hook_xreq_get(const struct afb_xreq *xreq, const char *name, struct afb_arg arg)
236 {
237         _HOOK_XREQ_(get, xreq, name, arg);
238         return arg;
239 }
240
241 void afb_hook_xreq_success(const struct afb_xreq *xreq, struct json_object *obj, const char *info)
242 {
243         _HOOK_XREQ_(success, xreq, obj, info);
244 }
245
246 void afb_hook_xreq_fail(const struct afb_xreq *xreq, const char *status, const char *info)
247 {
248         _HOOK_XREQ_(fail, xreq, status, info);
249 }
250
251 const char *afb_hook_xreq_raw(const struct afb_xreq *xreq, const char *buffer, size_t size)
252 {
253         _HOOK_XREQ_(raw, xreq, buffer, size);
254         return buffer;
255 }
256
257 void afb_hook_xreq_send(const struct afb_xreq *xreq, const char *buffer, size_t size)
258 {
259         _HOOK_XREQ_(send, xreq, buffer, size);
260 }
261
262 void *afb_hook_xreq_context_get(const struct afb_xreq *xreq, void *value)
263 {
264         _HOOK_XREQ_(context_get, xreq, value);
265         return value;
266 }
267
268 void afb_hook_xreq_context_set(const struct afb_xreq *xreq, void *value, void (*free_value)(void*))
269 {
270         _HOOK_XREQ_(context_set, xreq, value, free_value);
271 }
272
273 void afb_hook_xreq_addref(const struct afb_xreq *xreq)
274 {
275         _HOOK_XREQ_(addref, xreq);
276 }
277
278 void afb_hook_xreq_unref(const struct afb_xreq *xreq)
279 {
280         _HOOK_XREQ_(unref, xreq);
281 }
282
283 void afb_hook_xreq_session_close(const struct afb_xreq *xreq)
284 {
285         _HOOK_XREQ_(session_close, xreq);
286 }
287
288 int afb_hook_xreq_session_set_LOA(const struct afb_xreq *xreq, unsigned level, int result)
289 {
290         _HOOK_XREQ_(session_set_LOA, xreq, level, result);
291         return result;
292 }
293
294 int afb_hook_xreq_subscribe(const struct afb_xreq *xreq, struct afb_event event, int result)
295 {
296         _HOOK_XREQ_(subscribe, xreq, event, result);
297         return result;
298 }
299
300 int afb_hook_xreq_unsubscribe(const struct afb_xreq *xreq, struct afb_event event, int result)
301 {
302         _HOOK_XREQ_(unsubscribe, xreq, event, result);
303         return result;
304 }
305
306 void afb_hook_xreq_subcall(const struct afb_xreq *xreq, const char *api, const char *verb, struct json_object *args)
307 {
308         _HOOK_XREQ_(subcall, xreq, api, verb, args);
309 }
310
311 void afb_hook_xreq_subcall_result(const struct afb_xreq *xreq, int status, struct json_object *result)
312 {
313         _HOOK_XREQ_(subcall_result, xreq, status, result);
314 }
315
316 /******************************************************************************
317  * section: 
318  *****************************************************************************/
319
320 void afb_hook_init_xreq(struct afb_xreq *xreq)
321 {
322         static int reqindex;
323
324         int f, flags;
325         int add;
326         struct afb_hook *hook;
327
328         /* scan hook list to get the expected flags */
329         flags = 0;
330         pthread_rwlock_rdlock(&rwlock);
331         hook = list_of_hooks;
332         while (hook) {
333                 f = hook->flags & afb_hook_flags_req_all;
334                 add = f != 0
335                    && (!hook->session || hook->session == xreq->context.session)
336                    && (!hook->api || !strcasecmp(hook->api, xreq->api))
337                    && (!hook->verb || !strcasecmp(hook->verb, xreq->verb));
338                 if (add)
339                         flags |= f;
340                 hook = hook->next;
341         }
342         pthread_rwlock_unlock(&rwlock);
343
344         /* store the hooking data */
345         xreq->hookflags = flags;
346         if (flags) {
347                 pthread_rwlock_wrlock(&rwlock);
348                 if (++reqindex < 0)
349                         reqindex = 1;
350                 xreq->hookindex = reqindex;
351                 pthread_rwlock_unlock(&rwlock);
352         }
353 }
354
355 struct afb_hook *afb_hook_xreq_create(const char *api, const char *verb, struct afb_session *session, unsigned flags, struct afb_hook_xreq_itf *itf, void *closure)
356 {
357         struct afb_hook *hook;
358
359         /* alloc the result */
360         hook = malloc(sizeof *hook);
361         if (hook == NULL)
362                 return NULL;
363
364         /* get a copy of the names */
365         hook->api = api ? strdup(api) : NULL;
366         hook->verb = verb ? strdup(verb) : NULL;
367         if ((api && !hook->api) || (verb && !hook->verb)) {
368                 free(hook->api);
369                 free(hook->verb);
370                 free(hook);
371                 return NULL;
372         }
373
374         /* initialise the rest */
375         hook->session = session;
376         if (session)
377                 afb_session_addref(session);
378         hook->refcount = 1;
379         hook->flags = flags;
380         hook->reqitf = itf ? itf : &hook_xreq_default_itf;
381         hook->closure = closure;
382
383         /* record the hook */
384         pthread_rwlock_wrlock(&rwlock);
385         hook->next = list_of_hooks;
386         list_of_hooks = hook;
387         pthread_rwlock_unlock(&rwlock);
388
389         /* returns it */
390         return hook;
391 }
392
393 struct afb_hook *afb_hook_addref(struct afb_hook *hook)
394 {
395         pthread_rwlock_wrlock(&rwlock);
396         hook->refcount++;
397         pthread_rwlock_unlock(&rwlock);
398         return hook;
399 }
400
401 void afb_hook_unref(struct afb_hook *hook)
402 {
403         struct afb_hook **prv;
404
405         if (hook) {
406                 pthread_rwlock_wrlock(&rwlock);
407                 if (--hook->refcount)
408                         hook = NULL;
409                 else {
410                         /* unlink */
411                         prv = &list_of_hooks;
412                         while (*prv && *prv != hook)
413                                 prv = &(*prv)->next;
414                         if(*prv)
415                                 *prv = hook->next;
416                 }
417                 pthread_rwlock_unlock(&rwlock);
418                 if (hook) {
419                         /* free */
420                         free(hook->api);
421                         free(hook->verb);
422                         if (hook->session)
423                                 afb_session_unref(hook->session);
424                         free(hook);
425                 }
426         }
427 }
428