Simplify code by using a taskprocessor for dispatching events in the Asterisk core.
[asterisk/asterisk.git] / main / event.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2007, Digium, Inc.
5  *
6  * Russell Bryant <russell@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  *
21  * \brief Internal generic event system
22  *
23  * \author Russell Bryant <russell@digium.com>
24  */
25
26 #include "asterisk.h"
27
28 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
29
30 #include "asterisk/_private.h"
31 #include "asterisk/event.h"
32 #include "asterisk/linkedlists.h"
33 #include "asterisk/dlinkedlists.h"
34 #include "asterisk/lock.h"
35 #include "asterisk/utils.h"
36 #include "asterisk/unaligned.h"
37 #include "asterisk/taskprocessor.h"
38
39 struct ast_taskprocessor *event_dispatcher;
40
41 /*!
42  * \brief An event information element
43  *
44  * \note The format of this structure is important.  Since these events may
45  *       be sent directly over a network, changing this structure will break
46  *       compatibility with older versions.  However, at this point, this code
47  *       has not made it into a release, so it is still fair game for change.
48  */
49 struct ast_event_ie {
50         enum ast_event_ie_type ie_type:16;
51         /*! Total length of the IE payload */
52         uint16_t ie_payload_len;
53         unsigned char ie_payload[0];
54 } __attribute__ ((packed));
55
56 /*!
57  * \brief An event
58  *
59  * An ast_event consists of an event header (this structure), and zero or
60  * more information elements defined by ast_event_ie.
61  *
62  * \note The format of this structure is important.  Since these events may
63  *       be sent directly over a network, changing this structure will break
64  *       compatibility with older versions.  However, at this point, this code
65  *       has not made it into a release, so it is still fair game for change.
66  */
67 struct ast_event {
68         /*! Event type */
69         enum ast_event_type type:16;
70         /*! Total length of the event */
71         uint16_t event_len:16;
72         /*! The data payload of the event, made up of information elements */
73         unsigned char payload[0];
74 } __attribute__ ((packed));
75
76 struct ast_event_ref {
77         struct ast_event *event;
78         AST_LIST_ENTRY(ast_event_ref) entry;
79 };
80
81 struct ast_event_iterator {
82         uint16_t event_len;
83         const struct ast_event *event;
84         struct ast_event_ie *ie;
85 };
86
87 struct ast_event_ie_val {
88         AST_LIST_ENTRY(ast_event_ie_val) entry;
89         enum ast_event_ie_type ie_type;
90         enum ast_event_ie_pltype ie_pltype;
91         union {
92                 uint32_t uint;
93                 const char *str;
94         } payload;
95 };
96
97 /*! \brief Event subscription */
98 struct ast_event_sub {
99         enum ast_event_type type;
100         ast_event_cb_t cb;
101         void *userdata;
102         uint32_t uniqueid;
103         AST_LIST_HEAD_NOLOCK(, ast_event_ie_val) ie_vals;
104         AST_RWDLLIST_ENTRY(ast_event_sub) entry;
105 };
106
107 static uint32_t sub_uniqueid;
108
109 /*! \brief Event subscriptions
110  * The event subscribers are indexed by which event they are subscribed to */
111 static AST_RWDLLIST_HEAD(ast_event_sub_list, ast_event_sub) ast_event_subs[AST_EVENT_TOTAL];
112
113 /*! \brief Cached events
114  * The event cache is indexed on the event type.  The purpose of this is 
115  * for events that express some sort of state.  So, when someone first
116  * needs to know this state, it can get the last known state from the cache. */
117 static AST_RWLIST_HEAD(ast_event_ref_list, ast_event_ref) ast_event_cache[AST_EVENT_TOTAL];
118
119 static void ast_event_ie_val_destroy(struct ast_event_ie_val *ie_val)
120 {
121         if (ie_val->ie_pltype == AST_EVENT_IE_PLTYPE_STR)
122                 ast_free((void *) ie_val->payload.str);
123
124         ast_free(ie_val);
125 }
126
127 enum ast_event_subscriber_res ast_event_check_subscriber(enum ast_event_type type, ...)
128 {
129         va_list ap;
130         enum ast_event_ie_type ie_type;
131         enum ast_event_subscriber_res res = AST_EVENT_SUB_NONE;
132         struct ast_event_ie_val *ie_val, *sub_ie_val;
133         struct ast_event_sub *sub;
134         AST_LIST_HEAD_NOLOCK_STATIC(ie_vals, ast_event_ie_val);
135
136         if (type >= AST_EVENT_TOTAL) {
137                 ast_log(LOG_ERROR, "%u is an invalid type!\n", type);
138                 return res;
139         }
140
141         va_start(ap, type);
142         for (ie_type = va_arg(ap, enum ast_event_type);
143                 ie_type != AST_EVENT_IE_END;
144                 ie_type = va_arg(ap, enum ast_event_type))
145         {
146                 struct ast_event_ie_val *ie_val = alloca(sizeof(*ie_val));
147                 memset(ie_val, 0, sizeof(*ie_val));
148                 ie_val->ie_type = ie_type;
149                 ie_val->ie_pltype = va_arg(ap, enum ast_event_ie_pltype);
150                 if (ie_val->ie_pltype == AST_EVENT_IE_PLTYPE_UINT)
151                         ie_val->payload.uint = va_arg(ap, uint32_t);
152                 else if (ie_val->ie_pltype == AST_EVENT_IE_PLTYPE_STR)
153                         ie_val->payload.str = ast_strdupa(va_arg(ap, const char *));
154                 AST_LIST_INSERT_TAIL(&ie_vals, ie_val, entry);
155         }
156         va_end(ap);
157
158         AST_RWDLLIST_RDLOCK(&ast_event_subs[type]);
159         AST_RWDLLIST_TRAVERSE(&ast_event_subs[type], sub, entry) {
160                 AST_LIST_TRAVERSE(&ie_vals, ie_val, entry) {
161                         AST_LIST_TRAVERSE(&sub->ie_vals, sub_ie_val, entry) {
162                                 if (sub_ie_val->ie_type == ie_val->ie_type)
163                                         break;
164                         }
165                         if (!sub_ie_val) {
166                                 if (ie_val->ie_pltype == AST_EVENT_IE_PLTYPE_EXISTS)
167                                         break;
168                                 continue;
169                         }
170                         /* The subscriber doesn't actually care what the value is */
171                         if (sub_ie_val->ie_pltype == AST_EVENT_IE_PLTYPE_EXISTS)
172                                 continue;
173                         if (ie_val->ie_pltype == AST_EVENT_IE_PLTYPE_UINT &&
174                                 ie_val->payload.uint != sub_ie_val->payload.uint)
175                                 break;
176                         if (ie_val->ie_pltype == AST_EVENT_IE_PLTYPE_STR &&
177                                 strcmp(ie_val->payload.str, sub_ie_val->payload.str))
178                                 break;
179                 }
180                 if (!ie_val)
181                         break;
182         }
183         AST_RWDLLIST_UNLOCK(&ast_event_subs[type]);
184
185         if (sub) /* All parameters were matched */
186                 return AST_EVENT_SUB_EXISTS;
187
188         AST_RWDLLIST_RDLOCK(&ast_event_subs[AST_EVENT_ALL]);
189         if (!AST_DLLIST_EMPTY(&ast_event_subs[AST_EVENT_ALL]))
190                 res = AST_EVENT_SUB_EXISTS;
191         AST_RWDLLIST_UNLOCK(&ast_event_subs[AST_EVENT_ALL]);
192
193         return res;
194 }
195
196 /*! \brief Send AST_EVENT_SUB events to this subscriber of ... subscriber events */
197 void ast_event_report_subs(const struct ast_event_sub *event_sub)
198 {
199         struct ast_event *event;
200         struct ast_event_sub *sub;
201         enum ast_event_type event_type = -1;
202         struct ast_event_ie_val *ie_val;
203
204         if (event_sub->type != AST_EVENT_SUB)
205                 return;
206
207         AST_LIST_TRAVERSE(&event_sub->ie_vals, ie_val, entry) {
208                 if (ie_val->ie_type == AST_EVENT_IE_EVENTTYPE) {
209                         event_type = ie_val->payload.uint;
210                         break;
211                 }
212         }
213
214         if (event_type == -1)
215                 return;
216
217         AST_RWDLLIST_RDLOCK(&ast_event_subs[event_type]);
218         AST_RWDLLIST_TRAVERSE(&ast_event_subs[event_type], sub, entry) {
219                 if (event_sub == sub)
220                         continue;
221
222                 event = ast_event_new(AST_EVENT_SUB,
223                         AST_EVENT_IE_UNIQUEID,  AST_EVENT_IE_PLTYPE_UINT, sub->uniqueid,
224                         AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, sub->type,
225                         AST_EVENT_IE_END);
226
227                 AST_LIST_TRAVERSE(&sub->ie_vals, ie_val, entry) {
228                         switch (ie_val->ie_pltype) {
229                         case AST_EVENT_IE_PLTYPE_EXISTS:
230                                 ast_event_append_ie_uint(&event, AST_EVENT_IE_EXISTS, ie_val->ie_type);
231                                 break;
232                         case AST_EVENT_IE_PLTYPE_UINT:
233                                 ast_event_append_ie_uint(&event, ie_val->ie_type, ie_val->payload.uint);
234                                 break;
235                         case AST_EVENT_IE_PLTYPE_STR:
236                                 ast_event_append_ie_str(&event, ie_val->ie_type, ie_val->payload.str);
237                                 break;
238                         }
239                         if (!event)
240                                 break;
241                 }
242
243                 if (!event)
244                         continue;
245
246                 event_sub->cb(event, event_sub->userdata);
247
248                 ast_event_destroy(event);
249         }
250         AST_RWDLLIST_UNLOCK(&ast_event_subs[event_type]);
251 }
252
253 struct ast_event_sub *ast_event_subscribe(enum ast_event_type type, ast_event_cb_t cb, 
254         void *userdata, ...)
255 {
256         va_list ap;
257         enum ast_event_ie_type ie_type;
258         struct ast_event_sub *sub;
259         struct ast_event *event;
260
261         if (type >= AST_EVENT_TOTAL) {
262                 ast_log(LOG_ERROR, "%u is an invalid type!\n", type);
263                 return NULL;
264         }
265
266         if (!(sub = ast_calloc(1, sizeof(*sub))))
267                 return NULL;
268
269         va_start(ap, userdata);
270         for (ie_type = va_arg(ap, enum ast_event_type);
271                 ie_type != AST_EVENT_IE_END;
272                 ie_type = va_arg(ap, enum ast_event_type))
273         {
274                 struct ast_event_ie_val *ie_val;
275                 if (!(ie_val = ast_calloc(1, sizeof(*ie_val))))
276                         continue;
277                 ie_val->ie_type = ie_type;
278                 ie_val->ie_pltype = va_arg(ap, enum ast_event_ie_pltype);
279                 if (ie_val->ie_pltype == AST_EVENT_IE_PLTYPE_UINT)
280                         ie_val->payload.uint = va_arg(ap, uint32_t);
281                 else if (ie_val->ie_pltype == AST_EVENT_IE_PLTYPE_STR) {
282                         if (!(ie_val->payload.str = ast_strdup(va_arg(ap, const char *)))) {
283                                 ast_free(ie_val);
284                                 continue;
285                         }
286                 }
287                 AST_LIST_INSERT_TAIL(&sub->ie_vals, ie_val, entry);
288         }
289         va_end(ap);
290
291         sub->type = type;
292         sub->cb = cb;
293         sub->userdata = userdata;
294         sub->uniqueid = ast_atomic_fetchadd_int((int *) &sub_uniqueid, 1);
295
296         if (ast_event_check_subscriber(AST_EVENT_SUB,
297                 AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, type,
298                 AST_EVENT_IE_END) != AST_EVENT_SUB_NONE) {
299                 struct ast_event_ie_val *ie_val;
300
301                 event = ast_event_new(AST_EVENT_SUB,
302                         AST_EVENT_IE_UNIQUEID,  AST_EVENT_IE_PLTYPE_UINT, sub->uniqueid,
303                         AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, sub->type,
304                         AST_EVENT_IE_END);
305
306                 AST_LIST_TRAVERSE(&sub->ie_vals, ie_val, entry) {
307                         switch (ie_val->ie_pltype) {
308                         case AST_EVENT_IE_PLTYPE_EXISTS:
309                                 ast_event_append_ie_uint(&event, AST_EVENT_IE_EXISTS, ie_val->ie_type);
310                                 break;
311                         case AST_EVENT_IE_PLTYPE_UINT:
312                                 ast_event_append_ie_uint(&event, ie_val->ie_type, ie_val->payload.uint);
313                                 break;
314                         case AST_EVENT_IE_PLTYPE_STR:
315                                 ast_event_append_ie_str(&event, ie_val->ie_type, ie_val->payload.str);
316                                 break;
317                         }
318                         if (!event)
319                                 break;
320                 }
321
322                 if (event)
323                         ast_event_queue(event);
324         }
325
326         AST_RWDLLIST_WRLOCK(&ast_event_subs[type]);
327         AST_RWDLLIST_INSERT_TAIL(&ast_event_subs[type], sub, entry);
328         AST_RWDLLIST_UNLOCK(&ast_event_subs[type]);
329
330         return sub;
331 }
332
333 static void ast_event_sub_destroy(struct ast_event_sub *sub)
334 {
335         struct ast_event_ie_val *ie_val;
336
337         while ((ie_val = AST_LIST_REMOVE_HEAD(&sub->ie_vals, entry)))
338                 ast_event_ie_val_destroy(ie_val);
339
340         ast_free(sub);
341 }
342
343 void ast_event_unsubscribe(struct ast_event_sub *sub)
344 {
345         struct ast_event *event;
346
347         AST_RWDLLIST_WRLOCK(&ast_event_subs[sub->type]);
348         AST_DLLIST_REMOVE(&ast_event_subs[sub->type], sub, entry);
349         AST_RWDLLIST_UNLOCK(&ast_event_subs[sub->type]);
350
351         if (ast_event_check_subscriber(AST_EVENT_UNSUB,
352                 AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, sub->type,
353                 AST_EVENT_IE_END) != AST_EVENT_SUB_NONE) {
354                 
355                 event = ast_event_new(AST_EVENT_UNSUB,
356                         AST_EVENT_IE_UNIQUEID,  AST_EVENT_IE_PLTYPE_UINT, sub->uniqueid,
357                         AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, sub->type,
358                         AST_EVENT_IE_END);
359
360                 if (event)
361                         ast_event_queue(event);
362         }
363
364         ast_event_sub_destroy(sub);
365 }
366
367 void ast_event_iterator_init(struct ast_event_iterator *iterator, const struct ast_event *event)
368 {
369         iterator->event_len = ntohs(event->event_len);
370         iterator->event = event;
371         iterator->ie = (struct ast_event_ie *) ( ((char *) event) + sizeof(*event) );
372         return;
373 }
374
375 int ast_event_iterator_next(struct ast_event_iterator *iterator)
376 {
377         iterator->ie = (struct ast_event_ie *) ( ((char *) iterator->ie) + sizeof(*iterator->ie) + ntohs(iterator->ie->ie_payload_len));
378         return ((iterator->event_len <= (((char *) iterator->ie) - ((char *) iterator->event))) ? -1 : 0);
379 }
380
381 enum ast_event_ie_type ast_event_iterator_get_ie_type(struct ast_event_iterator *iterator)
382 {
383         return ntohs(iterator->ie->ie_type);
384 }
385
386 uint32_t ast_event_iterator_get_ie_uint(struct ast_event_iterator *iterator)
387 {
388         return ntohl(get_unaligned_uint32(iterator->ie->ie_payload));
389 }
390
391 const char *ast_event_iterator_get_ie_str(struct ast_event_iterator *iterator)
392 {
393         return (const char*)iterator->ie->ie_payload;
394 }
395
396 void *ast_event_iterator_get_ie_raw(struct ast_event_iterator *iterator)
397 {
398         return iterator->ie->ie_payload;
399 }
400
401 enum ast_event_type ast_event_get_type(const struct ast_event *event)
402 {
403         return ntohs(event->type);
404 }
405
406 uint32_t ast_event_get_ie_uint(const struct ast_event *event, enum ast_event_ie_type ie_type)
407 {
408         const uint32_t *ie_val;
409
410         ie_val = ast_event_get_ie_raw(event, ie_type);
411
412         return ie_val ? ntohl(get_unaligned_uint32(ie_val)) : 0;
413 }
414
415 const char *ast_event_get_ie_str(const struct ast_event *event, enum ast_event_ie_type ie_type)
416 {
417         return ast_event_get_ie_raw(event, ie_type);
418 }
419
420 const void *ast_event_get_ie_raw(const struct ast_event *event, enum ast_event_ie_type ie_type)
421 {
422         struct ast_event_iterator iterator;
423         int res = 0;
424
425         for (ast_event_iterator_init(&iterator, event); !res; res = ast_event_iterator_next(&iterator)) {
426                 if (ast_event_iterator_get_ie_type(&iterator) == ie_type)
427                         return ast_event_iterator_get_ie_raw(&iterator);
428         }
429
430         return NULL;
431 }
432
433 int ast_event_append_ie_str(struct ast_event **event, enum ast_event_ie_type ie_type,
434         const char *str)
435 {
436         return ast_event_append_ie_raw(event, ie_type, str, strlen(str) + 1);
437 }
438
439 int ast_event_append_ie_uint(struct ast_event **event, enum ast_event_ie_type ie_type,
440         uint32_t data)
441 {
442         data = htonl(data);
443         return ast_event_append_ie_raw(event, ie_type, &data, sizeof(data));
444 }
445
446 int ast_event_append_ie_raw(struct ast_event **event, enum ast_event_ie_type ie_type,
447         const void *data, size_t data_len)
448 {
449         struct ast_event_ie *ie;
450         unsigned int extra_len;
451         uint16_t event_len;
452
453         event_len = ntohs((*event)->event_len);
454         extra_len = sizeof(*ie) + data_len;
455
456         if (!(*event = ast_realloc(*event, event_len + extra_len)))
457                 return -1;
458
459         ie = (struct ast_event_ie *) ( ((char *) *event) + event_len );
460         ie->ie_type = htons(ie_type);
461         ie->ie_payload_len = htons(data_len);
462         memcpy(ie->ie_payload, data, data_len);
463
464         (*event)->event_len = htons(event_len + extra_len);
465
466         return 0;
467 }
468
469 struct ast_event *ast_event_new(enum ast_event_type type, ...)
470 {
471         va_list ap;
472         struct ast_event *event;
473         enum ast_event_type ie_type;
474         struct ast_event_ie_val *ie_val;
475         AST_LIST_HEAD_NOLOCK_STATIC(ie_vals, ast_event_ie_val);
476
477         /* Invalid type */
478         if (type >= AST_EVENT_TOTAL) {
479                 ast_log(LOG_WARNING, "Someone tried to create an event of invalid "
480                         "type '%d'!\n", type);
481                 return NULL;
482         }
483
484         va_start(ap, type);
485         for (ie_type = va_arg(ap, enum ast_event_type);
486                 ie_type != AST_EVENT_IE_END;
487                 ie_type = va_arg(ap, enum ast_event_type))
488         {
489                 struct ast_event_ie_val *ie_val = alloca(sizeof(*ie_val));
490                 memset(ie_val, 0, sizeof(*ie_val));
491                 ie_val->ie_type = ie_type;
492                 ie_val->ie_pltype = va_arg(ap, enum ast_event_ie_pltype);
493                 if (ie_val->ie_pltype == AST_EVENT_IE_PLTYPE_UINT)
494                         ie_val->payload.uint = va_arg(ap, uint32_t);
495                 else if (ie_val->ie_pltype == AST_EVENT_IE_PLTYPE_STR)
496                         ie_val->payload.str = ast_strdupa(va_arg(ap, const char *));
497                 AST_LIST_INSERT_TAIL(&ie_vals, ie_val, entry);
498         }
499         va_end(ap);
500
501         if (!(event = ast_calloc(1, sizeof(*event))))
502                 return NULL;
503
504         event->type = htons(type);
505         event->event_len = htons(sizeof(*event));
506
507         AST_LIST_TRAVERSE(&ie_vals, ie_val, entry) {
508                 if (ie_val->ie_pltype == AST_EVENT_IE_PLTYPE_STR)
509                         ast_event_append_ie_str(&event, ie_val->ie_type, ie_val->payload.str);
510                 else if (ie_val->ie_pltype == AST_EVENT_IE_PLTYPE_UINT)
511                         ast_event_append_ie_uint(&event, ie_val->ie_type, ie_val->payload.uint);
512
513                 if (!event)
514                         break;
515         }
516
517         return event;
518 }
519
520 void ast_event_destroy(struct ast_event *event)
521 {
522         ast_free(event);
523 }
524
525 static void ast_event_ref_destroy(struct ast_event_ref *event_ref)
526 {
527         ast_event_destroy(event_ref->event);
528         ast_free(event_ref);
529 }
530
531 static struct ast_event *ast_event_dup(const struct ast_event *event)
532 {
533         struct ast_event *dup_event;
534         uint16_t event_len;
535
536         event_len = ntohs(event->event_len);
537
538         if (!(dup_event = ast_calloc(1, event_len)))
539                 return NULL;
540         
541         memcpy(dup_event, event, event_len);
542
543         return dup_event;
544 }
545
546 struct ast_event *ast_event_get_cached(enum ast_event_type type, ...)
547 {
548         va_list ap;
549         enum ast_event_ie_type ie_type;
550         struct ast_event *dup_event = NULL;
551         struct ast_event_ref *event_ref;
552         struct cache_arg {
553                 AST_LIST_ENTRY(cache_arg) entry;
554                 enum ast_event_ie_type ie_type;
555                 enum ast_event_ie_pltype ie_pltype;
556                 union {
557                         uint32_t uint;
558                         const char *str;
559                 } payload;
560         } *cache_arg;
561         AST_LIST_HEAD_NOLOCK_STATIC(cache_args, cache_arg);
562
563         if (type >= AST_EVENT_TOTAL) {
564                 ast_log(LOG_ERROR, "%u is an invalid type!\n", type);
565                 return NULL;
566         }
567
568         va_start(ap, type);
569         for (ie_type = va_arg(ap, enum ast_event_type);
570                 ie_type != AST_EVENT_IE_END;
571                 ie_type = va_arg(ap, enum ast_event_type))
572         {
573                 cache_arg = alloca(sizeof(*cache_arg));
574                 memset(cache_arg, 0, sizeof(*cache_arg));
575                 cache_arg->ie_type = ie_type;
576                 cache_arg->ie_pltype = va_arg(ap, enum ast_event_ie_pltype);
577                 if (cache_arg->ie_pltype == AST_EVENT_IE_PLTYPE_UINT)
578                         cache_arg->payload.uint = va_arg(ap, uint32_t);
579                 else if (cache_arg->ie_pltype == AST_EVENT_IE_PLTYPE_STR)
580                         cache_arg->payload.str = ast_strdupa(va_arg(ap, const char *));
581                 AST_LIST_INSERT_TAIL(&cache_args, cache_arg, entry);
582         }
583         va_end(ap);
584
585         if (AST_LIST_EMPTY(&cache_args)) {
586                 ast_log(LOG_ERROR, "Events can not be retrieved from the cache without "
587                         "specifying at least one IE type!\n");
588                 return NULL;
589         }
590
591         AST_RWLIST_RDLOCK(&ast_event_cache[type]);
592         AST_RWLIST_TRAVERSE_SAFE_BEGIN(&ast_event_cache[type], event_ref, entry) {
593                 AST_LIST_TRAVERSE(&cache_args, cache_arg, entry) {
594                         if ( ! ( (cache_arg->ie_pltype == AST_EVENT_IE_PLTYPE_UINT &&
595                            (cache_arg->payload.uint ==
596                             ast_event_get_ie_uint(event_ref->event, cache_arg->ie_type))) ||
597
598                            (cache_arg->ie_pltype == AST_EVENT_IE_PLTYPE_STR &&
599                            (!strcmp(cache_arg->payload.str,
600                              ast_event_get_ie_str(event_ref->event, cache_arg->ie_type)))) ||
601
602                            (cache_arg->ie_pltype == AST_EVENT_IE_PLTYPE_EXISTS &&
603                             ast_event_get_ie_raw(event_ref->event, cache_arg->ie_type)) ) ) 
604                         {
605                                 break;  
606                         }
607                 }
608                 if (!cache_arg) {
609                         /* All parameters were matched on this cache entry, so return it */
610                         dup_event = ast_event_dup(event_ref->event);
611                         break;
612                 }
613         }
614         AST_RWLIST_TRAVERSE_SAFE_END
615         AST_RWLIST_UNLOCK(&ast_event_cache[type]);
616
617         return dup_event;
618 }
619
620 /*! \brief Duplicate an event and add it to the cache
621  * \note This assumes this index in to the cache is locked */
622 static int ast_event_dup_and_cache(const struct ast_event *event)
623 {
624         struct ast_event *dup_event;
625         struct ast_event_ref *event_ref;
626
627         if (!(dup_event = ast_event_dup(event)))
628                 return -1;
629         if (!(event_ref = ast_calloc(1, sizeof(*event_ref))))
630                 return -1;
631         
632         event_ref->event = dup_event;
633
634         AST_LIST_INSERT_TAIL(&ast_event_cache[ntohs(event->type)], event_ref, entry);
635
636         return 0;
637 }
638
639 int ast_event_queue_and_cache(struct ast_event *event, ...)
640 {
641         va_list ap;
642         enum ast_event_type ie_type;
643         uint16_t host_event_type;
644         struct ast_event_ref *event_ref;
645         int res;
646         struct cache_arg {
647                 AST_LIST_ENTRY(cache_arg) entry;
648                 enum ast_event_ie_type ie_type;
649                 enum ast_event_ie_pltype ie_pltype;
650         } *cache_arg;
651         AST_LIST_HEAD_NOLOCK_STATIC(cache_args, cache_arg);
652
653         host_event_type = ntohs(event->type);
654
655         /* Invalid type */
656         if (host_event_type >= AST_EVENT_TOTAL) {
657                 ast_log(LOG_WARNING, "Someone tried to queue an event of invalid "
658                         "type '%d'!\n", host_event_type);
659                 return -1;
660         }
661
662         va_start(ap, event);
663         for (ie_type = va_arg(ap, enum ast_event_type);
664                 ie_type != AST_EVENT_IE_END;
665                 ie_type = va_arg(ap, enum ast_event_type))
666         {
667                 cache_arg = alloca(sizeof(*cache_arg));
668                 memset(cache_arg, 0, sizeof(*cache_arg));
669                 cache_arg->ie_type = ie_type;
670                 cache_arg->ie_pltype = va_arg(ap, enum ast_event_ie_pltype);
671                 AST_LIST_INSERT_TAIL(&cache_args, cache_arg, entry);
672         }
673         va_end(ap);
674
675         if (AST_LIST_EMPTY(&cache_args)) {
676                 ast_log(LOG_ERROR, "Events can not be cached without specifying at "
677                         "least one IE type!\n");
678                 return ast_event_queue(event);
679         }
680  
681         AST_RWLIST_WRLOCK(&ast_event_cache[host_event_type]);
682         AST_RWLIST_TRAVERSE_SAFE_BEGIN(&ast_event_cache[host_event_type], event_ref, entry) {
683                 AST_LIST_TRAVERSE(&cache_args, cache_arg, entry) {
684                         if ( ! ( (cache_arg->ie_pltype == AST_EVENT_IE_PLTYPE_UINT &&
685                            (ast_event_get_ie_uint(event, cache_arg->ie_type) ==
686                             ast_event_get_ie_uint(event_ref->event, cache_arg->ie_type))) ||
687
688                            (cache_arg->ie_pltype == AST_EVENT_IE_PLTYPE_STR &&
689                            (!strcmp(ast_event_get_ie_str(event, cache_arg->ie_type),
690                              ast_event_get_ie_str(event_ref->event, cache_arg->ie_type)))) ||
691
692                            (cache_arg->ie_pltype == AST_EVENT_IE_PLTYPE_EXISTS &&
693                             ast_event_get_ie_raw(event_ref->event, cache_arg->ie_type)) ) )
694                         {
695                                 break;  
696                         }
697                 }
698                 if (!cache_arg) {
699                         /* All parameters were matched on this cache entry, so remove it */
700                         AST_LIST_REMOVE_CURRENT(entry);
701                         ast_event_ref_destroy(event_ref);
702                 }
703         }
704         AST_RWLIST_TRAVERSE_SAFE_END;
705         res = ast_event_dup_and_cache(event);
706         AST_RWLIST_UNLOCK(&ast_event_cache[host_event_type]);
707
708         return (ast_event_queue(event) || res) ? -1 : 0;
709 }
710
711 static int handle_event(void *data)
712 {
713         struct ast_event_ref *event_ref = data;
714         struct ast_event_sub *sub;
715         uint16_t host_event_type;
716
717         host_event_type = ntohs(event_ref->event->type);
718
719         /* Subscribers to this specific event first */
720         AST_RWDLLIST_RDLOCK(&ast_event_subs[host_event_type]);
721         AST_RWDLLIST_TRAVERSE(&ast_event_subs[host_event_type], sub, entry) {
722                 struct ast_event_ie_val *ie_val;
723                 AST_LIST_TRAVERSE(&sub->ie_vals, ie_val, entry) {
724                         if (ie_val->ie_pltype == AST_EVENT_IE_PLTYPE_EXISTS &&
725                                 ast_event_get_ie_raw(event_ref->event, ie_val->ie_type)) {
726                                 continue;
727                         } else if (ie_val->ie_pltype == AST_EVENT_IE_PLTYPE_UINT &&
728                                 ast_event_get_ie_uint(event_ref->event, ie_val->ie_type) 
729                                 == ie_val->payload.uint) {
730                                 continue;
731                         } else if (ie_val->ie_pltype == AST_EVENT_IE_PLTYPE_STR &&
732                                 !strcmp(ast_event_get_ie_str(event_ref->event, ie_val->ie_type),
733                                         ie_val->payload.str)) {
734                                 continue;
735                         }
736                         break;
737                 }
738                 if (ie_val)
739                         continue;
740                 sub->cb(event_ref->event, sub->userdata);
741         }
742         AST_RWDLLIST_UNLOCK(&ast_event_subs[host_event_type]);
743
744         /* Now to subscribers to all event types */
745         AST_RWDLLIST_RDLOCK(&ast_event_subs[AST_EVENT_ALL]);
746         AST_RWDLLIST_TRAVERSE(&ast_event_subs[AST_EVENT_ALL], sub, entry)
747                 sub->cb(event_ref->event, sub->userdata);
748         AST_RWDLLIST_UNLOCK(&ast_event_subs[AST_EVENT_ALL]);
749
750         ast_event_ref_destroy(event_ref);
751
752         return 0;
753 }
754
755 int ast_event_queue(struct ast_event *event)
756 {
757         struct ast_event_ref *event_ref;
758         uint16_t host_event_type;
759
760         host_event_type = ntohs(event->type);
761
762         /* Invalid type */
763         if (host_event_type >= AST_EVENT_TOTAL) {
764                 ast_log(LOG_WARNING, "Someone tried to queue an event of invalid "
765                         "type '%d'!\n", host_event_type);
766                 return -1;
767         }
768
769         /* If nobody has subscribed to this event type, throw it away now */
770         if (ast_event_check_subscriber(host_event_type, AST_EVENT_IE_END) 
771                 == AST_EVENT_SUB_NONE) {
772                 ast_event_destroy(event);
773                 return 0;
774         }
775
776         if (!(event_ref = ast_calloc(1, sizeof(*event_ref))))
777                 return -1;
778
779         event_ref->event = event;
780
781         return ast_taskprocessor_push(event_dispatcher, handle_event, event_ref);
782 }
783
784 void ast_event_init(void)
785 {
786         int i;
787
788         for (i = 0; i < AST_EVENT_TOTAL; i++)
789                 AST_RWDLLIST_HEAD_INIT(&ast_event_subs[i]);
790
791         for (i = 0; i < AST_EVENT_TOTAL; i++)
792                 AST_RWLIST_HEAD_INIT(&ast_event_cache[i]);
793
794         event_dispatcher = ast_taskprocessor_get("core_event_dispatcher", 0);
795 }