46bcf6dbc426bc58bbbd70629c6aed35638ce3e7
[src/app-framework-binder.git] / src / afb-evt.c
1 /*
2  * Copyright (C) 2015-2019 "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 <stdlib.h>
21 #include <string.h>
22 #include <assert.h>
23 #include <errno.h>
24 #include <pthread.h>
25
26 #include <json-c/json.h>
27 #include <afb/afb-event-x2-itf.h>
28 #include <afb/afb-event-x1.h>
29
30 #include "afb-evt.h"
31 #include "afb-hook.h"
32 #include "verbose.h"
33 #include "jobs.h"
34
35 struct afb_evt_watch;
36
37 /*
38  * Structure for event listeners
39  */
40 struct afb_evt_listener {
41
42         /* chaining listeners */
43         struct afb_evt_listener *next;
44
45         /* interface for callbacks */
46         const struct afb_evt_itf *itf;
47
48         /* closure for the callback */
49         void *closure;
50
51         /* head of the list of events listened */
52         struct afb_evt_watch *watchs;
53
54         /* rwlock of the listener */
55         pthread_rwlock_t rwlock;
56
57         /* count of reference to the listener */
58         int refcount;
59 };
60
61 /*
62  * Structure for describing events
63  */
64 struct afb_evtid {
65
66         /* interface */
67         struct afb_event_x2 eventid;
68
69         /* next event */
70         struct afb_evtid *next;
71
72         /* head of the list of listeners watching the event */
73         struct afb_evt_watch *watchs;
74
75         /* rwlock of the event */
76         pthread_rwlock_t rwlock;
77
78 #if WITH_AFB_HOOK
79         /* hooking */
80         int hookflags;
81 #endif
82
83         /* refcount */
84         int refcount;
85
86         /* id of the event */
87         int id;
88
89         /* has client? */
90         int has_client;
91
92         /* fullname of the event */
93         char fullname[];
94 };
95
96 /*
97  * Structure for associating events and listeners
98  */
99 struct afb_evt_watch {
100
101         /* the evtid */
102         struct afb_evtid *evtid;
103
104         /* link to the next watcher for the same evtid */
105         struct afb_evt_watch *next_by_evtid;
106
107         /* the listener */
108         struct afb_evt_listener *listener;
109
110         /* link to the next watcher for the same listener */
111         struct afb_evt_watch *next_by_listener;
112
113         /* activity */
114         unsigned activity;
115 };
116
117 /*
118  * structure for job of broadcasting events
119  */
120 struct job_broadcast
121 {
122         /** object atached to the event */
123         struct json_object *object;
124
125         /** name of the event to broadcast */
126         char event[];
127 };
128
129 /*
130  * structure for job of broadcasting or pushing events
131  */
132 struct job_evtid
133 {
134         /** the event to broadcast */
135         struct afb_evtid *evtid;
136
137         /** object atached to the event */
138         struct json_object *object;
139 };
140
141 /* the interface for events */
142 static struct afb_event_x2_itf afb_evt_event_x2_itf = {
143         .broadcast = (void*)afb_evt_evtid_broadcast,
144         .push = (void*)afb_evt_evtid_push,
145         .unref = (void*)afb_evt_evtid_unref,
146         .name = (void*)afb_evt_evtid_name,
147         .addref = (void*)afb_evt_evtid_addref
148 };
149
150 #if WITH_AFB_HOOK
151 /* the interface for events */
152 static struct afb_event_x2_itf afb_evt_hooked_event_x2_itf = {
153         .broadcast = (void*)afb_evt_evtid_hooked_broadcast,
154         .push = (void*)afb_evt_evtid_hooked_push,
155         .unref = (void*)afb_evt_evtid_hooked_unref,
156         .name = (void*)afb_evt_evtid_hooked_name,
157         .addref = (void*)afb_evt_evtid_hooked_addref
158 };
159 #endif
160
161 /* job groups for events push/broadcast */
162 #define BROADCAST_JOB_GROUP  (&afb_evt_event_x2_itf)
163 #define PUSH_JOB_GROUP       (&afb_evt_event_x2_itf)
164
165 /* head of the list of listeners */
166 static pthread_rwlock_t listeners_rwlock = PTHREAD_RWLOCK_INITIALIZER;
167 static struct afb_evt_listener *listeners = NULL;
168
169 /* handling id of events */
170 static pthread_rwlock_t events_rwlock = PTHREAD_RWLOCK_INITIALIZER;
171 static struct afb_evtid *evtids = NULL;
172 static int event_id_counter = 0;
173 static int event_id_wrapped = 0;
174
175 /*
176  * Create structure for job of broadcasting string 'event' with 'object'
177  * Returns the created structure or NULL if out of memory
178  */
179 static struct job_broadcast *make_job_broadcast(const char *event, struct json_object *object)
180 {
181         size_t sz = 1 + strlen(event);
182         struct job_broadcast *jb = malloc(sz + sizeof *jb);
183         if (jb) {
184                 jb->object = object;
185                 memcpy(jb->event, event, sz);
186         }
187         return jb;
188 }
189
190 /*
191  * Destroy structure 'jb' for job of broadcasting string events
192  */
193 static void destroy_job_broadcast(struct job_broadcast *jb)
194 {
195         json_object_put(jb->object);
196         free(jb);
197 }
198
199 /*
200  * Create structure for job of broadcasting or pushing 'evtid' with 'object'
201  * Returns the created structure or NULL if out of memory
202  */
203 static struct job_evtid *make_job_evtid(struct afb_evtid *evtid, struct json_object *object)
204 {
205         struct job_evtid *je = malloc(sizeof *je);
206         if (je) {
207                 je->evtid = afb_evt_evtid_addref(evtid);
208                 je->object = object;
209         }
210         return je;
211 }
212
213 /*
214  * Destroy structure for job of broadcasting or pushing evtid
215  */
216 static void destroy_job_evtid(struct job_evtid *je)
217 {
218         afb_evt_evtid_unref(je->evtid);
219         json_object_put(je->object);
220         free(je);
221 }
222
223 /*
224  * Broadcasts the 'event' of 'id' with its 'object'
225  */
226 static void broadcast(const char *event, struct json_object *object)
227 {
228         struct afb_evt_listener *listener;
229
230         pthread_rwlock_rdlock(&listeners_rwlock);
231         listener = listeners;
232         while(listener) {
233                 if (listener->itf->broadcast != NULL)
234                         listener->itf->broadcast(listener->closure, event, json_object_get(object));
235                 listener = listener->next;
236         }
237         pthread_rwlock_unlock(&listeners_rwlock);
238 }
239
240 /*
241  * Jobs callback for broadcasting string asynchronously
242  */
243 static void broadcast_job(int signum, void *closure)
244 {
245         struct job_broadcast *jb = closure;
246
247         if (signum == 0)
248                 broadcast(jb->event, jb->object);
249         destroy_job_broadcast(jb);
250 }
251
252 /*
253  * Broadcasts the string 'event' with its 'object'
254  */
255 static int unhooked_broadcast(const char *event, struct json_object *object)
256 {
257         struct job_broadcast *jb;
258         int rc;
259
260         jb = make_job_broadcast(event, object);
261         if (jb == NULL) {
262                 ERROR("Cant't create broadcast string job item for %s(%s)",
263                         event, json_object_to_json_string(object));
264                 json_object_put(object);
265                 return -1;
266         }
267
268         rc = jobs_queue(BROADCAST_JOB_GROUP, 0, broadcast_job, jb);
269         if (rc) {
270                 ERROR("cant't queue broadcast string job item for %s(%s)",
271                         event, json_object_to_json_string(object));
272                 destroy_job_broadcast(jb);
273         }
274         return rc;
275 }
276
277 /*
278  * Broadcasts the event 'evtid' with its 'object'
279  * 'object' is released (like json_object_put)
280  * Returns the count of listener that received the event.
281  */
282 int afb_evt_evtid_broadcast(struct afb_evtid *evtid, struct json_object *object)
283 {
284         return unhooked_broadcast(evtid->fullname, object);
285 }
286
287 #if WITH_AFB_HOOK
288 /*
289  * Broadcasts the event 'evtid' with its 'object'
290  * 'object' is released (like json_object_put)
291  * Returns the count of listener that received the event.
292  */
293 int afb_evt_evtid_hooked_broadcast(struct afb_evtid *evtid, struct json_object *object)
294 {
295         int result;
296
297         json_object_get(object);
298
299         if (evtid->hookflags & afb_hook_flag_evt_broadcast_before)
300                 afb_hook_evt_broadcast_before(evtid->fullname, evtid->id, object);
301
302         result = afb_evt_evtid_broadcast(evtid, object);
303
304         if (evtid->hookflags & afb_hook_flag_evt_broadcast_after)
305                 afb_hook_evt_broadcast_after(evtid->fullname, evtid->id, object, result);
306
307         json_object_put(object);
308
309         return result;
310 }
311 #endif
312
313 /*
314  * Broadcasts the 'event' with its 'object'
315  * 'object' is released (like json_object_put)
316  * Returns the count of listener having receive the event.
317  */
318 int afb_evt_broadcast(const char *event, struct json_object *object)
319 {
320         int result;
321
322 #if WITH_AFB_HOOK
323         json_object_get(object);
324         afb_hook_evt_broadcast_before(event, 0, object);
325 #endif
326
327         result = unhooked_broadcast(event, object);
328
329 #if WITH_AFB_HOOK
330         afb_hook_evt_broadcast_after(event, 0, object, result);
331         json_object_put(object);
332 #endif
333         return result;
334 }
335
336 /*
337  * Pushes the event 'evtid' with 'obj' to its listeners
338  * Returns the count of listener that received the event.
339  */
340 static void push_evtid(struct afb_evtid *evtid, struct json_object *object)
341 {
342         int has_client;
343         struct afb_evt_watch *watch;
344         struct afb_evt_listener *listener;
345
346         has_client = 0;
347         pthread_rwlock_rdlock(&evtid->rwlock);
348         watch = evtid->watchs;
349         while(watch) {
350                 listener = watch->listener;
351                 assert(listener->itf->push != NULL);
352                 if (watch->activity != 0) {
353                         listener->itf->push(listener->closure, evtid->fullname, evtid->id, json_object_get(object));
354                         has_client = 1;
355                 }
356                 watch = watch->next_by_evtid;
357         }
358         evtid->has_client = has_client;
359         pthread_rwlock_unlock(&evtid->rwlock);
360 }
361
362 /*
363  * Jobs callback for pushing evtid asynchronously
364  */
365 static void push_job_evtid(int signum, void *closure)
366 {
367         struct job_evtid *je = closure;
368
369         if (signum == 0)
370                 push_evtid(je->evtid, je->object);
371         destroy_job_evtid(je);
372 }
373
374 /*
375  * Pushes the event 'evtid' with 'obj' to its listeners
376  * 'obj' is released (like json_object_put)
377  * Returns 1 if at least one listener exists or 0 if no listener exists or
378  * -1 in case of error and the event can't be delivered
379  */
380 int afb_evt_evtid_push(struct afb_evtid *evtid, struct json_object *object)
381 {
382         struct job_evtid *je;
383         int rc;
384
385         je = make_job_evtid(evtid, object);
386         if (je == NULL) {
387                 ERROR("Cant't create push evtid job item for %s(%s)",
388                         evtid->fullname, json_object_to_json_string(object));
389                 json_object_put(object);
390                 return -1;
391         }
392
393         rc = jobs_queue(PUSH_JOB_GROUP, 0, push_job_evtid, je);
394         if (rc == 0)
395                 rc = evtid->has_client;
396         else {
397                 ERROR("cant't queue push evtid job item for %s(%s)",
398                         evtid->fullname, json_object_to_json_string(object));
399                 destroy_job_evtid(je);
400         }
401
402         return rc;
403 }
404
405 #if WITH_AFB_HOOK
406 /*
407  * Pushes the event 'evtid' with 'obj' to its listeners
408  * 'obj' is released (like json_object_put)
409  * Emits calls to hooks.
410  * Returns the count of listener taht received the event.
411  */
412 int afb_evt_evtid_hooked_push(struct afb_evtid *evtid, struct json_object *obj)
413 {
414
415         int result;
416
417         /* lease the object */
418         json_object_get(obj);
419
420         /* hook before push */
421         if (evtid->hookflags & afb_hook_flag_evt_push_before)
422                 afb_hook_evt_push_before(evtid->fullname, evtid->id, obj);
423
424         /* push */
425         result = afb_evt_evtid_push(evtid, obj);
426
427         /* hook after push */
428         if (evtid->hookflags & afb_hook_flag_evt_push_after)
429                 afb_hook_evt_push_after(evtid->fullname, evtid->id, obj, result);
430
431         /* release the object */
432         json_object_put(obj);
433         return result;
434 }
435 #endif
436
437 /*
438  * remove the 'watch'
439  */
440 static void remove_watch(struct afb_evt_watch *watch)
441 {
442         struct afb_evt_watch **prv;
443         struct afb_evtid *evtid;
444         struct afb_evt_listener *listener;
445
446         /* notify listener if needed */
447         evtid = watch->evtid;
448         listener = watch->listener;
449         if (watch->activity != 0 && listener->itf->remove != NULL)
450                 listener->itf->remove(listener->closure, evtid->fullname, evtid->id);
451
452         /* unlink the watch for its event */
453         prv = &evtid->watchs;
454         while(*prv != watch)
455                 prv = &(*prv)->next_by_evtid;
456         *prv = watch->next_by_evtid;
457
458         /* unlink the watch for its listener */
459         prv = &listener->watchs;
460         while(*prv != watch)
461                 prv = &(*prv)->next_by_listener;
462         *prv = watch->next_by_listener;
463
464         /* recycle memory */
465         free(watch);
466 }
467
468 /*
469  * Creates an event of name 'fullname' and returns it or NULL on error.
470  */
471 struct afb_evtid *afb_evt_evtid_create(const char *fullname)
472 {
473         size_t len;
474         struct afb_evtid *evtid, *oevt;
475
476         /* allocates the event */
477         len = strlen(fullname);
478         evtid = malloc(len + 1 + sizeof * evtid);
479         if (evtid == NULL)
480                 goto error;
481
482         /* allocates the id */
483         pthread_rwlock_wrlock(&events_rwlock);
484         do {
485                 if (++event_id_counter < 0) {
486                         event_id_wrapped = 1;
487                         event_id_counter = 1024; /* heuristic: small numbers are not destroyed */
488                 }
489                 if (!event_id_wrapped)
490                         break;
491                 oevt = evtids;
492                 while(oevt != NULL && oevt->id != event_id_counter)
493                         oevt = oevt->next;
494         } while (oevt != NULL);
495
496         /* initialize the event */
497         memcpy(evtid->fullname, fullname, len + 1);
498         evtid->next = evtids;
499         evtid->refcount = 1;
500         evtid->watchs = NULL;
501         evtid->id = event_id_counter;
502         evtid->has_client = 0;
503         pthread_rwlock_init(&evtid->rwlock, NULL);
504         evtids = evtid;
505 #if WITH_AFB_HOOK
506         evtid->hookflags = afb_hook_flags_evt(evtid->fullname);
507         evtid->eventid.itf = evtid->hookflags ? &afb_evt_hooked_event_x2_itf : &afb_evt_event_x2_itf;
508         if (evtid->hookflags & afb_hook_flag_evt_create)
509                 afb_hook_evt_create(evtid->fullname, evtid->id);
510 #else
511         evtid->eventid.itf = &afb_evt_event_x2_itf;
512 #endif
513         pthread_rwlock_unlock(&events_rwlock);
514
515         /* returns the event */
516         return evtid;
517 error:
518         return NULL;
519 }
520
521 /*
522  * Creates an event of name 'prefix'/'name' and returns it or NULL on error.
523  */
524 struct afb_evtid *afb_evt_evtid_create2(const char *prefix, const char *name)
525 {
526         size_t prelen, postlen;
527         char *fullname;
528
529         /* makes the event fullname */
530         prelen = strlen(prefix);
531         postlen = strlen(name);
532         fullname = alloca(prelen + postlen + 2);
533         memcpy(fullname, prefix, prelen);
534         fullname[prelen] = '/';
535         memcpy(fullname + prelen + 1, name, postlen + 1);
536
537         /* create the event */
538         return afb_evt_evtid_create(fullname);
539 }
540
541 /*
542  * increment the reference count of the event 'evtid'
543  */
544 struct afb_evtid *afb_evt_evtid_addref(struct afb_evtid *evtid)
545 {
546         __atomic_add_fetch(&evtid->refcount, 1, __ATOMIC_RELAXED);
547         return evtid;
548 }
549
550 #if WITH_AFB_HOOK
551 /*
552  * increment the reference count of the event 'evtid'
553  */
554 struct afb_evtid *afb_evt_evtid_hooked_addref(struct afb_evtid *evtid)
555 {
556         if (evtid->hookflags & afb_hook_flag_evt_addref)
557                 afb_hook_evt_addref(evtid->fullname, evtid->id);
558         return afb_evt_evtid_addref(evtid);
559 }
560 #endif
561
562 /*
563  * decrement the reference count of the event 'evtid'
564  * and destroy it when the count reachs zero
565  */
566 void afb_evt_evtid_unref(struct afb_evtid *evtid)
567 {
568         int found;
569         struct afb_evtid **prv;
570         struct afb_evt_listener *listener;
571
572         if (!__atomic_sub_fetch(&evtid->refcount, 1, __ATOMIC_RELAXED)) {
573                 /* unlinks the event if valid! */
574                 pthread_rwlock_wrlock(&events_rwlock);
575                 found = 0;
576                 prv = &evtids;
577                 while (*prv && !(found = (*prv == evtid)))
578                         prv = &(*prv)->next;
579                 if (found)
580                         *prv = evtid->next;
581                 pthread_rwlock_unlock(&events_rwlock);
582
583                 /* destroys the event */
584                 if (!found)
585                         ERROR("event not found");
586                 else {
587                         /* removes all watchers */
588                         while(evtid->watchs != NULL) {
589                                 listener = evtid->watchs->listener;
590                                 pthread_rwlock_wrlock(&listener->rwlock);
591                                 pthread_rwlock_wrlock(&evtid->rwlock);
592                                 remove_watch(evtid->watchs);
593                                 pthread_rwlock_unlock(&evtid->rwlock);
594                                 pthread_rwlock_unlock(&listener->rwlock);
595                         }
596
597                         /* free */
598                         pthread_rwlock_destroy(&evtid->rwlock);
599                         free(evtid);
600                 }
601         }
602 }
603
604 #if WITH_AFB_HOOK
605 /*
606  * decrement the reference count of the event 'evtid'
607  * and destroy it when the count reachs zero
608  */
609 void afb_evt_evtid_hooked_unref(struct afb_evtid *evtid)
610 {
611         if (evtid->hookflags & afb_hook_flag_evt_unref)
612                 afb_hook_evt_unref(evtid->fullname, evtid->id);
613         afb_evt_evtid_unref(evtid);
614 }
615 #endif
616
617 /*
618  * Returns the true name of the 'event'
619  */
620 const char *afb_evt_evtid_fullname(struct afb_evtid *evtid)
621 {
622         return evtid->fullname;
623 }
624
625 /*
626  * Returns the name of the 'event'
627  */
628 const char *afb_evt_evtid_name(struct afb_evtid *evtid)
629 {
630         const char *name = strchr(evtid->fullname, '/');
631         return name ? name + 1 : evtid->fullname;
632 }
633
634 #if WITH_AFB_HOOK
635 /*
636  * Returns the name associated to the event 'evtid'.
637  */
638 const char *afb_evt_evtid_hooked_name(struct afb_evtid *evtid)
639 {
640         const char *result = afb_evt_evtid_name(evtid);
641         if (evtid->hookflags & afb_hook_flag_evt_name)
642                 afb_hook_evt_name(evtid->fullname, evtid->id, result);
643         return result;
644 }
645 #endif
646
647 /*
648  * Returns the id of the 'event'
649  */
650 int afb_evt_evtid_id(struct afb_evtid *evtid)
651 {
652         return evtid->id;
653 }
654
655 /*
656  * Returns an instance of the listener defined by the 'send' callback
657  * and the 'closure'.
658  * Returns NULL in case of memory depletion.
659  */
660 struct afb_evt_listener *afb_evt_listener_create(const struct afb_evt_itf *itf, void *closure)
661 {
662         struct afb_evt_listener *listener;
663
664         /* search if an instance already exists */
665         pthread_rwlock_wrlock(&listeners_rwlock);
666         listener = listeners;
667         while (listener != NULL) {
668                 if (listener->itf == itf && listener->closure == closure) {
669                         listener = afb_evt_listener_addref(listener);
670                         goto found;
671                 }
672                 listener = listener->next;
673         }
674
675         /* allocates */
676         listener = calloc(1, sizeof *listener);
677         if (listener != NULL) {
678                 /* init */
679                 listener->itf = itf;
680                 listener->closure = closure;
681                 listener->watchs = NULL;
682                 listener->refcount = 1;
683                 pthread_rwlock_init(&listener->rwlock, NULL);
684                 listener->next = listeners;
685                 listeners = listener;
686         }
687  found:
688         pthread_rwlock_unlock(&listeners_rwlock);
689         return listener;
690 }
691
692 /*
693  * Increases the reference count of 'listener' and returns it
694  */
695 struct afb_evt_listener *afb_evt_listener_addref(struct afb_evt_listener *listener)
696 {
697         __atomic_add_fetch(&listener->refcount, 1, __ATOMIC_RELAXED);
698         return listener;
699 }
700
701 /*
702  * Decreases the reference count of the 'listener' and destroys it
703  * when no more used.
704  */
705 void afb_evt_listener_unref(struct afb_evt_listener *listener)
706 {
707         struct afb_evt_listener **prv;
708         struct afb_evtid *evtid;
709
710         if (listener && !__atomic_sub_fetch(&listener->refcount, 1, __ATOMIC_RELAXED)) {
711
712                 /* unlink the listener */
713                 pthread_rwlock_wrlock(&listeners_rwlock);
714                 prv = &listeners;
715                 while (*prv != listener)
716                         prv = &(*prv)->next;
717                 *prv = listener->next;
718                 pthread_rwlock_unlock(&listeners_rwlock);
719
720                 /* remove the watchers */
721                 pthread_rwlock_wrlock(&listener->rwlock);
722                 while (listener->watchs != NULL) {
723                         evtid = listener->watchs->evtid;
724                         pthread_rwlock_wrlock(&evtid->rwlock);
725                         remove_watch(listener->watchs);
726                         pthread_rwlock_unlock(&evtid->rwlock);
727                 }
728                 pthread_rwlock_unlock(&listener->rwlock);
729
730                 /* free the listener */
731                 pthread_rwlock_destroy(&listener->rwlock);
732                 free(listener);
733         }
734 }
735
736 /*
737  * Makes the 'listener' watching 'evtid'
738  * Returns 0 in case of success or else -1.
739  */
740 int afb_evt_watch_add_evtid(struct afb_evt_listener *listener, struct afb_evtid *evtid)
741 {
742         struct afb_evt_watch *watch;
743
744         /* check parameter */
745         if (listener->itf->push == NULL) {
746                 errno = EINVAL;
747                 return -1;
748         }
749
750         /* search the existing watch for the listener */
751         pthread_rwlock_wrlock(&listener->rwlock);
752         watch = listener->watchs;
753         while(watch != NULL) {
754                 if (watch->evtid == evtid)
755                         goto found;
756                 watch = watch->next_by_listener;
757         }
758
759         /* not found, allocate a new */
760         watch = malloc(sizeof *watch);
761         if (watch == NULL) {
762                 pthread_rwlock_unlock(&listener->rwlock);
763                 errno = ENOMEM;
764                 return -1;
765         }
766
767         /* initialise and link */
768         watch->evtid = evtid;
769         watch->activity = 0;
770         watch->listener = listener;
771         watch->next_by_listener = listener->watchs;
772         listener->watchs = watch;
773         pthread_rwlock_wrlock(&evtid->rwlock);
774         watch->next_by_evtid = evtid->watchs;
775         evtid->watchs = watch;
776         pthread_rwlock_unlock(&evtid->rwlock);
777
778 found:
779         if (watch->activity == 0 && listener->itf->add != NULL)
780                 listener->itf->add(listener->closure, evtid->fullname, evtid->id);
781         watch->activity++;
782         evtid->has_client = 1;
783         pthread_rwlock_unlock(&listener->rwlock);
784
785         return 0;
786 }
787
788 /*
789  * Avoids the 'listener' to watch 'evtid'
790  * Returns 0 in case of success or else -1.
791  */
792 int afb_evt_watch_sub_evtid(struct afb_evt_listener *listener, struct afb_evtid *evtid)
793 {
794         struct afb_evt_watch *watch;
795
796         /* search the existing watch */
797         pthread_rwlock_wrlock(&listener->rwlock);
798         watch = listener->watchs;
799         while(watch != NULL) {
800                 if (watch->evtid == evtid) {
801                         if (watch->activity != 0) {
802                                 watch->activity--;
803                                 if (watch->activity == 0 && listener->itf->remove != NULL)
804                                         listener->itf->remove(listener->closure, evtid->fullname, evtid->id);
805                         }
806                         pthread_rwlock_unlock(&listener->rwlock);
807                         return 0;
808                 }
809                 watch = watch->next_by_listener;
810         }
811         pthread_rwlock_unlock(&listener->rwlock);
812         errno = ENOENT;
813         return -1;
814 }
815
816 #if WITH_AFB_HOOK
817 /*
818  * update the hooks for events
819  */
820 void afb_evt_update_hooks()
821 {
822         struct afb_evtid *evtid;
823
824         pthread_rwlock_rdlock(&events_rwlock);
825         for (evtid = evtids ; evtid ; evtid = evtid->next) {
826                 evtid->hookflags = afb_hook_flags_evt(evtid->fullname);
827                 evtid->eventid.itf = evtid->hookflags ? &afb_evt_hooked_event_x2_itf : &afb_evt_event_x2_itf;
828         }
829         pthread_rwlock_unlock(&events_rwlock);
830 }
831 #endif
832
833 inline struct afb_evtid *afb_evt_event_x2_to_evtid(struct afb_event_x2 *eventid)
834 {
835         return (struct afb_evtid*)eventid;
836 }
837
838 inline struct afb_event_x2 *afb_evt_event_x2_from_evtid(struct afb_evtid *evtid)
839 {
840         return &evtid->eventid;
841 }
842
843 /*
844  * Creates an event of 'fullname' and returns it.
845  * Returns an event with closure==NULL in case of error.
846  */
847 struct afb_event_x2 *afb_evt_event_x2_create(const char *fullname)
848 {
849         return afb_evt_event_x2_from_evtid(afb_evt_evtid_create(fullname));
850 }
851
852 /*
853  * Creates an event of name 'prefix'/'name' and returns it.
854  * Returns an event with closure==NULL in case of error.
855  */
856 struct afb_event_x2 *afb_evt_event_x2_create2(const char *prefix, const char *name)
857 {
858         return afb_evt_event_x2_from_evtid(afb_evt_evtid_create2(prefix, name));
859 }
860
861 /*
862  * Returns the fullname of the 'eventid'
863  */
864 const char *afb_evt_event_x2_fullname(struct afb_event_x2 *eventid)
865 {
866         struct afb_evtid *evtid = afb_evt_event_x2_to_evtid(eventid);
867         return evtid ? evtid->fullname : NULL;
868 }
869
870 /*
871  * Returns the id of the 'eventid'
872  */
873 int afb_evt_event_x2_id(struct afb_event_x2 *eventid)
874 {
875         struct afb_evtid *evtid = afb_evt_event_x2_to_evtid(eventid);
876         return evtid ? evtid->id : 0;
877 }
878
879 /*
880  * Makes the 'listener' watching 'eventid'
881  * Returns 0 in case of success or else -1.
882  */
883 int afb_evt_event_x2_add_watch(struct afb_evt_listener *listener, struct afb_event_x2 *eventid)
884 {
885         struct afb_evtid *evtid = afb_evt_event_x2_to_evtid(eventid);
886
887         /* check parameter */
888         if (!evtid) {
889                 errno = EINVAL;
890                 return -1;
891         }
892
893         /* search the existing watch for the listener */
894         return afb_evt_watch_add_evtid(listener, evtid);
895 }
896
897 /*
898  * Avoids the 'listener' to watch 'eventid'
899  * Returns 0 in case of success or else -1.
900  */
901 int afb_evt_event_x2_remove_watch(struct afb_evt_listener *listener, struct afb_event_x2 *eventid)
902 {
903         struct afb_evtid *evtid = afb_evt_event_x2_to_evtid(eventid);
904
905         /* check parameter */
906         if (!evtid) {
907                 errno = EINVAL;
908                 return -1;
909         }
910
911         /* search the existing watch */
912         return afb_evt_watch_sub_evtid(listener, evtid);
913 }
914
915 int afb_evt_event_x2_push(struct afb_event_x2 *eventid, struct json_object *object)
916 #if WITH_AFB_HOOK
917 {
918         struct afb_evtid *evtid = afb_evt_event_x2_to_evtid(eventid);
919         if (evtid)
920                 return afb_evt_evtid_hooked_push(evtid, object);
921         json_object_put(object);
922         return 0;
923 }
924 #else
925         __attribute__((alias("afb_evt_event_x2_unhooked_push")));
926 #endif
927
928 int afb_evt_event_x2_unhooked_push(struct afb_event_x2 *eventid, struct json_object *object)
929 {
930         struct afb_evtid *evtid = afb_evt_event_x2_to_evtid(eventid);
931         if (evtid)
932                 return afb_evt_evtid_push(evtid, object);
933         json_object_put(object);
934         return 0;
935 }
936
937 #if WITH_LEGACY_BINDING_V1 || WITH_LEGACY_BINDING_V2
938 struct afb_event_x1 afb_evt_event_from_evtid(struct afb_evtid *evtid)
939 {
940         return evtid
941 #if WITH_AFB_HOOK
942                 ? (struct afb_event_x1){ .itf = &afb_evt_hooked_event_x2_itf, .closure = &evtid->eventid }
943 #else
944                 ? (struct afb_event_x1){ .itf = &afb_evt_event_x2_itf, .closure = &evtid->eventid }
945 #endif
946                 : (struct afb_event_x1){ .itf = NULL, .closure = NULL };
947 }
948 #endif
949
950 void afb_evt_event_x2_unref(struct afb_event_x2 *eventid)
951 {
952         struct afb_evtid *evtid = afb_evt_event_x2_to_evtid(eventid);
953         if (evtid)
954                 afb_evt_evtid_unref(evtid);
955 }
956
957 struct afb_event_x2 *afb_evt_event_x2_addref(struct afb_event_x2 *eventid)
958 {
959         struct afb_evtid *evtid = afb_evt_event_x2_to_evtid(eventid);
960         if (evtid)
961                 afb_evt_evtid_addref(evtid);
962         return eventid;
963 }
964