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