diff options
Diffstat (limited to 'main')
-rw-r--r-- | main/pbx.c | 327 |
1 files changed, 299 insertions, 28 deletions
diff --git a/main/pbx.c b/main/pbx.c index 7bf4faab0..f6fd56137 100644 --- a/main/pbx.c +++ b/main/pbx.c @@ -354,8 +354,10 @@ static int hashtab_compare_extens(const void *ah_a, const void *ah_b) const struct ast_exten *ac = ah_a; const struct ast_exten *bc = ah_b; int x = strcmp(ac->exten, bc->exten); - if (x) /* if exten names are diff, then return */ + if (x) { /* if exten names are diff, then return */ return x; + } + /* but if they are the same, do the cidmatch values match? */ if (ac->matchcid && bc->matchcid) { return strcmp(ac->cidmatch,bc->cidmatch); @@ -728,6 +730,185 @@ static AST_RWLIST_HEAD_STATIC(hints, ast_hint); static AST_LIST_HEAD_NOLOCK_STATIC(statecbs, ast_state_cb); +#ifdef CONTEXT_DEBUG + +/* these routines are provided for doing run-time checks + on the extension structures, in case you are having + problems, this routine might help you localize where + the problem is occurring. It's kinda like a debug memory + allocator's arena checker... It'll eat up your cpu cycles! + but you'll see, if you call it in the right places, + right where your problems began... +*/ + +/* you can break on the check_contexts_trouble() +routine in your debugger to stop at the moment +there's a problem */ +void check_contexts_trouble(void); + +void check_contexts_trouble(void) +{ + int x = 1; + x = 2; +} + +static struct ast_context *find_context_locked(const char *context); +int check_contexts(char *, int); + +int check_contexts(char *file, int line ) +{ + struct ast_hashtab_iter *t1; + struct ast_context *c1, *c2; + int found = 0; + struct ast_exten *e1, *e2, *e3; + struct ast_exten ex; + + /* try to find inconsistencies */ + /* is every context in the context table in the context list and vice-versa ? */ + + if (!contexts_table) { + ast_log(LOG_NOTICE,"Called from: %s:%d: No contexts_table!\n", file, line); + usleep(500000); + } + + t1 = ast_hashtab_start_traversal(contexts_table); + while( (c1 = ast_hashtab_next(t1))) { + for(c2=contexts;c2;c2=c2->next) { + if (!strcmp(c1->name, c2->name)) { + found = 1; + break; + } + } + if (!found) { + ast_log(LOG_NOTICE,"Called from: %s:%d: Could not find the %s context in the linked list\n", file, line, c1->name); + check_contexts_trouble(); + } + } + ast_hashtab_end_traversal(t1); + for(c2=contexts;c2;c2=c2->next) { + c1 = find_context_locked(c2->name); + if (!c1) { + ast_log(LOG_NOTICE,"Could not find the %s context in the hashtab\n", c2->name); + check_contexts_trouble(); + } else + ast_unlock_contexts(); + } + + /* loop thru all contexts, and verify the exten structure compares to the + hashtab structure */ + for(c2=contexts;c2;c2=c2->next) { + c1 = find_context_locked(c2->name); + if (c1) + { + + ast_unlock_contexts(); + + /* is every entry in the root list also in the root_table? */ + for(e1 = c1->root; e1; e1=e1->next) + { + char dummy_name[1024]; + ex.exten = dummy_name; + ex.matchcid = e1->matchcid; + ex.cidmatch = e1->cidmatch; + ast_copy_string(dummy_name, e1->exten, sizeof(dummy_name)); + e2 = ast_hashtab_lookup(c1->root_table, &ex); + if (!e2) { + if (e1->matchcid) { + ast_log(LOG_NOTICE,"Called from: %s:%d: The %s context records the exten %s (CID match: %s) but it is not in its root_table\n", file, line, c2->name, dummy_name, e1->cidmatch ); + } else { + ast_log(LOG_NOTICE,"Called from: %s:%d: The %s context records the exten %s but it is not in its root_table\n", file, line, c2->name, dummy_name ); + } + check_contexts_trouble(); + } + } + + /* is every entry in the root_table also in the root list? */ + if (!c2->root_table) { + if (c2->root) { + ast_log(LOG_NOTICE,"Called from: %s:%d: No c2->root_table for context %s!\n", file, line, c2->name); + usleep(500000); + } + } else { + t1 = ast_hashtab_start_traversal(c2->root_table); + while( (e2 = ast_hashtab_next(t1)) ) { + for(e1=c2->root;e1;e1=e1->next) { + if (!strcmp(e1->exten, e2->exten)) { + found = 1; + break; + } + } + if (!found) { + ast_log(LOG_NOTICE,"Called from: %s:%d: The %s context records the exten %s but it is not in its root_table\n", file, line, c2->name, e2->exten); + check_contexts_trouble(); + } + + } + ast_hashtab_end_traversal(t1); + } + } + /* is every priority reflected in the peer_table at the head of the list? */ + + /* is every entry in the root list also in the root_table? */ + /* are the per-extension peer_tables in the right place? */ + + for(e1 = c2->root; e1; e1 = e1->next) { + + for(e2=e1;e2;e2=e2->peer) { + ex.priority = e2->priority; + if (e2 != e1 && e2->peer_table) { + ast_log(LOG_NOTICE,"Called from: %s:%d: The %s context, %s exten, %d priority has a peer_table entry, and shouldn't!\n", file, line, c2->name, e1->exten, e2->priority ); + check_contexts_trouble(); + } + + if (e2 != e1 && e2->peer_label_table) { + ast_log(LOG_NOTICE,"Called from: %s:%d: The %s context, %s exten, %d priority has a peer_label_table entry, and shouldn't!\n", file, line, c2->name, e1->exten, e2->priority ); + check_contexts_trouble(); + } + + if (e2 == e1 && !e2->peer_table){ + ast_log(LOG_NOTICE,"Called from: %s:%d: The %s context, %s exten, %d priority doesn't have a peer_table!\n", file, line, c2->name, e1->exten, e2->priority ); + check_contexts_trouble(); + } + + if (e2 == e1 && !e2->peer_label_table) { + ast_log(LOG_NOTICE,"Called from: %s:%d: The %s context, %s exten, %d priority doesn't have a peer_label_table!\n", file, line, c2->name, e1->exten, e2->priority ); + check_contexts_trouble(); + } + + + e3 = ast_hashtab_lookup(e1->peer_table, &ex); + if (!e3) { + ast_log(LOG_NOTICE,"Called from: %s:%d: The %s context, %s exten, %d priority is not reflected in the peer_table\n", file, line, c2->name, e1->exten, e2->priority ); + check_contexts_trouble(); + } + } + + if (!e1->peer_table){ + ast_log(LOG_NOTICE,"Called from: %s:%d: No e1->peer_table!\n", file, line); + usleep(500000); + } + + /* is every entry in the peer_table also in the peer list? */ + t1 = ast_hashtab_start_traversal(e1->peer_table); + while( (e2 = ast_hashtab_next(t1)) ) { + for(e3=e1;e3;e3=e3->peer) { + if (e3->priority == e2->priority) { + found = 1; + break; + } + } + if (!found) { + ast_log(LOG_NOTICE,"Called from: %s:%d: The %s context, %s exten, %d priority is not reflected in the peer list\n", file, line, c2->name, e1->exten, e2->priority ); + check_contexts_trouble(); + } + } + ast_hashtab_end_traversal(t1); + } + } + return 0; +} +#endif + /* \note This function is special. It saves the stack so that no matter how many times it is called, it returns to the same place */ @@ -4060,15 +4241,17 @@ int ast_context_remove_extension_callerid2(struct ast_context *con, const char * #ifdef NEED_DEBUG ast_verb(3,"Removing %s/%s/%d%s%s from trees, registrar=%s\n", con->name, extension, priority, matchcid ? "/" : "", matchcid ? callerid : "", registrar); #endif +#ifdef CONTEXT_DEBUG + check_contexts(__FILE__, __LINE__); +#endif /* find this particular extension */ ex.exten = dummy_name; - ex.matchcid = matchcid; + ex.matchcid = matchcid && !ast_strlen_zero(callerid); /* don't say match if there's no callerid */ ex.cidmatch = callerid; ast_copy_string(dummy_name, extension, sizeof(dummy_name)); exten = ast_hashtab_lookup(con->root_table, &ex); if (exten) { - if (priority == 0) - { + if (priority == 0) { exten2 = ast_hashtab_remove_this_object(con->root_table, exten); if (!exten2) ast_log(LOG_ERROR,"Trying to delete the exten %s from context %s, but could not remove from the root_table\n", extension, con->name); @@ -4135,7 +4318,8 @@ int ast_context_remove_extension_callerid2(struct ast_context *con, const char * /* scan the extension list to find first matching extension-registrar */ for (exten = con->root; exten; prev_exten = exten, exten = exten->next) { if (!strcmp(exten->exten, extension) && - (!registrar || !strcmp(exten->registrar, registrar))) + (!registrar || !strcmp(exten->registrar, registrar)) && + (!matchcid || (!ast_strlen_zero(callerid) && !ast_strlen_zero(exten->cidmatch) && !strcmp(exten->cidmatch, callerid)) || (ast_strlen_zero(callerid) && ast_strlen_zero(exten->cidmatch)))) break; } if (!exten) { @@ -4147,7 +4331,7 @@ int ast_context_remove_extension_callerid2(struct ast_context *con, const char * /* scan the priority list to remove extension with exten->priority == priority */ for (peer = exten, next_peer = exten->peer ? exten->peer : exten->next; - peer && !strcmp(peer->exten, extension); + peer && !strcmp(peer->exten, extension) && (!matchcid || (!ast_strlen_zero(callerid) && !ast_strlen_zero(peer->cidmatch) && !strcmp(peer->cidmatch,callerid)) || (ast_strlen_zero(callerid) && ast_strlen_zero(peer->cidmatch))); peer = next_peer, next_peer = next_peer ? (next_peer->peer ? next_peer->peer : next_peer->next) : NULL) { if ((priority == 0 || peer->priority == priority) && (!callerid || !matchcid || (matchcid && !strcmp(peer->cidmatch, callerid))) && @@ -4161,7 +4345,14 @@ int ast_context_remove_extension_callerid2(struct ast_context *con, const char * * The next node is either the next priority or the next extension */ struct ast_exten *next_node = peer->peer ? peer->peer : peer->next; - + if (peer->peer) { + /* move the peer_table and peer_label_table down to the next peer, if + it is there */ + peer->peer->peer_table = peer->peer_table; + peer->peer->peer_label_table = peer->peer_label_table; + peer->peer_table = NULL; + peer->peer_label_table = NULL; + } if (!prev_exten) { /* change the root... */ con->root = next_node; } else { @@ -4180,6 +4371,9 @@ int ast_context_remove_extension_callerid2(struct ast_context *con, const char * previous_peer = peer; } } +#ifdef CONTEXT_DEBUG + check_contexts(__FILE__, __LINE__); +#endif if (!already_locked) ast_unlock_context(con); return found ? 0 : -1; @@ -6361,8 +6555,10 @@ static int add_pri(struct ast_context *con, struct ast_exten *tmp, } if (!e) { /* go at the end, and ep is surely set because the list is not empty */ ast_hashtab_insert_safe(eh->peer_table, tmp); - if (tmp->label) + + if (tmp->label) { ast_hashtab_insert_safe(eh->peer_label_table, tmp); + } ep->peer = tmp; return 0; /* success */ } @@ -6383,11 +6579,16 @@ static int add_pri(struct ast_context *con, struct ast_exten *tmp, tmp->peer = e->peer; /* always meaningful */ if (ep) { /* We're in the peer list, just insert ourselves */ ast_hashtab_remove_object_via_lookup(eh->peer_table,e); - if (e->label) + + if (e->label) { ast_hashtab_remove_object_via_lookup(eh->peer_label_table,e); + } + ast_hashtab_insert_safe(eh->peer_table,tmp); - if (tmp->label) + if (tmp->label) { ast_hashtab_insert_safe(eh->peer_label_table,tmp); + } + ep->peer = tmp; } else if (el) { /* We're the first extension. Take over e's functions */ struct match_char *x = add_exten_to_pattern_tree(con, e, 1); @@ -6395,10 +6596,13 @@ static int add_pri(struct ast_context *con, struct ast_exten *tmp, tmp->peer_label_table = e->peer_label_table; ast_hashtab_remove_object_via_lookup(tmp->peer_table,e); ast_hashtab_insert_safe(tmp->peer_table,tmp); - if (e->label) + if (e->label) { ast_hashtab_remove_object_via_lookup(tmp->peer_label_table, e); - if (tmp->label) + } + if (tmp->label) { ast_hashtab_insert_safe(tmp->peer_label_table, tmp); + } + ast_hashtab_remove_object_via_lookup(con->root_table, e); ast_hashtab_insert_safe(con->root_table, tmp); el->next = tmp; @@ -6419,10 +6623,13 @@ static int add_pri(struct ast_context *con, struct ast_exten *tmp, tmp->peer_label_table = e->peer_label_table; ast_hashtab_remove_object_via_lookup(tmp->peer_table, e); ast_hashtab_insert_safe(tmp->peer_table, tmp); - if (e->label) + if (e->label) { ast_hashtab_remove_object_via_lookup(tmp->peer_label_table, e); - if (tmp->label) - ast_hashtab_insert_safe(tmp->peer_label_table, tmp); + } + if (tmp->label) { + ast_hashtab_insert_safe(tmp->peer_label_table, tmp); + } + ast_hashtab_remove_object_via_lookup(con->root_table, e); ast_hashtab_insert_safe(con->root_table, tmp); con->root = tmp; @@ -6446,8 +6653,9 @@ static int add_pri(struct ast_context *con, struct ast_exten *tmp, tmp->peer = e; tmp->next = e->next; /* extension chain, or NULL if e is not the first extension */ if (ep) { /* Easy enough, we're just in the peer list */ - if (tmp->label) + if (tmp->label) { ast_hashtab_insert_safe(eh->peer_label_table, tmp); + } ast_hashtab_insert_safe(eh->peer_table, tmp); ep->peer = tmp; } else { /* we are the first in some peer list, so link in the ext list */ @@ -6636,10 +6844,10 @@ int ast_add_extension2(struct ast_context *con, ast_hashtab_newsize_java, hashtab_hash_labels, 0); - if (label) + if (label) { ast_hashtab_insert_safe(tmp->peer_label_table, tmp); + } ast_hashtab_insert_safe(tmp->peer_table, tmp); - } else { /* this is the first exten in this context */ if (!con->root_table) con->root_table = ast_hashtab_create(27, @@ -6661,9 +6869,11 @@ int ast_add_extension2(struct ast_context *con, ast_hashtab_newsize_java, hashtab_hash_labels, 0); - if (label) + if (label) { ast_hashtab_insert_safe(con->root->peer_label_table, tmp); + } ast_hashtab_insert_safe(con->root->peer_table, tmp); + } ast_hashtab_insert_safe(con->root_table, tmp); ast_unlock_context(con); @@ -7128,12 +7338,20 @@ void __ast_context_destroy(struct ast_context *list, struct ast_hashtab *context for (tmp = list; tmp; ) { struct ast_context *next = NULL; /* next starting point */ - for (; tmp; tmpl = tmp, tmp = tmp->next) { - ast_debug(1, "check ctx %s %s\n", tmp->name, tmp->registrar); - if ( (registrar && !strcasecmp(tmp->registrar, registrar)) || (con && !strcasecmp(tmp->name, con->name)) ) { - break; /* found it */ + /* The following code used to skip forward to the next + context with matching registrar, but this didn't + make sense; individual priorities registrar'd to + the matching registrar could occur in any context! */ + ast_debug(1, "Investigate ctx %s %s\n", tmp->name, tmp->registrar); + if (con) { + for (; tmp; tmpl = tmp, tmp = tmp->next) { /* skip to the matching context */ + ast_debug(1, "check ctx %s %s\n", tmp->name, tmp->registrar); + if ( !strcasecmp(tmp->name, con->name) ) { + break; /* found it */ + } } } + if (!tmp) /* not found, we are done */ break; ast_wrlock_context(tmp); @@ -7142,19 +7360,66 @@ void __ast_context_destroy(struct ast_context *list, struct ast_hashtab *context /* then search thru and remove any extens that match registrar. */ struct ast_hashtab_iter *exten_iter; struct ast_hashtab_iter *prio_iter; + struct ast_ignorepat *ip, *ipl = NULL, *ipn = NULL; + struct ast_include *i, *pi = NULL, *ni = NULL; + struct ast_sw *sw = NULL; + + /* remove any ignorepats whose registrar matches */ + for (ip = tmp->ignorepats; ip; ip = ipn) { + ipn = ip->next; + if (!strcmp(ip->registrar, registrar)) { + if (ipl) { + ipl->next = ip->next; + ast_free(ip); + continue; /* don't change ipl */ + } else { + tmp->ignorepats = ip->next; + ast_free(ip); + continue; /* don't change ipl */ + } + } + ipl = ip; + } + /* remove any includes whose registrar matches */ + for (i = tmp->includes; i; i = ni) { + ni = i->next; + if (strcmp(i->registrar, registrar) == 0) { + /* remove from list */ + if (pi) { + pi->next = i->next; + /* free include */ + ast_free(i); + continue; /* don't change pi */ + } else { + tmp->includes = i->next; + /* free include */ + ast_free(i); + continue; /* don't change pi */ + } + } + pi = i; + } + /* remove any switches whose registrar matches */ + AST_LIST_TRAVERSE_SAFE_BEGIN(&tmp->alts, sw, list) { + if (strcmp(sw->registrar,registrar) == 0) { + AST_LIST_REMOVE_CURRENT(list); + ast_free(sw); + } + } + AST_LIST_TRAVERSE_SAFE_END if (tmp->root_table) { /* it is entirely possible that the context is EMPTY */ exten_iter = ast_hashtab_start_traversal(tmp->root_table); while ((exten_item=ast_hashtab_next(exten_iter))) { prio_iter = ast_hashtab_start_traversal(exten_item->peer_table); while ((prio_item=ast_hashtab_next(prio_iter))) { - if (strcmp(prio_item->registrar, registrar) != 0) { + if (!prio_item->registrar || strcmp(prio_item->registrar, registrar) != 0) { continue; } ast_verb(3, "Remove %s/%s/%d, registrar=%s; con=%s(%p); con->root=%p\n", - tmp->name, prio_item->exten, prio_item->priority, registrar, con? con->name : "<nil>", con, con? con->root_table: NULL); - - ast_context_remove_extension2(tmp, prio_item->exten, prio_item->priority, registrar, 1); + tmp->name, prio_item->exten, prio_item->priority, registrar, con? con->name : "<nil>", con, con? con->root_table: NULL); + /* set matchcid to 1 to insure we get a direct match, and NULL registrar to make sure no wildcarding is done */ + ast_context_remove_extension_callerid2(tmp, prio_item->exten, prio_item->priority, prio_item->cidmatch, 1, NULL, 1); } ast_hashtab_end_traversal(prio_iter); } @@ -7162,7 +7427,9 @@ void __ast_context_destroy(struct ast_context *list, struct ast_hashtab *context } /* delete the context if it's registrar matches, is empty, has refcount of 1, */ - if (strcmp(tmp->registrar, registrar) == 0 && tmp->refcount < 2 && !tmp->root) { + /* it's not empty, if it has includes, ignorepats, or switches that are registered from + another registrar. It's not empty if there are any extensions */ + if (strcmp(tmp->registrar, registrar) == 0 && tmp->refcount < 2 && !tmp->root && !tmp->ignorepats && !tmp->includes && AST_LIST_EMPTY(&tmp->alts)) { ast_debug(1, "delete ctx %s %s\n", tmp->name, tmp->registrar); ast_hashtab_remove_this_object(contexttab, tmp); @@ -7176,7 +7443,11 @@ void __ast_context_destroy(struct ast_context *list, struct ast_hashtab *context ast_unlock_context(tmp); __ast_internal_context_destroy(tmp); } else { + ast_debug(1,"Couldn't delete ctx %s/%s; refc=%d; tmp.root=%p\n", tmp->name, tmp->registrar, + tmp->refcount, tmp->root); ast_unlock_context(tmp); + next = tmp->next; + tmpl = tmp; } } else if (con) { ast_verb(3, "Deleting context %s registrar=%s\n", tmp->name, tmp->registrar); |