Finalize 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 void hook_xreq_subcallsync_default_cb(void * closure, const struct afb_xreq *xreq, const char *api, const char *verb, struct json_object *args)
178 {
179         _hook_xreq_(xreq, "subcallsync(%s/%s, %s) ...", api, verb, json_object_to_json_string(args));
180 }
181
182 static void hook_xreq_subcallsync_result_default_cb(void * closure, const struct afb_xreq *xreq, int status, struct json_object *result)
183 {
184         _hook_xreq_(xreq, "    ...subcallsync... -> %d: %s", status, json_object_to_json_string(result));
185 }
186
187 static struct afb_hook_xreq_itf hook_xreq_default_itf = {
188         .hook_xreq_begin = hook_xreq_begin_default_cb,
189         .hook_xreq_end = hook_xreq_end_default_cb,
190         .hook_xreq_json = hook_xreq_json_default_cb,
191         .hook_xreq_get = hook_xreq_get_default_cb,
192         .hook_xreq_success = hook_xreq_success_default_cb,
193         .hook_xreq_fail = hook_xreq_fail_default_cb,
194         .hook_xreq_raw = hook_xreq_raw_default_cb,
195         .hook_xreq_send = hook_xreq_send_default_cb,
196         .hook_xreq_context_get = hook_xreq_context_get_default_cb,
197         .hook_xreq_context_set = hook_xreq_context_set_default_cb,
198         .hook_xreq_addref = hook_xreq_addref_default_cb,
199         .hook_xreq_unref = hook_xreq_unref_default_cb,
200         .hook_xreq_session_close = hook_xreq_session_close_default_cb,
201         .hook_xreq_session_set_LOA = hook_xreq_session_set_LOA_default_cb,
202         .hook_xreq_subscribe = hook_xreq_subscribe_default_cb,
203         .hook_xreq_unsubscribe = hook_xreq_unsubscribe_default_cb,
204         .hook_xreq_subcall = hook_xreq_subcall_default_cb,
205         .hook_xreq_subcall_result = hook_xreq_subcall_result_default_cb,
206         .hook_xreq_subcallsync = hook_xreq_subcallsync_default_cb,
207         .hook_xreq_subcallsync_result = hook_xreq_subcallsync_result_default_cb,
208 };
209
210 /******************************************************************************
211  * section: hooks for tracing requests
212  *****************************************************************************/
213
214 #define _HOOK_XREQ_(what,...)   \
215         struct afb_hook *hook; \
216         pthread_rwlock_rdlock(&rwlock); \
217         hook = list_of_hooks; \
218         while (hook) { \
219                 if (hook->reqitf->hook_xreq_##what \
220                  && (hook->flags & afb_hook_flag_req_##what) != 0 \
221                  && (!hook->session || hook->session == xreq->context.session) \
222                  && (!hook->api || !strcasecmp(hook->api, xreq->api)) \
223                  && (!hook->verb || !strcasecmp(hook->verb, xreq->verb))) { \
224                         hook->reqitf->hook_xreq_##what(hook->closure, __VA_ARGS__); \
225                 } \
226                 hook = hook->next; \
227         } \
228         pthread_rwlock_unlock(&rwlock);
229
230
231 void afb_hook_xreq_begin(const struct afb_xreq *xreq)
232 {
233         _HOOK_XREQ_(begin, xreq);
234 }
235
236 void afb_hook_xreq_end(const struct afb_xreq *xreq)
237 {
238         _HOOK_XREQ_(end, xreq);
239 }
240
241 struct json_object *afb_hook_xreq_json(const struct afb_xreq *xreq, struct json_object *obj)
242 {
243         _HOOK_XREQ_(json, xreq, obj);
244         return obj;
245 }
246
247 struct afb_arg afb_hook_xreq_get(const struct afb_xreq *xreq, const char *name, struct afb_arg arg)
248 {
249         _HOOK_XREQ_(get, xreq, name, arg);
250         return arg;
251 }
252
253 void afb_hook_xreq_success(const struct afb_xreq *xreq, struct json_object *obj, const char *info)
254 {
255         _HOOK_XREQ_(success, xreq, obj, info);
256 }
257
258 void afb_hook_xreq_fail(const struct afb_xreq *xreq, const char *status, const char *info)
259 {
260         _HOOK_XREQ_(fail, xreq, status, info);
261 }
262
263 const char *afb_hook_xreq_raw(const struct afb_xreq *xreq, const char *buffer, size_t size)
264 {
265         _HOOK_XREQ_(raw, xreq, buffer, size);
266         return buffer;
267 }
268
269 void afb_hook_xreq_send(const struct afb_xreq *xreq, const char *buffer, size_t size)
270 {
271         _HOOK_XREQ_(send, xreq, buffer, size);
272 }
273
274 void *afb_hook_xreq_context_get(const struct afb_xreq *xreq, void *value)
275 {
276         _HOOK_XREQ_(context_get, xreq, value);
277         return value;
278 }
279
280 void afb_hook_xreq_context_set(const struct afb_xreq *xreq, void *value, void (*free_value)(void*))
281 {
282         _HOOK_XREQ_(context_set, xreq, value, free_value);
283 }
284
285 void afb_hook_xreq_addref(const struct afb_xreq *xreq)
286 {
287         _HOOK_XREQ_(addref, xreq);
288 }
289
290 void afb_hook_xreq_unref(const struct afb_xreq *xreq)
291 {
292         _HOOK_XREQ_(unref, xreq);
293 }
294
295 void afb_hook_xreq_session_close(const struct afb_xreq *xreq)
296 {
297         _HOOK_XREQ_(session_close, xreq);
298 }
299
300 int afb_hook_xreq_session_set_LOA(const struct afb_xreq *xreq, unsigned level, int result)
301 {
302         _HOOK_XREQ_(session_set_LOA, xreq, level, result);
303         return result;
304 }
305
306 int afb_hook_xreq_subscribe(const struct afb_xreq *xreq, struct afb_event event, int result)
307 {
308         _HOOK_XREQ_(subscribe, xreq, event, result);
309         return result;
310 }
311
312 int afb_hook_xreq_unsubscribe(const struct afb_xreq *xreq, struct afb_event event, int result)
313 {
314         _HOOK_XREQ_(unsubscribe, xreq, event, result);
315         return result;
316 }
317
318 void afb_hook_xreq_subcall(const struct afb_xreq *xreq, const char *api, const char *verb, struct json_object *args)
319 {
320         _HOOK_XREQ_(subcall, xreq, api, verb, args);
321 }
322
323 void afb_hook_xreq_subcall_result(const struct afb_xreq *xreq, int status, struct json_object *result)
324 {
325         _HOOK_XREQ_(subcall_result, xreq, status, result);
326 }
327
328 void afb_hook_xreq_subcallsync(const struct afb_xreq *xreq, const char *api, const char *verb, struct json_object *args)
329 {
330         _HOOK_XREQ_(subcallsync, xreq, api, verb, args);
331 }
332
333 int afb_hook_xreq_subcallsync_result(const struct afb_xreq *xreq, int status, struct json_object *result)
334 {
335         _HOOK_XREQ_(subcallsync_result, xreq, status, result);
336         return status;
337 }
338
339 /******************************************************************************
340  * section: 
341  *****************************************************************************/
342
343 void afb_hook_init_xreq(struct afb_xreq *xreq)
344 {
345         static int reqindex;
346
347         int f, flags;
348         int add;
349         struct afb_hook *hook;
350
351         /* scan hook list to get the expected flags */
352         flags = 0;
353         pthread_rwlock_rdlock(&rwlock);
354         hook = list_of_hooks;
355         while (hook) {
356                 f = hook->flags & afb_hook_flags_req_all;
357                 add = f != 0
358                    && (!hook->session || hook->session == xreq->context.session)
359                    && (!hook->api || !strcasecmp(hook->api, xreq->api))
360                    && (!hook->verb || !strcasecmp(hook->verb, xreq->verb));
361                 if (add)
362                         flags |= f;
363                 hook = hook->next;
364         }
365         pthread_rwlock_unlock(&rwlock);
366
367         /* store the hooking data */
368         xreq->hookflags = flags;
369         if (flags) {
370                 pthread_rwlock_wrlock(&rwlock);
371                 if (++reqindex < 0)
372                         reqindex = 1;
373                 xreq->hookindex = reqindex;
374                 pthread_rwlock_unlock(&rwlock);
375         }
376 }
377
378 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)
379 {
380         struct afb_hook *hook;
381
382         /* alloc the result */
383         hook = malloc(sizeof *hook);
384         if (hook == NULL)
385                 return NULL;
386
387         /* get a copy of the names */
388         hook->api = api ? strdup(api) : NULL;
389         hook->verb = verb ? strdup(verb) : NULL;
390         if ((api && !hook->api) || (verb && !hook->verb)) {
391                 free(hook->api);
392                 free(hook->verb);
393                 free(hook);
394                 return NULL;
395         }
396
397         /* initialise the rest */
398         hook->session = session;
399         if (session)
400                 afb_session_addref(session);
401         hook->refcount = 1;
402         hook->flags = flags;
403         hook->reqitf = itf ? itf : &hook_xreq_default_itf;
404         hook->closure = closure;
405
406         /* record the hook */
407         pthread_rwlock_wrlock(&rwlock);
408         hook->next = list_of_hooks;
409         list_of_hooks = hook;
410         pthread_rwlock_unlock(&rwlock);
411
412         /* returns it */
413         return hook;
414 }
415
416 struct afb_hook *afb_hook_addref(struct afb_hook *hook)
417 {
418         pthread_rwlock_wrlock(&rwlock);
419         hook->refcount++;
420         pthread_rwlock_unlock(&rwlock);
421         return hook;
422 }
423
424 void afb_hook_unref(struct afb_hook *hook)
425 {
426         struct afb_hook **prv;
427
428         if (hook) {
429                 pthread_rwlock_wrlock(&rwlock);
430                 if (--hook->refcount)
431                         hook = NULL;
432                 else {
433                         /* unlink */
434                         prv = &list_of_hooks;
435                         while (*prv && *prv != hook)
436                                 prv = &(*prv)->next;
437                         if(*prv)
438                                 *prv = hook->next;
439                 }
440                 pthread_rwlock_unlock(&rwlock);
441                 if (hook) {
442                         /* free */
443                         free(hook->api);
444                         free(hook->verb);
445                         if (hook->session)
446                                 afb_session_unref(hook->session);
447                         free(hook);
448                 }
449         }
450 }
451