Index: Makefile =================================================================== RCS file: /usr/cvsroot/asterisk/Makefile,v retrieving revision 1.135 diff -u -r1.135 Makefile --- Makefile 11 Feb 2005 21:16:34 -0000 1.135 +++ Makefile 25 Feb 2005 00:08:24 -0000 @@ -231,7 +231,8 @@ cdr.o tdd.o acl.o rtp.o manager.o asterisk.o ast_expr.o \ dsp.o chanvars.o indications.o autoservice.o db.o privacy.o \ astmm.o enum.o srv.o dns.o aescrypt.o aestab.o aeskey.o \ - utils.o config_old.o + utils.o config_old.o events.o rb.o + ifeq (${OSARCH},Darwin) OBJS+=poll.o dlfcn.o ASTLINK=-Wl,-dynamic Index: asterisk.c =================================================================== RCS file: /usr/cvsroot/asterisk/asterisk.c,v retrieving revision 1.141 diff -u -r1.141 asterisk.c --- asterisk.c 11 Feb 2005 21:16:34 -0000 1.141 +++ asterisk.c 25 Feb 2005 00:08:25 -0000 @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -1911,6 +1912,7 @@ printf(term_quit()); exit(1); } + ast_events_init(); ast_channels_init(); if (init_manager()) { printf(term_quit()); Index: channel.c =================================================================== RCS file: /usr/cvsroot/asterisk/channel.c,v retrieving revision 1.173 diff -u -r1.173 channel.c --- channel.c 10 Feb 2005 23:46:03 -0000 1.173 +++ channel.c 25 Feb 2005 00:08:27 -0000 @@ -40,6 +40,7 @@ #include #include #include +#include #ifdef ZAPTEL_OPTIMIZATIONS #include #ifdef __linux__ @@ -2570,6 +2571,7 @@ int ast_setstate(struct ast_channel *chan, int state) { + unsigned int event_type; if (chan->_state != state) { int oldstate = chan->_state; chan->_state = state; @@ -2596,6 +2598,20 @@ chan->cid.cid_num ? chan->cid.cid_num : "", chan->cid.cid_name ? chan->cid.cid_name : "", chan->uniqueid); + } + + event_type = ast_event_convert_from_state(chan->_state); + if(event_type != -1) { + /* TODO: This sucks monkey balls. seriously */ + char *device; + char *rest; + device = strdup(chan->name); + rest = strchr(device, '-'); + if (rest) { + *rest = 0; + } + ast_event_trigger(event_type, device); + free(device); } } return 0; Index: pbx.c =================================================================== RCS file: /usr/cvsroot/asterisk/pbx.c,v retrieving revision 1.206 diff -u -r1.206 pbx.c --- pbx.c 23 Feb 2005 22:48:47 -0000 1.206 +++ pbx.c 25 Feb 2005 00:08:31 -0000 @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -1537,6 +1538,8 @@ strncpy(hint, ast_get_extension_app(list->exten), sizeof(hint) - 1); cur = hint; + state = ast_extension_state2(list->exten); + ast_event_trigger(ast_event_convert_from_state(state), device); do { rest = strchr(cur, '&'); if (rest) { Index: channels/chan_sip.c =================================================================== RCS file: /usr/cvsroot/asterisk/channels/chan_sip.c,v retrieving revision 1.665 diff -u -r1.665 chan_sip.c --- channels/chan_sip.c 24 Feb 2005 13:57:45 -0000 1.665 +++ channels/chan_sip.c 25 Feb 2005 00:08:52 -0000 @@ -59,6 +59,7 @@ #include #include #include +#include #ifndef DEFAULT_USERAGENT #define DEFAULT_USERAGENT "Asterisk PBX" @@ -4029,6 +4030,24 @@ return send_request(p, &req, init ? 2 : 1, p->ocseq); } +static const char* state_to_string(int state) +{ + switch(state) { + case AST_STATE_DOWN: + return "terminated"; + case AST_STATE_RING: + case AST_STATE_RINGING: + return "proceeding"; + case AST_STATE_DIALING_OFFHOOK: + case AST_STATE_DIALING: + return "trying"; + case AST_STATE_RESERVED: + return "early"; + default: + return "confirmed"; + } +} + /*--- transmit_state_notify: Used in the SUBSCRIBE notification subsystem ----*/ static int transmit_state_notify(struct sip_pvt *p, int state, int full) { @@ -4123,7 +4142,7 @@ bytes = snprintf(t, maxbytes, "\n", p->exten); t += bytes; maxbytes -= bytes; - bytes = snprintf(t, maxbytes, "%s\n", state ? "confirmed" : "terminated"); + bytes = snprintf(t, maxbytes, "%s\n", state_to_string(state)); t += bytes; maxbytes -= bytes; bytes = snprintf(t, maxbytes, "\n\n"); @@ -5138,13 +5157,34 @@ return 0; } - transmit_state_notify(p, state, 1); + // transmit_state_notify(p, state, 1); if (option_debug) ast_verbose(VERBOSE_PREFIX_1 "Extension Changed %s new state %d for Notify User %s\n", exten, state, p->username); return 0; } +/** BITSTRUCT **/ +static int cb_event_link_free(void* baton) +{ +// free(baton); + return 0; +} + +/*--- cb_event_link_busy: Run when the user is busy --*/ +static int cb_event_link_busy(const char* contact, unsigned int event_type, + const char *source, void *baton) +{ + struct sip_pvt *p = baton; + + ast_log(LOG_NOTICE, "Triggered Event: contact='%s', event_type='%d', source='%s'.\n", contact, event_type, source); + transmit_state_notify(p, ast_event_convert_to_state(event_type), 1); + + return 0; +} + +/** END BITSTRUCT **/ + /*--- register_verify: Verify registration of user */ static int register_verify(struct sip_pvt *p, struct sockaddr_in *sin, struct sip_request *req, char *uri, int ignore) { @@ -8326,10 +8366,50 @@ transmit_response(p, "484 Address Incomplete", req); ast_set_flag(p, SIP_NEEDDESTROY); } else { - /* Initialize tag */ + { + char tmpf[256]= "", *fr, *a; + strncpy(tmpf, get_header(req, "From"), sizeof(tmpf) - 1); + fr = ditch_braces(tmpf); + if (!ast_strlen_zero(fr)) { + if (strncmp(fr, "sip:", 4)) { + ast_log(LOG_WARNING, "Huh? Not a SIP header (%s)?\n", fr); + } + else { + char* fme = malloc(sizeof("SIP/") + strlen(p->exten)); + char* fme2; + fr += 4; + if ((a = strchr(fr, ';'))) + *a = '\0'; + if ((a = strchr(fr, '@'))) + *a = '\0'; + fme2 = malloc(sizeof("SIP/") + strlen(fr)); + sprintf(fme, "SIP/%s", p->exten); + sprintf(fme2, "SIP/%s", fr); + p->subscribed = 2; + + ast_event_subscribe(fme2, AST_EVENT_LINK_BUSY, fme, 3600, cb_event_link_busy, + cb_event_link_free, p); + + ast_event_subscribe(fme2, AST_EVENT_LINK_IDLE, fme, 3600, cb_event_link_busy, + cb_event_link_free, p); + + ast_event_subscribe(fme2, AST_EVENT_LINK_RING, fme, 3600, cb_event_link_busy, + cb_event_link_free, p); + + ast_event_subscribe(fme2, AST_EVENT_LINK_RESERVED, fme, 3600, cb_event_link_busy, + cb_event_link_free, p); + + free(fme); + free(fme2); + } + } + } + + /* Initialize tag */ p->tag = rand(); - if (!strcmp(get_header(req, "Accept"), "application/dialog-info+xml")) - p->subscribed = 2; + if (!strcmp(get_header(req, "Accept"), "application/dialog-info+xml")) { + p->subscribed = 2; + } else if (!strcmp(get_header(req, "Accept"), "application/simple-message-summary")) { /* Looks like they actually want a mailbox */ @@ -8369,6 +8449,7 @@ if (!ignore && p) p->lastinvite = seqno; if (p && !ast_test_flag(p, SIP_NEEDDESTROY)) { + char* fme; if (!(p->expiry = atoi(get_header(req, "Expires")))) { transmit_response(p, "200 OK", req); ast_set_flag(p, SIP_NEEDDESTROY); @@ -8381,6 +8462,10 @@ } transmit_response(p, "200 OK", req); sip_scheddestroy(p, (p->expiry+10)*1000); + fme = malloc(sizeof("SIP/") + strlen(p->exten)); + sprintf(fme, "SIP/%s", p->exten); + ast_event_trigger(ast_event_convert_from_state(ast_extension_state(NULL, p->context, p->exten)), fme); + free(fme); transmit_state_notify(p, ast_extension_state(NULL, p->context, p->exten),1); } } else if (!strcasecmp(cmd, "INFO")) { --- /dev/null 2005-02-10 18:43:56.833702072 -0800 +++ events.c 2005-02-24 15:38:06.323149872 -0800 @@ -0,0 +1,670 @@ +/* + * Asterisk -- A telephony toolkit for Linux. + * + * Event Handler for notifying channels of state changes + * + * Copyright (C) 2005, BitStruct, LLC + * + * Paul Querna + * Robert Emanuele + * + * This program is free software, distributed under the terms of + * the GNU General Public License + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* TODO: + - write callbacks for common SIP operations + - add support for MWI / voice mail app + - code formating + - send patch to dev@ and mantis bug tracker +*/ + +struct contact_entry { + char* contact; + unsigned int expire_time; +}; +typedef struct contact_entry contact_entry_t; + +struct handler_entry { + void *context; + struct rb_table *contact_tree; + ast_event_cleanup_cb_type cleanup; + ast_event_handler_cb_type callback; +}; +typedef struct handler_entry handler_entry_t; + +struct source_entry { + char* source; + struct rb_table *handler_tree; +}; +typedef struct source_entry source_entry_t; + +struct expire_entry { + contact_entry_t* contact_entry_p; + handler_entry_t* handler_entry_p; + source_entry_t* source_entry_p; + unsigned int event_type; +}; +typedef struct expire_entry expire_entry_t; + +struct event { + unsigned int type; + char* source; + struct event* next; +}; +typedef struct event event_t; + +static struct rb_table* source_trees[AST_EVENT_TYPE_MAX]; /* each event type has its own tree */ +static struct rb_table* expiration_tree; +static ast_mutex_t source_trees_mutex; + +static event_t* event_list_head; +static event_t** event_list_tailp; +static ast_mutex_t event_list_mutex; + +static pthread_cond_t event_condvar; +static pthread_mutex_t event_condlock; +static pthread_t et; + +#define FREE_LIST_MAX 16 + +static int should_exit; + +static void expire_contact(expire_entry_t* expire_item); +static void process_event(unsigned int event_type, char* source); + +static int source_entry_compare(const void *rb_a, const void *rb_b, void *rb_param) +{ + const source_entry_t* a = (const source_entry_t*)rb_a; + const source_entry_t* b = (const source_entry_t*)rb_b; + return strcmp(a->source,b->source); +} + +static int handler_entry_compare(const void *rb_a, const void *rb_b, void *rb_param) +{ + const handler_entry_t* a = (const handler_entry_t*)rb_a; + const handler_entry_t* b = (const handler_entry_t*)rb_b; + void* data[2][3]; + data[0][0] = a->context; + data[0][1] = a->cleanup; + data[0][2] = a->callback; + data[1][0] = b->context; + data[1][1] = b->cleanup; + data[1][2] = b->callback; + return memcmp(&(data[0][0]),&(data[1][0]),sizeof(void*)*3); +} + +static int contact_entry_compare(const void *rb_a, const void *rb_b, void *rb_param) +{ + const contact_entry_t* a = (const contact_entry_t*)rb_a; + const contact_entry_t* b = (const contact_entry_t*)rb_b; + + return strcmp(a->contact,b->contact); +} + +static int expire_entry_compare(const void *rb_a, const void *rb_b, void *rb_param) +{ + const expire_entry_t* a = (const expire_entry_t*)rb_a; + const expire_entry_t* b = (const expire_entry_t*)rb_b; + + int ret; + + if (a->contact_entry_p->expire_time != b->contact_entry_p->expire_time) + { + if (a->contact_entry_p->expire_time > b->contact_entry_p->expire_time) + ret = 1; + else + ret = -1; + } + else + ret = 0; + + if (ret == 0) + ret = contact_entry_compare(a->contact_entry_p,b->contact_entry_p,NULL); + if (ret == 0) + ret = handler_entry_compare(a->handler_entry_p,b->handler_entry_p,NULL); + if (ret == 0) + ret = source_entry_compare(a->source_entry_p,b->source_entry_p,NULL); + if (ret == 0) + { + if (a->event_type > b->event_type) + ret = 1; + if (a->event_type < b->event_type) + ret = -1; + } + + return ret; +} + +static void contact_entry_free(void *rb_item, void *rb_param) +{ + contact_entry_t* item = (contact_entry_t*)rb_item; + free(item->contact); + free(item); +} + +static void handler_entry_free(void *rb_item, void *rb_param) +{ + handler_entry_t* item = (handler_entry_t*)rb_item; + if (item->cleanup) + item->cleanup(item->context); + rb_destroy(item->contact_tree,contact_entry_free); + free(item); +} + +static void source_entry_free(void *rb_item, void *rb_param) +{ + source_entry_t* item = (source_entry_t*)rb_item; + free(item->source); + rb_destroy(item->handler_tree,handler_entry_free); + free(item); +} + +static const char* etype2str(unsigned int type) +{ + switch(type) { + case AST_EVENT_LINK_CREATE: + return "Link Create"; + case AST_EVENT_LINK_DESTROY: + return "Link Destroy"; + case AST_EVENT_LINK_RING: + return "Link RING"; + case AST_EVENT_LINK_IDLE: + return "Link Idle"; + case AST_EVENT_LINK_BUSY: + return "Link Busy"; + case AST_EVENT_LINK_RESERVED: + return "Link Reserved"; + case AST_EVENT_LINK_DIALING: + return "Link Dialing"; + case AST_EVENT_LINK_OFFHOOK: + return "Link Off Hook"; + case AST_EVENT_LINK_DIAL_OFFHOOK: + return "Link Dailing Off Hook"; + case AST_EVENT_VM_NEW: + return "New Voice Mail"; + default: + return "Unknown Type?"; + } +} +static void *event_thread(void *ignore) +{ + event_t* process_list; + event_t* event_to_process; + expire_entry_t* expire_item; + struct rb_traverser expire_traverser; + time_t ct; + + while(1) { + pthread_mutex_lock(&event_condlock); + pthread_cond_wait(&event_condvar, &event_condlock); + if(should_exit == 1) { + pthread_mutex_unlock(&event_condlock); + pthread_exit(0); + } + pthread_mutex_unlock(&event_condlock); + + ast_mutex_lock(&event_list_mutex); + + process_list = event_list_head; + event_list_head = NULL; + event_list_tailp = &event_list_head; + ast_mutex_unlock(&event_list_mutex); + + ast_mutex_lock(&source_trees_mutex); + while (process_list) + { + event_to_process = process_list; + process_list = process_list->next; + ast_log(LOG_NOTICE,"Got an event: (%d) %s from %s\n", + event_to_process->type, + etype2str(event_to_process->type), + event_to_process->source); + + process_event(event_to_process->type,event_to_process->source); + + free(event_to_process->source); + free(event_to_process); + } + + ct = time(NULL); + expire_item = rb_t_first(&expire_traverser,expiration_tree); + while (expire_item && expire_item->contact_entry_p->expire_time <= ct) + { + expire_contact(expire_item); + expire_item = rb_t_first(&expire_traverser,expiration_tree); + } + + ast_mutex_unlock(&source_trees_mutex); + + } + return NULL; +} +static char show_dump_sub_usage[] = +"Usage: events show subscribers\n" +" Dump all active event subscribers\n"; + +static int ast_event_dump_subscribers(int fd, int argc, char *argv[]); + +static struct ast_cli_entry cli_dump_sub = +{ { "events", "show", "subscribers", NULL }, ast_event_dump_subscribers, "Dump all Event Subscribers", show_dump_sub_usage}; + +void ast_events_init() +{ + should_exit = 0; + + /* Null the source_tree and event_list */ + event_list_head = NULL; + event_list_tailp = &event_list_head; + memset(source_trees,0,sizeof(source_trees)); + + /* Create the expiration_tree */ + expiration_tree = rb_create(expire_entry_compare,NULL,NULL); + + /* Create the mutexes */ + if (ast_mutex_init(&source_trees_mutex) != 0) { + ast_log(LOG_ERROR,"ast_events_init could not init source trees mutex"); + } + + if (ast_mutex_init(&event_list_mutex) != 0) { + ast_log(LOG_ERROR,"ast_events_init could not init event_list_mutex"); + } + + if (pthread_cond_init(&event_condvar, NULL) != 0) { + ast_log(LOG_ERROR,"ast_events_init could not init event_condvar: %s", strerror(errno)); + } + + if (pthread_mutex_init(&event_condlock, NULL) != 0) { + ast_log(LOG_ERROR,"ast_events_init could not init event_condlock: %s", strerror(errno)); + } + + ast_pthread_create(&et, NULL, event_thread, NULL); + ast_cli_register(&cli_dump_sub); + ast_register_atexit(ast_events_deinit); +} + +void ast_events_deinit() +{ + event_t* tmp_event; + int eventnum; + + /* Kill The Event Thread Safely */ + pthread_mutex_lock(&event_condlock); + should_exit = 1; + pthread_cond_signal(&event_condvar); + pthread_mutex_unlock(&event_condlock); + + if (pthread_join(et, NULL) != 0) { + ast_log(LOG_ERROR,"ast_events_deinit could not join on event thread: %s", strerror(errno)); + } + + /* clear events */ + + ast_mutex_lock(&event_list_mutex); + while (event_list_head) + { + tmp_event = event_list_head; + event_list_head = event_list_head->next; + free(tmp_event); + } + ast_mutex_unlock(&event_list_mutex); + event_list_tailp = &event_list_head; + + /* Clear source_tree and expiration_tree*/ + ast_mutex_lock(&source_trees_mutex); + eventnum = 0; + while (eventnum < AST_EVENT_TYPE_MAX) + { + if (source_trees[eventnum]) + rb_destroy(source_trees[eventnum],source_entry_free); + eventnum++; + } + + rb_destroy(expiration_tree,NULL); + ast_mutex_unlock(&source_trees_mutex); + + // Destroy the mutexes + if (ast_mutex_destroy(&source_trees_mutex) != 0) { + ast_log(LOG_ERROR,"ast_events_deinit could not destroy source trees mutex: %s", strerror(errno)); + } + + if (ast_mutex_destroy(&event_list_mutex) != 0) { + ast_log(LOG_ERROR,"ast_events_deinit could not destroy event_list_mutex: %s", strerror(errno)); + } + + if (pthread_cond_destroy(&event_condvar) != 0) { + ast_log(LOG_ERROR,"ast_events_init could not destroy event_condvar: %s", strerror(errno)); + } + + if (pthread_mutex_destroy(&event_condlock) != 0) { + ast_log(LOG_ERROR,"ast_events_init could not destroy event_condlock: %s", strerror(errno)); + } + +} + +void ast_event_subscribe(const char* contact, unsigned int event_type, const char* source, int timeout, + ast_event_handler_cb_type callback, ast_event_cleanup_cb_type cleanup, void *data) +{ + struct rb_table** source_tree = &(source_trees[event_type]); + void* item_in_tree; + /* + The Free array is limited in size to FREE_LIST_MAX + Make sure you know how many items are going on the array worst case + This is used so deallocations can be done outside the mutex + */ + void* free_array[FREE_LIST_MAX]; + int free_array_i = 0; + source_entry_t* source_item; + handler_entry_t* handler_item; + contact_entry_t* contact_item; + expire_entry_t* expire_item; + expire_entry_t* expire_item_in_tree; + + /* validate params */ + if (contact && source && callback && event_type < AST_EVENT_TYPE_MAX) + { + /* + Create all your structures and allocations before the mutexe + */ + source_item = malloc(sizeof(source_entry_t)); + handler_item = malloc(sizeof(handler_entry_t)); + contact_item = malloc(sizeof(contact_entry_t)); + expire_item = malloc(sizeof(expire_entry_t)); + + source_item->source = strdup(source); + source_item->handler_tree = NULL; + + handler_item->context = data; + handler_item->callback = callback; + handler_item->cleanup = cleanup; + handler_item->contact_tree = NULL; + + contact_item->contact = strdup(contact); + contact_item->expire_time = time(NULL) + timeout + 1; + + expire_item->event_type = event_type; + + ast_mutex_lock(&source_trees_mutex); + + /* create the trees as needed always adding the next subtree and data */ + if (!*source_tree) + *source_tree = rb_create(source_entry_compare,NULL,NULL); + + item_in_tree = rb_insert(*source_tree,source_item); + if (item_in_tree != NULL) + { + /* dispose of the new source item and use the item_in_tree */ + free_array[free_array_i++] = source_item->source; + free_array[free_array_i++] = source_item; + source_item = item_in_tree; + } + expire_item->source_entry_p = source_item; + + if (!source_item->handler_tree) + source_item->handler_tree = rb_create(handler_entry_compare,NULL,NULL); + + item_in_tree = rb_insert(source_item->handler_tree,handler_item); + if (item_in_tree != NULL) + { + /* dispose of the new handler item and use the item_in_tree */ + free_array[free_array_i++] = handler_item; + handler_item = item_in_tree; + } + expire_item->handler_entry_p = handler_item; + + if (!handler_item->contact_tree) + handler_item->contact_tree = rb_create(contact_entry_compare,NULL,NULL); + + item_in_tree = rb_insert(handler_item->contact_tree,contact_item); + if (item_in_tree != NULL) + { + /* delete it from the contact_expiration_tree so it can be resorted*/ + expire_item_in_tree = rb_delete(expiration_tree,expire_item); + if (!expire_item_in_tree) + ast_log(LOG_ERROR,"ast_event_subscribe: DANGER: item in source_tree not found in expiration_tree\n"); + free_array[free_array_i++] = expire_item_in_tree; + + /* dispose of the new contact item and use the item_in_tree */ + free_array[free_array_i++] = contact_item->contact; + free_array[free_array_i++] = contact_item; + ((contact_entry_t*)item_in_tree)->expire_time = contact_item->expire_time; + contact_item = item_in_tree; + } + expire_item->contact_entry_p = contact_item; + + /* add it to the contact_expiration_tree */ + expire_item_in_tree = rb_insert(expiration_tree,expire_item); + if (expire_item_in_tree) + ast_log(LOG_ERROR,"ast_event_subscribe: DANGER: item in already in expiration_tree\n"); + + ast_mutex_unlock(&source_trees_mutex); + + /* free the unused data outside the mutex */ + while (free_array_i > 0) + { + free(free_array[--free_array_i]); + } + } + else + { + ast_log(LOG_ERROR,"ast_event_subscribe: contact, source, and callback, must be !NULL and event_type < AST_EVENT_TYPE_MAX\n"); + } + +} + +void ast_event_unsubscribe(const char* contact, unsigned int event_type, const char* source, + ast_event_handler_cb_type callback, ast_event_cleanup_cb_type cleanup, void *data) +{ + // update the contact to expire now + ast_event_subscribe(contact,event_type,source,0,callback,cleanup,data); +} + +void ast_event_trigger(unsigned int event_type, char* source) +{ + event_t* e = malloc(sizeof(event_t)); + e->source = strdup(source); + e->type = event_type; + e->next = NULL; + ast_mutex_lock(&event_list_mutex); + *event_list_tailp = e; + event_list_tailp = &(e->next); + ast_mutex_unlock(&event_list_mutex); + + pthread_mutex_lock(&event_condlock); + pthread_cond_signal(&event_condvar); + pthread_mutex_unlock(&event_condlock); +} + +int ast_event_convert_from_state(int state) +{ + /* convert AST_STATE_ types to more generic events */ + switch(state) { + case AST_STATE_DOWN: + return AST_EVENT_LINK_IDLE; + + case AST_STATE_RESERVED: + return AST_EVENT_LINK_RESERVED; + + case AST_STATE_OFFHOOK: + return AST_EVENT_LINK_OFFHOOK; + + case AST_STATE_DIALING: + return AST_EVENT_LINK_DIALING; + + case AST_STATE_RING: + case AST_STATE_RINGING: + return AST_EVENT_LINK_RING; + + case AST_STATE_UP: + case AST_STATE_BUSY: + return AST_EVENT_LINK_BUSY; + + case AST_STATE_DIALING_OFFHOOK: + return AST_EVENT_LINK_DIAL_OFFHOOK; + + default: + /* we didn't know about this type, or don't care about it */ + return -1; + } +} + +int ast_event_convert_to_state(unsigned int state) +{ + /* convert AST_EVENT_ types to specific channel states */ + switch(state) { + case AST_EVENT_LINK_IDLE: + return AST_STATE_DOWN; + case AST_EVENT_LINK_RESERVED: + return AST_STATE_RESERVED; + case AST_EVENT_LINK_OFFHOOK: + return AST_STATE_OFFHOOK; + case AST_EVENT_LINK_DIALING: + return AST_STATE_DIALING; + case AST_EVENT_LINK_RING: + return AST_STATE_RINGING; + case AST_EVENT_LINK_BUSY: + return AST_STATE_BUSY; + case AST_EVENT_LINK_DIAL_OFFHOOK: + return AST_STATE_DIALING_OFFHOOK; + default: + return -1; + } +} + +static void process_event(unsigned int event_type, char* source) +{ + source_entry_t question_source_item; + source_entry_t* source_item; + handler_entry_t* handler_item; + contact_entry_t* contact_item; + struct rb_traverser handler_tranverser; + struct rb_traverser contact_tranverser; + + question_source_item.source = source; + + if (source_trees[event_type]) + { + source_item = rb_find(source_trees[event_type],&question_source_item); + + if (source_item) + { + handler_item = rb_t_first(&handler_tranverser, source_item->handler_tree); + while (handler_item) + { + contact_item = rb_t_first(&contact_tranverser, handler_item->contact_tree); + while (contact_item) + { + handler_item->callback(contact_item->contact, + event_type, source, handler_item->context); + contact_item = rb_t_next(&contact_tranverser); + } + + handler_item = rb_t_next(&handler_tranverser); + } + } + } +} + +static void expire_contact(expire_entry_t* expire_item) +{ + rb_delete(expiration_tree,expire_item); + /* if there is one item (must be this one) in the contact tree delete the handler */ + if (rb_count(expire_item->handler_entry_p->contact_tree) == 1) + { + /* if there is one item (must be this one) in the handler tree delete the source */ + if (rb_count(expire_item->source_entry_p->handler_tree) == 1) + { + /* if there is one item (must be this one) in the source tree delete the source_tree */ + if (rb_count(source_trees[expire_item->event_type]) == 1) + { + /* delete the source_tree */ + rb_destroy(source_trees[expire_item->event_type],source_entry_free); + source_trees[expire_item->event_type] = NULL; + } + else + { + /* just delete the source_entry */ + rb_delete(source_trees[expire_item->event_type],expire_item->source_entry_p); + handler_entry_free(expire_item->source_entry_p,NULL); + } + } + else + { + /* just delete the handler_entry */ + rb_delete(expire_item->source_entry_p->handler_tree,expire_item->handler_entry_p); + handler_entry_free(expire_item->contact_entry_p,NULL); + } + + } + else + { + /* just delete the contact_entry */ + rb_delete(expire_item->handler_entry_p->contact_tree,expire_item->contact_entry_p); + contact_entry_free(expire_item->contact_entry_p,NULL); + } + free(expire_item); +} + + +static int ast_event_dump_subscribers(int fd, int argc, char *argv[]) +{ + unsigned int event_type; + source_entry_t* source_item; + handler_entry_t* handler_item; + contact_entry_t* contact_item; + struct rb_traverser source_tranverser; + struct rb_traverser handler_tranverser; + struct rb_traverser contact_tranverser; + Dl_info info; + event_type = 0; + time_t ct = time(NULL); + + ast_mutex_lock(&source_trees_mutex); + ast_cli(fd, "Current Time: %lu\n",ct); + while (event_type < AST_EVENT_TYPE_MAX) + { + if (source_trees[event_type]) + { + ast_cli(fd, "Event_type:%u\n",event_type); + source_item = rb_t_first(&source_tranverser,source_trees[event_type]); + while (source_item) + { + ast_cli(fd, "\tSrc:%s\n",source_item->source); + handler_item = rb_t_first(&handler_tranverser, source_item->handler_tree); + while (handler_item) + { + dladdr(handler_item->callback, &info); + ast_cli(fd, "\t\tHndlr:%s (%p)\n",info.dli_sname,handler_item->callback); + contact_item = rb_t_first(&contact_tranverser, handler_item->contact_tree); + while (contact_item) + { + ast_cli(fd, "\t\t\tCntct:%s Exp in: %lu\n", + contact_item->contact,contact_item->expire_time - ct); + contact_item = rb_t_next(&contact_tranverser); + } + + handler_item = rb_t_next(&handler_tranverser); + } + source_item = rb_t_next(&source_tranverser); + } + } + event_type++; + } + + ast_mutex_unlock(&source_trees_mutex); + return 0; +} + --- /dev/null 2005-02-10 18:43:56.833702072 -0800 +++ rb.c 2005-02-24 14:50:08.556636880 -0800 @@ -0,0 +1,929 @@ +/* Produced by texiweb from libavl.w. */ + +/* libavl - library for manipulation of binary trees. + Copyright (C) 1998-2002, 2004 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. + + The author may be contacted at on the Internet, or + write to Ben Pfaff, Stanford University, Computer Science Dept., 353 + Serra Mall, Stanford CA 94305, USA. +*/ + +#include +#include +#include +#include +#include "asterisk/rb.h" + +/* Creates and returns a new table + with comparison function |compare| using parameter |param| + and memory allocator |allocator|. + Returns |NULL| if memory allocation failed. */ +struct rb_table * +rb_create (rb_comparison_func *compare, void *param, + struct libavl_allocator *allocator) +{ + struct rb_table *tree; + + assert (compare != NULL); + + if (allocator == NULL) + allocator = &rb_allocator_default; + + tree = allocator->libavl_malloc (allocator, sizeof *tree); + if (tree == NULL) + return NULL; + + tree->rb_root = NULL; + tree->rb_compare = compare; + tree->rb_param = param; + tree->rb_alloc = allocator; + tree->rb_count = 0; + tree->rb_generation = 0; + + return tree; +} + +/* Search |tree| for an item matching |item|, and return it if found. + Otherwise return |NULL|. */ +void * +rb_find (const struct rb_table *tree, const void *item) +{ + const struct rb_node *p; + + assert (tree != NULL && item != NULL); + for (p = tree->rb_root; p != NULL; ) + { + int cmp = tree->rb_compare (item, p->rb_data, tree->rb_param); + + if (cmp < 0) + p = p->rb_link[0]; + else if (cmp > 0) + p = p->rb_link[1]; + else /* |cmp == 0| */ + return p->rb_data; + } + + return NULL; +} + +/* Inserts |item| into |tree| and returns a pointer to |item|'s address. + If a duplicate item is found in the tree, + returns a pointer to the duplicate without inserting |item|. + Returns |NULL| in case of memory allocation failure. */ +void ** +rb_probe (struct rb_table *tree, void *item) +{ + struct rb_node *pa[RB_MAX_HEIGHT]; /* Nodes on stack. */ + unsigned char da[RB_MAX_HEIGHT]; /* Directions moved from stack nodes. */ + int k; /* Stack height. */ + + struct rb_node *p; /* Traverses tree looking for insertion point. */ + struct rb_node *n; /* Newly inserted node. */ + + assert (tree != NULL && item != NULL); + + pa[0] = (struct rb_node *) &tree->rb_root; + da[0] = 0; + k = 1; + for (p = tree->rb_root; p != NULL; p = p->rb_link[da[k - 1]]) + { + int cmp = tree->rb_compare (item, p->rb_data, tree->rb_param); + if (cmp == 0) + return &p->rb_data; + + pa[k] = p; + da[k++] = cmp > 0; + } + + n = pa[k - 1]->rb_link[da[k - 1]] = + tree->rb_alloc->libavl_malloc (tree->rb_alloc, sizeof *n); + if (n == NULL) + return NULL; + + n->rb_data = item; + n->rb_link[0] = n->rb_link[1] = NULL; + n->rb_color = RB_RED; + tree->rb_count++; + tree->rb_generation++; + + while (k >= 3 && pa[k - 1]->rb_color == RB_RED) + { + if (da[k - 2] == 0) + { + struct rb_node *y = pa[k - 2]->rb_link[1]; + if (y != NULL && y->rb_color == RB_RED) + { + pa[k - 1]->rb_color = y->rb_color = RB_BLACK; + pa[k - 2]->rb_color = RB_RED; + k -= 2; + } + else + { + struct rb_node *x; + + if (da[k - 1] == 0) + y = pa[k - 1]; + else + { + x = pa[k - 1]; + y = x->rb_link[1]; + x->rb_link[1] = y->rb_link[0]; + y->rb_link[0] = x; + pa[k - 2]->rb_link[0] = y; + } + + x = pa[k - 2]; + x->rb_color = RB_RED; + y->rb_color = RB_BLACK; + + x->rb_link[0] = y->rb_link[1]; + y->rb_link[1] = x; + pa[k - 3]->rb_link[da[k - 3]] = y; + break; + } + } + else + { + struct rb_node *y = pa[k - 2]->rb_link[0]; + if (y != NULL && y->rb_color == RB_RED) + { + pa[k - 1]->rb_color = y->rb_color = RB_BLACK; + pa[k - 2]->rb_color = RB_RED; + k -= 2; + } + else + { + struct rb_node *x; + + if (da[k - 1] == 1) + y = pa[k - 1]; + else + { + x = pa[k - 1]; + y = x->rb_link[0]; + x->rb_link[0] = y->rb_link[1]; + y->rb_link[1] = x; + pa[k - 2]->rb_link[1] = y; + } + + x = pa[k - 2]; + x->rb_color = RB_RED; + y->rb_color = RB_BLACK; + + x->rb_link[1] = y->rb_link[0]; + y->rb_link[0] = x; + pa[k - 3]->rb_link[da[k - 3]] = y; + break; + } + } + } + tree->rb_root->rb_color = RB_BLACK; + + + return &n->rb_data; +} + +/* Inserts |item| into |table|. + Returns |NULL| if |item| was successfully inserted + or if a memory allocation error occurred. + Otherwise, returns the duplicate item. */ +void * +rb_insert (struct rb_table *table, void *item) +{ + void **p = rb_probe (table, item); + return p == NULL || *p == item ? NULL : *p; +} + +/* Inserts |item| into |table|, replacing any duplicate item. + Returns |NULL| if |item| was inserted without replacing a duplicate, + or if a memory allocation error occurred. + Otherwise, returns the item that was replaced. */ +void * +rb_replace (struct rb_table *table, void *item) +{ + void **p = rb_probe (table, item); + if (p == NULL || *p == item) + return NULL; + else + { + void *r = *p; + *p = item; + return r; + } +} + +/* Deletes from |tree| and returns an item matching |item|. + Returns a null pointer if no matching item found. */ +void * +rb_delete (struct rb_table *tree, const void *item) +{ + struct rb_node *pa[RB_MAX_HEIGHT]; /* Nodes on stack. */ + unsigned char da[RB_MAX_HEIGHT]; /* Directions moved from stack nodes. */ + int k; /* Stack height. */ + + struct rb_node *p; /* The node to delete, or a node part way to it. */ + int cmp; /* Result of comparison between |item| and |p|. */ + + assert (tree != NULL && item != NULL); + + k = 0; + p = (struct rb_node *) &tree->rb_root; + for (cmp = -1; cmp != 0; + cmp = tree->rb_compare (item, p->rb_data, tree->rb_param)) + { + int dir = cmp > 0; + + pa[k] = p; + da[k++] = dir; + + p = p->rb_link[dir]; + if (p == NULL) + return NULL; + } + item = p->rb_data; + + if (p->rb_link[1] == NULL) + pa[k - 1]->rb_link[da[k - 1]] = p->rb_link[0]; + else + { + enum rb_color t; + struct rb_node *r = p->rb_link[1]; + + if (r->rb_link[0] == NULL) + { + r->rb_link[0] = p->rb_link[0]; + t = r->rb_color; + r->rb_color = p->rb_color; + p->rb_color = t; + pa[k - 1]->rb_link[da[k - 1]] = r; + da[k] = 1; + pa[k++] = r; + } + else + { + struct rb_node *s; + int j = k++; + + for (;;) + { + da[k] = 0; + pa[k++] = r; + s = r->rb_link[0]; + if (s->rb_link[0] == NULL) + break; + + r = s; + } + + da[j] = 1; + pa[j] = s; + pa[j - 1]->rb_link[da[j - 1]] = s; + + s->rb_link[0] = p->rb_link[0]; + r->rb_link[0] = s->rb_link[1]; + s->rb_link[1] = p->rb_link[1]; + + t = s->rb_color; + s->rb_color = p->rb_color; + p->rb_color = t; + } + } + + if (p->rb_color == RB_BLACK) + { + for (;;) + { + struct rb_node *x = pa[k - 1]->rb_link[da[k - 1]]; + if (x != NULL && x->rb_color == RB_RED) + { + x->rb_color = RB_BLACK; + break; + } + if (k < 2) + break; + + if (da[k - 1] == 0) + { + struct rb_node *w = pa[k - 1]->rb_link[1]; + + if (w->rb_color == RB_RED) + { + w->rb_color = RB_BLACK; + pa[k - 1]->rb_color = RB_RED; + + pa[k - 1]->rb_link[1] = w->rb_link[0]; + w->rb_link[0] = pa[k - 1]; + pa[k - 2]->rb_link[da[k - 2]] = w; + + pa[k] = pa[k - 1]; + da[k] = 0; + pa[k - 1] = w; + k++; + + w = pa[k - 1]->rb_link[1]; + } + + if ((w->rb_link[0] == NULL + || w->rb_link[0]->rb_color == RB_BLACK) + && (w->rb_link[1] == NULL + || w->rb_link[1]->rb_color == RB_BLACK)) + w->rb_color = RB_RED; + else + { + if (w->rb_link[1] == NULL + || w->rb_link[1]->rb_color == RB_BLACK) + { + struct rb_node *y = w->rb_link[0]; + y->rb_color = RB_BLACK; + w->rb_color = RB_RED; + w->rb_link[0] = y->rb_link[1]; + y->rb_link[1] = w; + w = pa[k - 1]->rb_link[1] = y; + } + + w->rb_color = pa[k - 1]->rb_color; + pa[k - 1]->rb_color = RB_BLACK; + w->rb_link[1]->rb_color = RB_BLACK; + + pa[k - 1]->rb_link[1] = w->rb_link[0]; + w->rb_link[0] = pa[k - 1]; + pa[k - 2]->rb_link[da[k - 2]] = w; + break; + } + } + else + { + struct rb_node *w = pa[k - 1]->rb_link[0]; + + if (w->rb_color == RB_RED) + { + w->rb_color = RB_BLACK; + pa[k - 1]->rb_color = RB_RED; + + pa[k - 1]->rb_link[0] = w->rb_link[1]; + w->rb_link[1] = pa[k - 1]; + pa[k - 2]->rb_link[da[k - 2]] = w; + + pa[k] = pa[k - 1]; + da[k] = 1; + pa[k - 1] = w; + k++; + + w = pa[k - 1]->rb_link[0]; + } + + if ((w->rb_link[0] == NULL + || w->rb_link[0]->rb_color == RB_BLACK) + && (w->rb_link[1] == NULL + || w->rb_link[1]->rb_color == RB_BLACK)) + w->rb_color = RB_RED; + else + { + if (w->rb_link[0] == NULL + || w->rb_link[0]->rb_color == RB_BLACK) + { + struct rb_node *y = w->rb_link[1]; + y->rb_color = RB_BLACK; + w->rb_color = RB_RED; + w->rb_link[1] = y->rb_link[0]; + y->rb_link[0] = w; + w = pa[k - 1]->rb_link[0] = y; + } + + w->rb_color = pa[k - 1]->rb_color; + pa[k - 1]->rb_color = RB_BLACK; + w->rb_link[0]->rb_color = RB_BLACK; + + pa[k - 1]->rb_link[0] = w->rb_link[1]; + w->rb_link[1] = pa[k - 1]; + pa[k - 2]->rb_link[da[k - 2]] = w; + break; + } + } + + k--; + } + + } + + tree->rb_alloc->libavl_free (tree->rb_alloc, p); + tree->rb_count--; + tree->rb_generation++; + return (void *) item; +} + +/* Refreshes the stack of parent pointers in |trav| + and updates its generation number. */ +static void +trav_refresh (struct rb_traverser *trav) +{ + assert (trav != NULL); + + trav->rb_generation = trav->rb_table->rb_generation; + + if (trav->rb_node != NULL) + { + rb_comparison_func *cmp = trav->rb_table->rb_compare; + void *param = trav->rb_table->rb_param; + struct rb_node *node = trav->rb_node; + struct rb_node *i; + + trav->rb_height = 0; + for (i = trav->rb_table->rb_root; i != node; ) + { + assert (trav->rb_height < RB_MAX_HEIGHT); + assert (i != NULL); + + trav->rb_stack[trav->rb_height++] = i; + i = i->rb_link[cmp (node->rb_data, i->rb_data, param) > 0]; + } + } +} + +/* Initializes |trav| for use with |tree| + and selects the null node. */ +void +rb_t_init (struct rb_traverser *trav, struct rb_table *tree) +{ + trav->rb_table = tree; + trav->rb_node = NULL; + trav->rb_height = 0; + trav->rb_generation = tree->rb_generation; +} + +/* Initializes |trav| for |tree| + and selects and returns a pointer to its least-valued item. + Returns |NULL| if |tree| contains no nodes. */ +void * +rb_t_first (struct rb_traverser *trav, struct rb_table *tree) +{ + struct rb_node *x; + + assert (tree != NULL && trav != NULL); + + trav->rb_table = tree; + trav->rb_height = 0; + trav->rb_generation = tree->rb_generation; + + x = tree->rb_root; + if (x != NULL) + while (x->rb_link[0] != NULL) + { + assert (trav->rb_height < RB_MAX_HEIGHT); + trav->rb_stack[trav->rb_height++] = x; + x = x->rb_link[0]; + } + trav->rb_node = x; + + return x != NULL ? x->rb_data : NULL; +} + +/* Initializes |trav| for |tree| + and selects and returns a pointer to its greatest-valued item. + Returns |NULL| if |tree| contains no nodes. */ +void * +rb_t_last (struct rb_traverser *trav, struct rb_table *tree) +{ + struct rb_node *x; + + assert (tree != NULL && trav != NULL); + + trav->rb_table = tree; + trav->rb_height = 0; + trav->rb_generation = tree->rb_generation; + + x = tree->rb_root; + if (x != NULL) + while (x->rb_link[1] != NULL) + { + assert (trav->rb_height < RB_MAX_HEIGHT); + trav->rb_stack[trav->rb_height++] = x; + x = x->rb_link[1]; + } + trav->rb_node = x; + + return x != NULL ? x->rb_data : NULL; +} + +/* Searches for |item| in |tree|. + If found, initializes |trav| to the item found and returns the item + as well. + If there is no matching item, initializes |trav| to the null item + and returns |NULL|. */ +void * +rb_t_find (struct rb_traverser *trav, struct rb_table *tree, void *item) +{ + struct rb_node *p, *q; + + assert (trav != NULL && tree != NULL && item != NULL); + trav->rb_table = tree; + trav->rb_height = 0; + trav->rb_generation = tree->rb_generation; + for (p = tree->rb_root; p != NULL; p = q) + { + int cmp = tree->rb_compare (item, p->rb_data, tree->rb_param); + + if (cmp < 0) + q = p->rb_link[0]; + else if (cmp > 0) + q = p->rb_link[1]; + else /* |cmp == 0| */ + { + trav->rb_node = p; + return p->rb_data; + } + + assert (trav->rb_height < RB_MAX_HEIGHT); + trav->rb_stack[trav->rb_height++] = p; + } + + trav->rb_height = 0; + trav->rb_node = NULL; + return NULL; +} + +/* Attempts to insert |item| into |tree|. + If |item| is inserted successfully, it is returned and |trav| is + initialized to its location. + If a duplicate is found, it is returned and |trav| is initialized to + its location. No replacement of the item occurs. + If a memory allocation failure occurs, |NULL| is returned and |trav| + is initialized to the null item. */ +void * +rb_t_insert (struct rb_traverser *trav, struct rb_table *tree, void *item) +{ + void **p; + + assert (trav != NULL && tree != NULL && item != NULL); + + p = rb_probe (tree, item); + if (p != NULL) + { + trav->rb_table = tree; + trav->rb_node = + ((struct rb_node *) + ((char *) p - offsetof (struct rb_node, rb_data))); + trav->rb_generation = tree->rb_generation - 1; + return *p; + } + else + { + rb_t_init (trav, tree); + return NULL; + } +} + +/* Initializes |trav| to have the same current node as |src|. */ +void * +rb_t_copy (struct rb_traverser *trav, const struct rb_traverser *src) +{ + assert (trav != NULL && src != NULL); + + if (trav != src) + { + trav->rb_table = src->rb_table; + trav->rb_node = src->rb_node; + trav->rb_generation = src->rb_generation; + if (trav->rb_generation == trav->rb_table->rb_generation) + { + trav->rb_height = src->rb_height; + memcpy (trav->rb_stack, (const void *) src->rb_stack, + sizeof *trav->rb_stack * trav->rb_height); + } + } + + return trav->rb_node != NULL ? trav->rb_node->rb_data : NULL; +} + +/* Returns the next data item in inorder + within the tree being traversed with |trav|, + or if there are no more data items returns |NULL|. */ +void * +rb_t_next (struct rb_traverser *trav) +{ + struct rb_node *x; + + assert (trav != NULL); + + if (trav->rb_generation != trav->rb_table->rb_generation) + trav_refresh (trav); + + x = trav->rb_node; + if (x == NULL) + { + return rb_t_first (trav, trav->rb_table); + } + else if (x->rb_link[1] != NULL) + { + assert (trav->rb_height < RB_MAX_HEIGHT); + trav->rb_stack[trav->rb_height++] = x; + x = x->rb_link[1]; + + while (x->rb_link[0] != NULL) + { + assert (trav->rb_height < RB_MAX_HEIGHT); + trav->rb_stack[trav->rb_height++] = x; + x = x->rb_link[0]; + } + } + else + { + struct rb_node *y; + + do + { + if (trav->rb_height == 0) + { + trav->rb_node = NULL; + return NULL; + } + + y = x; + x = trav->rb_stack[--trav->rb_height]; + } + while (y == x->rb_link[1]); + } + trav->rb_node = x; + + return x->rb_data; +} + +/* Returns the previous data item in inorder + within the tree being traversed with |trav|, + or if there are no more data items returns |NULL|. */ +void * +rb_t_prev (struct rb_traverser *trav) +{ + struct rb_node *x; + + assert (trav != NULL); + + if (trav->rb_generation != trav->rb_table->rb_generation) + trav_refresh (trav); + + x = trav->rb_node; + if (x == NULL) + { + return rb_t_last (trav, trav->rb_table); + } + else if (x->rb_link[0] != NULL) + { + assert (trav->rb_height < RB_MAX_HEIGHT); + trav->rb_stack[trav->rb_height++] = x; + x = x->rb_link[0]; + + while (x->rb_link[1] != NULL) + { + assert (trav->rb_height < RB_MAX_HEIGHT); + trav->rb_stack[trav->rb_height++] = x; + x = x->rb_link[1]; + } + } + else + { + struct rb_node *y; + + do + { + if (trav->rb_height == 0) + { + trav->rb_node = NULL; + return NULL; + } + + y = x; + x = trav->rb_stack[--trav->rb_height]; + } + while (y == x->rb_link[0]); + } + trav->rb_node = x; + + return x->rb_data; +} + +/* Returns |trav|'s current item. */ +void * +rb_t_cur (struct rb_traverser *trav) +{ + assert (trav != NULL); + + return trav->rb_node != NULL ? trav->rb_node->rb_data : NULL; +} + +/* Replaces the current item in |trav| by |new| and returns the item replaced. + |trav| must not have the null item selected. + The new item must not upset the ordering of the tree. */ +void * +rb_t_replace (struct rb_traverser *trav, void *new) +{ + void *old; + + assert (trav != NULL && trav->rb_node != NULL && new != NULL); + old = trav->rb_node->rb_data; + trav->rb_node->rb_data = new; + return old; +} + +/* Destroys |new| with |rb_destroy (new, destroy)|, + first setting right links of nodes in |stack| within |new| + to null pointers to avoid touching uninitialized data. */ +static void +copy_error_recovery (struct rb_node **stack, int height, + struct rb_table *new, rb_item_func *destroy) +{ + assert (stack != NULL && height >= 0 && new != NULL); + + for (; height > 2; height -= 2) + stack[height - 1]->rb_link[1] = NULL; + rb_destroy (new, destroy); +} + +/* Copies |org| to a newly created tree, which is returned. + If |copy != NULL|, each data item in |org| is first passed to |copy|, + and the return values are inserted into the tree, + with |NULL| return values taken as indications of failure. + On failure, destroys the partially created new tree, + applying |destroy|, if non-null, to each item in the new tree so far, + and returns |NULL|. + If |allocator != NULL|, it is used for allocation in the new tree. + Otherwise, the same allocator used for |org| is used. */ +struct rb_table * +rb_copy (const struct rb_table *org, rb_copy_func *copy, + rb_item_func *destroy, struct libavl_allocator *allocator) +{ + struct rb_node *stack[2 * (RB_MAX_HEIGHT + 1)]; + int height = 0; + + struct rb_table *new; + const struct rb_node *x; + struct rb_node *y; + + assert (org != NULL); + new = rb_create (org->rb_compare, org->rb_param, + allocator != NULL ? allocator : org->rb_alloc); + if (new == NULL) + return NULL; + new->rb_count = org->rb_count; + if (new->rb_count == 0) + return new; + + x = (const struct rb_node *) &org->rb_root; + y = (struct rb_node *) &new->rb_root; + for (;;) + { + while (x->rb_link[0] != NULL) + { + assert (height < 2 * (RB_MAX_HEIGHT + 1)); + + y->rb_link[0] = + new->rb_alloc->libavl_malloc (new->rb_alloc, + sizeof *y->rb_link[0]); + if (y->rb_link[0] == NULL) + { + if (y != (struct rb_node *) &new->rb_root) + { + y->rb_data = NULL; + y->rb_link[1] = NULL; + } + + copy_error_recovery (stack, height, new, destroy); + return NULL; + } + + stack[height++] = (struct rb_node *) x; + stack[height++] = y; + x = x->rb_link[0]; + y = y->rb_link[0]; + } + y->rb_link[0] = NULL; + + for (;;) + { + y->rb_color = x->rb_color; + if (copy == NULL) + y->rb_data = x->rb_data; + else + { + y->rb_data = copy (x->rb_data, org->rb_param); + if (y->rb_data == NULL) + { + y->rb_link[1] = NULL; + copy_error_recovery (stack, height, new, destroy); + return NULL; + } + } + + if (x->rb_link[1] != NULL) + { + y->rb_link[1] = + new->rb_alloc->libavl_malloc (new->rb_alloc, + sizeof *y->rb_link[1]); + if (y->rb_link[1] == NULL) + { + copy_error_recovery (stack, height, new, destroy); + return NULL; + } + + x = x->rb_link[1]; + y = y->rb_link[1]; + break; + } + else + y->rb_link[1] = NULL; + + if (height <= 2) + return new; + + y = stack[--height]; + x = stack[--height]; + } + } +} + +/* Frees storage allocated for |tree|. + If |destroy != NULL|, applies it to each data item in inorder. */ +void +rb_destroy (struct rb_table *tree, rb_item_func *destroy) +{ + struct rb_node *p, *q; + + assert (tree != NULL); + + for (p = tree->rb_root; p != NULL; p = q) + if (p->rb_link[0] == NULL) + { + q = p->rb_link[1]; + if (destroy != NULL && p->rb_data != NULL) + destroy (p->rb_data, tree->rb_param); + tree->rb_alloc->libavl_free (tree->rb_alloc, p); + } + else + { + q = p->rb_link[0]; + p->rb_link[0] = q->rb_link[1]; + q->rb_link[1] = p; + } + + tree->rb_alloc->libavl_free (tree->rb_alloc, tree); +} + +/* Allocates |size| bytes of space using |malloc()|. + Returns a null pointer if allocation fails. */ +void * +rb_malloc (struct libavl_allocator *allocator, size_t size) +{ + assert (allocator != NULL && size > 0); + return malloc (size); +} + +/* Frees |block|. */ +void +rb_free (struct libavl_allocator *allocator, void *block) +{ + assert (allocator != NULL && block != NULL); + free (block); +} + +/* Default memory allocator that uses |malloc()| and |free()|. */ +struct libavl_allocator rb_allocator_default = + { + rb_malloc, + rb_free + }; + +#undef NDEBUG +#include + +/* Asserts that |rb_insert()| succeeds at inserting |item| into |table|. */ +void +(rb_assert_insert) (struct rb_table *table, void *item) +{ + void **p = rb_probe (table, item); + assert (p != NULL && *p == item); +} + +/* Asserts that |rb_delete()| really removes |item| from |table|, + and returns the removed item. */ +void * +(rb_assert_delete) (struct rb_table *table, void *item) +{ + void *p = rb_delete (table, item); + assert (p != NULL); + return p; +} + --- /dev/null 2005-02-10 18:43:56.833702072 -0800 +++ include/asterisk/events.h 2005-02-24 14:50:08.252683088 -0800 @@ -0,0 +1,78 @@ +/* + * Asterisk -- A telephony toolkit for Linux. + * + * Generic Event Handling System + * + * Copyright (C) 2005, BitStruct, LLC + * + * Paul Querna + * Robert Emanuele + * + * This program is free software, distributed under the terms of + * the GNU General Public License + */ + +#ifndef _ASTERISK_EVENTS_H +#define _ASTERISK_EVENTS_H + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +#define AST_EVENT_LINK_CREATE 0 +#define AST_EVENT_LINK_DESTROY 1 +#define AST_EVENT_LINK_RING 2 +#define AST_EVENT_LINK_IDLE 3 +#define AST_EVENT_LINK_BUSY 4 +#define AST_EVENT_LINK_RESERVED 5 +#define AST_EVENT_LINK_DIALING 6 +#define AST_EVENT_LINK_OFFHOOK 7 +#define AST_EVENT_LINK_DIAL_OFFHOOK 8 +#define AST_EVENT_VM_NEW 9 +#define AST_EVENT_TYPE_MAX 10 + +typedef int (*ast_event_handler_cb_type)(const char* contact, unsigned int event_type, + const char *source, void *data); + +typedef int (*ast_event_cleanup_cb_type)(void *data); + +/*! Initialize the Asterisk Events System */ +void ast_events_init(void); + +/*! Shutdown the Asterisk Events System */ +void ast_events_deinit(void); + +/*! Subscribe to an Event */ +/*! + * \param contact Channel to Contact when an event triggers + * \param event_type Subscribe to this event type + * \param source Where this event will come from + * \param timeout Time in seconds for this callback to exist. If -1, the callback will not expire. + * \param callback Function to run when this event triggers + * \param cleanup Function to run when this event times out or is destroyed + * \param data Opaque object passed to the callback function + * + * Note: Callbacks will be ran from a different thread. + */ +void ast_event_subscribe(const char* contact, unsigned int event_type, const char* source, int timeout, + ast_event_handler_cb_type callback, ast_event_cleanup_cb_type cleanup, void *data); + +void ast_event_unsubscribe(const char* contact, unsigned int event_type, const char* source, + ast_event_handler_cb_type callback, ast_event_cleanup_cb_type cleanup, void *data); + +/*! Trigger any Callbacks subscribed to this Event */ +/*! + * \param event_type Type of Event to Trigger + * \param source Where this event is coming from + */ +void ast_event_trigger(unsigned int event_type, char* source); + + +int ast_event_convert_from_state(int state); +int ast_event_convert_to_state(unsigned int state); + +#if defined(__cplusplus) || defined(c_plusplus) +} +#endif + +#endif --- /dev/null 2005-02-10 18:43:56.833702072 -0800 +++ include/asterisk/rb.h 2005-02-24 14:50:08.248683696 -0800 @@ -0,0 +1,122 @@ +/* Produced by texiweb from libavl.w. */ + +/* libavl - library for manipulation of binary trees. + Copyright (C) 1998-2002, 2004 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. + + The author may be contacted at on the Internet, or + write to Ben Pfaff, Stanford University, Computer Science Dept., 353 + Serra Mall, Stanford CA 94305, USA. +*/ + +#ifndef RB_H +#define RB_H 1 + +#include + +/* Function types. */ +typedef int rb_comparison_func (const void *rb_a, const void *rb_b, + void *rb_param); +typedef void rb_item_func (void *rb_item, void *rb_param); +typedef void *rb_copy_func (void *rb_item, void *rb_param); + +#ifndef LIBAVL_ALLOCATOR +#define LIBAVL_ALLOCATOR +/* Memory allocator. */ +struct libavl_allocator + { + void *(*libavl_malloc) (struct libavl_allocator *, size_t libavl_size); + void (*libavl_free) (struct libavl_allocator *, void *libavl_block); + }; +#endif + +/* Default memory allocator. */ +extern struct libavl_allocator rb_allocator_default; +void *rb_malloc (struct libavl_allocator *, size_t); +void rb_free (struct libavl_allocator *, void *); + +/* Maximum RB height. */ +#ifndef RB_MAX_HEIGHT +#define RB_MAX_HEIGHT 48 +#endif + +/* Tree data structure. */ +struct rb_table + { + struct rb_node *rb_root; /* Tree's root. */ + rb_comparison_func *rb_compare; /* Comparison function. */ + void *rb_param; /* Extra argument to |rb_compare|. */ + struct libavl_allocator *rb_alloc; /* Memory allocator. */ + size_t rb_count; /* Number of items in tree. */ + unsigned long rb_generation; /* Generation number. */ + }; + +/* Color of a red-black node. */ +enum rb_color + { + RB_BLACK, /* Black. */ + RB_RED /* Red. */ + }; + +/* A red-black tree node. */ +struct rb_node + { + struct rb_node *rb_link[2]; /* Subtrees. */ + void *rb_data; /* Pointer to data. */ + unsigned char rb_color; /* Color. */ + }; + +/* RB traverser structure. */ +struct rb_traverser + { + struct rb_table *rb_table; /* Tree being traversed. */ + struct rb_node *rb_node; /* Current node in tree. */ + struct rb_node *rb_stack[RB_MAX_HEIGHT]; + /* All the nodes above |rb_node|. */ + size_t rb_height; /* Number of nodes in |rb_parent|. */ + unsigned long rb_generation; /* Generation number. */ + }; + +/* Table functions. */ +struct rb_table *rb_create (rb_comparison_func *, void *, + struct libavl_allocator *); +struct rb_table *rb_copy (const struct rb_table *, rb_copy_func *, + rb_item_func *, struct libavl_allocator *); +void rb_destroy (struct rb_table *, rb_item_func *); +void **rb_probe (struct rb_table *, void *); +void *rb_insert (struct rb_table *, void *); +void *rb_replace (struct rb_table *, void *); +void *rb_delete (struct rb_table *, const void *); +void *rb_find (const struct rb_table *, const void *); +void rb_assert_insert (struct rb_table *, void *); +void *rb_assert_delete (struct rb_table *, void *); + +#define rb_count(table) ((size_t) (table)->rb_count) + +/* Table traverser functions. */ +void rb_t_init (struct rb_traverser *, struct rb_table *); +void *rb_t_first (struct rb_traverser *, struct rb_table *); +void *rb_t_last (struct rb_traverser *, struct rb_table *); +void *rb_t_find (struct rb_traverser *, struct rb_table *, void *); +void *rb_t_insert (struct rb_traverser *, struct rb_table *, void *); +void *rb_t_copy (struct rb_traverser *, const struct rb_traverser *); +void *rb_t_next (struct rb_traverser *); +void *rb_t_prev (struct rb_traverser *); +void *rb_t_cur (struct rb_traverser *); +void *rb_t_replace (struct rb_traverser *, void *); + +#endif /* rb.h */