diff options
Diffstat (limited to 'main/pbx.c')
-rw-r--r-- | main/pbx.c | 592 |
1 files changed, 381 insertions, 211 deletions
diff --git a/main/pbx.c b/main/pbx.c index 8d9658f36..427c0cca9 100644 --- a/main/pbx.c +++ b/main/pbx.c @@ -48,6 +48,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/cdr.h" #include "asterisk/config.h" #include "asterisk/term.h" +#include "asterisk/time.h" #include "asterisk/manager.h" #include "asterisk/ast_expr.h" #include "asterisk/linkedlists.h" @@ -140,8 +141,8 @@ struct ast_exten { void *data; /*!< Data to use (arguments) */ void (*datad)(void *); /*!< Data destructor */ struct ast_exten *peer; /*!< Next higher priority with our extension */ - struct ast_hashtab *peer_tree; /*!< Priorities list in tree form -- only on the head of the peer list */ - struct ast_hashtab *peer_label_tree; /*!< labeled priorities in the peer list -- only on the head of the peer list */ + struct ast_hashtab *peer_table; /*!< Priorities list in hashtab form -- only on the head of the peer list */ + struct ast_hashtab *peer_label_table; /*!< labeled priorities in the peers -- only on the head of the peer list */ const char *registrar; /*!< Registrar */ struct ast_exten *next; /*!< Extension with a greater ID */ char stuff[0]; @@ -203,12 +204,13 @@ struct scoreboard /* make sure all fields are 0 before calling new_find_extensi struct ast_context { ast_rwlock_t lock; /*!< A lock to prevent multiple threads from clobbering the context */ struct ast_exten *root; /*!< The root of the list of extensions */ - struct ast_hashtab *root_tree; /*!< For exact matches on the extensions in the pattern tree, and for traversals of the pattern_tree */ + struct ast_hashtab *root_table; /*!< For exact matches on the extensions in the pattern tree, and for traversals of the pattern_tree */ struct match_char *pattern_tree; /*!< A tree to speed up extension pattern matching */ struct ast_context *next; /*!< Link them together */ struct ast_include *includes; /*!< Include other contexts */ struct ast_ignorepat *ignorepats; /*!< Patterns for which to continue playing dialtone */ const char *registrar; /*!< Registrar */ + int refcount; /*!< each module that would have created this context should inc/dec this as appropriate */ AST_LIST_HEAD_NOLOCK(, ast_sw) alts; /*!< Alternative switches */ ast_mutex_t macrolock; /*!< A lock to implement "exclusive" macros - held whilst a call is executing in the macro */ char name[0]; /*!< Name of the context */ @@ -327,17 +329,18 @@ static struct match_char *add_pattern_node(struct ast_context *con, struct match static void create_match_char_tree(struct ast_context *con); static struct ast_exten *get_canmatch_exten(struct match_char *node); static void destroy_pattern_tree(struct match_char *pattern_tree); -static int hashtab_compare_contexts(const void *ah_a, const void *ah_b); +int ast_hashtab_compare_contexts(const void *ah_a, const void *ah_b); static int hashtab_compare_extens(const void *ha_a, const void *ah_b); static int hashtab_compare_exten_numbers(const void *ah_a, const void *ah_b); static int hashtab_compare_exten_labels(const void *ah_a, const void *ah_b); -static unsigned int hashtab_hash_contexts(const void *obj); +unsigned int ast_hashtab_hash_contexts(const void *obj); static unsigned int hashtab_hash_extens(const void *obj); static unsigned int hashtab_hash_priority(const void *obj); static unsigned int hashtab_hash_labels(const void *obj); +static void __ast_internal_context_destroy( struct ast_context *con); /* labels, contexts are case sensitive priority numbers are ints */ -static int hashtab_compare_contexts(const void *ah_a, const void *ah_b) +int ast_hashtab_compare_contexts(const void *ah_a, const void *ah_b) { const struct ast_context *ac = ah_a; const struct ast_context *bc = ah_b; @@ -376,7 +379,7 @@ static int hashtab_compare_exten_labels(const void *ah_a, const void *ah_b) return strcmp(ac->label, bc->label); } -static unsigned int hashtab_hash_contexts(const void *obj) +unsigned int ast_hashtab_hash_contexts(const void *obj) { const struct ast_context *ac = obj; return ast_hashtab_hash_string(ac->name); @@ -684,7 +687,7 @@ static struct pbx_builtin { }; static struct ast_context *contexts; -static struct ast_hashtab *contexts_tree = NULL; +static struct ast_hashtab *contexts_table = NULL; AST_RWLOCK_DEFINE_STATIC(conlock); /*!< Lock for the ast_context list */ @@ -1252,11 +1255,11 @@ static void create_match_char_tree(struct ast_context *con) int biggest_bucket, resizes, numobjs, numbucks; ast_log(LOG_DEBUG,"Creating Extension Trie for context %s\n", con->name); - ast_hashtab_get_stats(con->root_tree, &biggest_bucket, &resizes, &numobjs, &numbucks); + ast_hashtab_get_stats(con->root_table, &biggest_bucket, &resizes, &numobjs, &numbucks); ast_log(LOG_DEBUG,"This tree has %d objects in %d bucket lists, longest list=%d objects, and has resized %d times\n", numobjs, numbucks, biggest_bucket, resizes); #endif - t1 = ast_hashtab_start_traversal(con->root_tree); + t1 = ast_hashtab_start_traversal(con->root_table); while( (e1 = ast_hashtab_next(t1)) ) { if (e1->exten) add_exten_to_pattern_tree(con, e1, 0); @@ -1582,12 +1585,13 @@ struct fake_context /* this struct is purely for matching in the hashtab */ { ast_rwlock_t lock; struct ast_exten *root; - struct ast_hashtab *root_tree; + struct ast_hashtab *root_table; struct match_char *pattern_tree; struct ast_context *next; struct ast_include *includes; struct ast_ignorepat *ignorepats; - const char *registrar; + const char *registrar; + int refcount; AST_LIST_HEAD_NOLOCK(, ast_sw) alts; ast_mutex_t macrolock; char name[256]; @@ -1599,8 +1603,8 @@ struct ast_context *ast_context_find(const char *name) struct fake_context item; strncpy(item.name,name,256); ast_rdlock_contexts(); - if( contexts_tree ) { - tmp = ast_hashtab_lookup(contexts_tree,&item); + if( contexts_table ) { + tmp = ast_hashtab_lookup(contexts_table,&item); } else { while ( (tmp = ast_walk_contexts(tmp)) ) { if (!name || !strcasecmp(name, tmp->name)) @@ -1668,7 +1672,7 @@ struct ast_exten *pbx_find_extension(struct ast_channel *chan, else { /* look in contexts */ struct fake_context item; strncpy(item.name,context,256); - tmp = ast_hashtab_lookup(contexts_tree,&item); + tmp = ast_hashtab_lookup(contexts_table,&item); #ifdef NOTNOW tmp = NULL; while ((tmp = ast_walk_contexts(tmp)) ) { @@ -1690,7 +1694,7 @@ struct ast_exten *pbx_find_extension(struct ast_channel *chan, score.total_specificity = 0; score.exten = 0; score.total_length = 0; - if (!tmp->pattern_tree && tmp->root_tree) + if (!tmp->pattern_tree && tmp->root_table) { create_match_char_tree(tmp); #ifdef NEED_DEBUG @@ -1750,9 +1754,9 @@ struct ast_exten *pbx_find_extension(struct ast_channel *chan, if (action == E_FINDLABEL && label ) { if (q->status < STATUS_NO_LABEL) q->status = STATUS_NO_LABEL; - e = ast_hashtab_lookup(eroot->peer_label_tree, &pattern); + e = ast_hashtab_lookup(eroot->peer_label_table, &pattern); } else { - e = ast_hashtab_lookup(eroot->peer_tree, &pattern); + e = ast_hashtab_lookup(eroot->peer_table, &pattern); } if (e) { /* found a valid match */ q->status = STATUS_SUCCESS; @@ -1786,9 +1790,9 @@ struct ast_exten *pbx_find_extension(struct ast_channel *chan, if (action == E_FINDLABEL && label ) { if (q->status < STATUS_NO_LABEL) q->status = STATUS_NO_LABEL; - e = ast_hashtab_lookup(eroot->peer_label_tree, &pattern); + e = ast_hashtab_lookup(eroot->peer_label_table, &pattern); } else { - e = ast_hashtab_lookup(eroot->peer_tree, &pattern); + e = ast_hashtab_lookup(eroot->peer_table, &pattern); } #ifdef NOTNOW while ( (e = ast_walk_extension_priorities(eroot, e)) ) { @@ -2465,7 +2469,7 @@ static void pbx_substitute_variables_helper_full(struct ast_channel *c, struct v vare++; } if (brackets) - ast_log(LOG_NOTICE, "Error in extension logic (missing '}')\n"); + ast_log(LOG_WARNING, "Error in extension logic (missing '}')\n"); len = vare - vars - 1; /* Skip totally over variable string */ @@ -2552,7 +2556,7 @@ static void pbx_substitute_variables_helper_full(struct ast_channel *c, struct v vare++; } if (brackets) - ast_log(LOG_NOTICE, "Error in extension logic (missing ']')\n"); + ast_log(LOG_WARNING, "Error in extension logic (missing ']')\n"); len = vare - vars - 1; /* Skip totally over expression */ @@ -3479,14 +3483,14 @@ static int increase_call_count(const struct ast_channel *c) ast_mutex_lock(&maxcalllock); if (option_maxcalls) { if (countcalls >= option_maxcalls) { - ast_log(LOG_NOTICE, "Maximum call limit of %d calls exceeded by '%s'!\n", option_maxcalls, c->name); + ast_log(LOG_WARNING, "Maximum call limit of %d calls exceeded by '%s'!\n", option_maxcalls, c->name); failed = -1; } } if (option_maxload) { getloadavg(&curloadavg, 1); if (curloadavg >= option_maxload) { - ast_log(LOG_NOTICE, "Maximum loadavg limit of %f load exceeded by '%s' (currently %f)!\n", option_maxload, c->name, curloadavg); + ast_log(LOG_WARNING, "Maximum loadavg limit of %f load exceeded by '%s' (currently %f)!\n", option_maxload, c->name, curloadavg); failed = -1; } } @@ -3527,10 +3531,10 @@ static void destroy_exten(struct ast_exten *e) if (e->priority == PRIORITY_HINT) ast_remove_hint(e); - if (e->peer_tree) - ast_hashtab_destroy(e->peer_tree,0); - if (e->peer_label_tree) - ast_hashtab_destroy(e->peer_label_tree, 0); + if (e->peer_table) + ast_hashtab_destroy(e->peer_table,0); + if (e->peer_label_table) + ast_hashtab_destroy(e->peer_label_table, 0); if (e->datad) e->datad(e->data); ast_free(e); @@ -3627,8 +3631,7 @@ static struct ast_context *find_context_locked(const char *context) ast_copy_string(item.name, context, sizeof(item.name)); ast_rdlock_contexts(); - - c = ast_hashtab_lookup(contexts_tree,&item); + c = ast_hashtab_lookup(contexts_table,&item); #ifdef NOTNOW @@ -3789,19 +3792,19 @@ int ast_context_remove_extension2(struct ast_context *con, const char *extension /* Handle this is in the new world */ #ifdef NEED_DEBUG - ast_log(LOG_NOTICE,"Removing %s/%s/%d from trees, registrar=%s\n", con->name, extension, priority, registrar); + ast_verb(3,"Removing %s/%s/%d from trees, registrar=%s\n", con->name, extension, priority, registrar); #endif /* find this particular extension */ ex.exten = dummy_name; ex.matchcid = 0; ast_copy_string(dummy_name,extension, sizeof(dummy_name)); - exten = ast_hashtab_lookup(con->root_tree, &ex); + exten = ast_hashtab_lookup(con->root_table, &ex); if (exten) { if (priority == 0) { - exten2 = ast_hashtab_remove_this_object(con->root_tree, exten); + 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_tree\n", extension, con->name); + 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); if (con->pattern_tree) { struct match_char *x = add_exten_to_pattern_tree(con, exten, 1); @@ -3815,24 +3818,28 @@ int ast_context_remove_extension2(struct ast_context *con, const char *extension } } else { ex.priority = priority; - exten2 = ast_hashtab_lookup(exten->peer_tree, &ex); + exten2 = ast_hashtab_lookup(exten->peer_table, &ex); if (exten2) { if (exten2->label) { /* if this exten has a label, remove that, too */ - exten3 = ast_hashtab_remove_this_object(exten->peer_label_tree,exten2); + exten3 = ast_hashtab_remove_this_object(exten->peer_label_table,exten2); if (!exten3) - ast_log(LOG_ERROR,"Did not remove this priority label (%d/%s) from the peer_label_tree of context %s, extension %s!\n", priority, exten2->label, con->name, exten2->exten); + ast_log(LOG_ERROR,"Did not remove this priority label (%d/%s) from the peer_label_table of context %s, extension %s!\n", priority, exten2->label, con->name, exten2->exten); } - exten3 = ast_hashtab_remove_this_object(exten->peer_tree, exten2); + exten3 = ast_hashtab_remove_this_object(exten->peer_table, exten2); if (!exten3) - ast_log(LOG_ERROR,"Did not remove this priority (%d) from the peer_tree of context %s, extension %s!\n", priority, con->name, exten2->exten); - if (ast_hashtab_size(exten->peer_tree) == 0) { + ast_log(LOG_ERROR,"Did not remove this priority (%d) from the peer_table of context %s, extension %s!\n", priority, con->name, exten2->exten); + if (exten2 == exten && exten2->peer) { + exten2 = ast_hashtab_remove_this_object(con->root_table, exten); + ast_hashtab_insert_immediate(con->root_table, exten2->peer); + } + if (ast_hashtab_size(exten->peer_table) == 0) { /* well, if the last priority of an exten is to be removed, then, the extension is removed, too! */ - exten3 = ast_hashtab_remove_this_object(con->root_tree, exten); + exten3 = ast_hashtab_remove_this_object(con->root_table, exten); if (!exten3) - ast_log(LOG_ERROR,"Did not remove this exten (%s) from the context root_tree (%s) (priority %d)\n", exten->exten, con->name, priority); + ast_log(LOG_ERROR,"Did not remove this exten (%s) from the context root_table (%s) (priority %d)\n", exten->exten, con->name, priority); if (con->pattern_tree) { struct match_char *x = add_exten_to_pattern_tree(con, exten, 1); if (x->exten) { /* this test for safety purposes */ @@ -3848,7 +3855,7 @@ int ast_context_remove_extension2(struct ast_context *con, const char *extension } } else { /* hmmm? this exten is not in this pattern tree? */ - ast_log(LOG_WARNING,"Cannot find extension %s in root_tree in context %s\n", + ast_log(LOG_WARNING,"Cannot find extension %s in root_table in context %s\n", extension, con->name); } #ifdef NEED_DEBUG @@ -3905,10 +3912,10 @@ int ast_context_remove_extension2(struct ast_context *con, const char *extension */ struct ast_exten *next_node = peer->peer ? peer->peer : peer->next; if (next_node && next_node == peer->peer) { - next_node->peer_tree = exten->peer_tree; /* move the priority hash tabs over */ - exten->peer_tree = 0; - next_node->peer_label_tree = exten->peer_label_tree; - exten->peer_label_tree = 0; + next_node->peer_table = exten->peer_table; /* move the priority hash tabs over */ + exten->peer_table = 0; + next_node->peer_label_table = exten->peer_label_table; + exten->peer_label_table = 0; } if (!prev_exten) { /* change the root... */ con->root = next_node; @@ -3946,7 +3953,7 @@ int ast_context_lockmacro(const char *context) ast_rdlock_contexts(); strncpy(item.name,context,256); - c = ast_hashtab_lookup(contexts_tree,&item); + c = ast_hashtab_lookup(contexts_table,&item); if (c) ret = 0; @@ -3984,7 +3991,7 @@ int ast_context_unlockmacro(const char *context) ast_rdlock_contexts(); strncpy(item.name, context, 256); - c = ast_hashtab_lookup(contexts_tree,&item); + c = ast_hashtab_lookup(contexts_table,&item); if (c) ret = 0; #ifdef NOTNOW @@ -4578,7 +4585,7 @@ static int show_dialplan_helper(int fd, const char *context, const char *exten, if (exten) { /* Check all includes for the requested extension */ if (includecount >= AST_PBX_MAX_STACK) { - ast_log(LOG_NOTICE, "Maximum include depth exceeded!\n"); + ast_log(LOG_WARNING, "Maximum include depth exceeded!\n"); } else { int dupe = 0; int x; @@ -5127,42 +5134,37 @@ int ast_unregister_application(const char *app) return tmp ? 0 : -1; } -static struct ast_context *__ast_context_create(struct ast_context **extcontexts, const char *name, const char *registrar, int existsokay) +struct ast_context *ast_context_find_or_create(struct ast_context **extcontexts, struct ast_hashtab *exttable, const char *name, const char *registrar) { struct ast_context *tmp, **local_contexts; struct fake_context search; int length = sizeof(struct ast_context) + strlen(name) + 1; - if (!contexts_tree) { - contexts_tree = ast_hashtab_create(17, - hashtab_compare_contexts, + if (!contexts_table) { + contexts_table = ast_hashtab_create(17, + ast_hashtab_compare_contexts, ast_hashtab_resize_java, ast_hashtab_newsize_java, - hashtab_hash_contexts, + ast_hashtab_hash_contexts, 0); } + strncpy(search.name,name,sizeof(search.name)); if (!extcontexts) { ast_rdlock_contexts(); local_contexts = &contexts; - strncpy(search.name,name,sizeof(search.name)); - tmp = ast_hashtab_lookup(contexts_tree, &search); - if (!existsokay && tmp) { - ast_log(LOG_WARNING, "Tried to register context '%s', already in use\n", name); - } + tmp = ast_hashtab_lookup(contexts_table, &search); ast_unlock_contexts(); - if (tmp) + if (tmp) { + tmp->refcount++; return tmp; + } } else { /* local contexts just in a linked list; search there for the new context; slow, linear search, but not frequent */ local_contexts = extcontexts; - for (tmp = *local_contexts; tmp; tmp = tmp->next) { - if (!strcasecmp(tmp->name, name)) { - if (!existsokay) { - ast_log(LOG_WARNING, "Tried to register context '%s', already in use\n", name); - tmp = NULL; - } - return tmp; - } + tmp = ast_hashtab_lookup(exttable, &search); + if (tmp) { + tmp->refcount++; + return tmp; } } @@ -5171,19 +5173,26 @@ static struct ast_context *__ast_context_create(struct ast_context **extcontexts ast_mutex_init(&tmp->macrolock); strcpy(tmp->name, name); tmp->root = NULL; - tmp->root_tree = NULL; + tmp->root_table = NULL; tmp->registrar = registrar; tmp->includes = NULL; tmp->ignorepats = NULL; + tmp->refcount = 1; + } else { + ast_log(LOG_ERROR, "Danger! We failed to allocate a context for %s!\n", name); + return NULL; } + if (!extcontexts) { ast_wrlock_contexts(); tmp->next = *local_contexts; *local_contexts = tmp; - ast_hashtab_insert_safe(contexts_tree, tmp); /*put this context into the tree */ + ast_hashtab_insert_safe(contexts_table, tmp); /*put this context into the tree */ ast_unlock_contexts(); } else { tmp->next = *local_contexts; + if (exttable) + ast_hashtab_insert_immediate(exttable, tmp); /*put this context into the tree */ *local_contexts = tmp; } ast_debug(1, "Registered context '%s'\n", tmp->name); @@ -5191,16 +5200,7 @@ static struct ast_context *__ast_context_create(struct ast_context **extcontexts return tmp; } -struct ast_context *ast_context_create(struct ast_context **extcontexts, const char *name, const char *registrar) -{ - return __ast_context_create(extcontexts, name, registrar, 0); -} - -struct ast_context *ast_context_find_or_create(struct ast_context **extcontexts, const char *name, const char *registrar) -{ - return __ast_context_create(extcontexts, name, registrar, 1); -} -void __ast_context_destroy(struct ast_context *con, const char *registrar); +void __ast_context_destroy(struct ast_context *list, struct ast_hashtab *contexttab, struct ast_context *con, const char *registrar); struct store_hint { char *context; @@ -5213,17 +5213,89 @@ struct store_hint { AST_LIST_HEAD(store_hints, store_hint); +/* the purpose of this routine is to duplicate a context, with all its substructure, + except for any extens that have a matching registrar */ +static void context_merge(struct ast_context **extcontexts, struct ast_hashtab *exttable, struct ast_context *context, const char *registrar) +{ + struct ast_context *new = ast_hashtab_lookup(exttable, context); /* is there a match in the new set? */ + struct ast_exten *exten_item, *prio_item, *new_exten_item, *new_prio_item; + struct ast_hashtab_iter *exten_iter; + struct ast_hashtab_iter *prio_iter; + int insert_count = 0; + + /* We'll traverse all the extensions/prios, and see which are not registrar'd with + the current registrar, and copy them to the new context. If the new context does not + exist, we'll create it "on demand". If no items are in this context to copy, then we'll + only create the empty matching context if the old one meets the criteria */ + if (context->root_table) { + exten_iter = ast_hashtab_start_traversal(context->root_table); + while ((exten_item=ast_hashtab_next(exten_iter))) { + if (new) { + new_exten_item = ast_hashtab_lookup(new->root_table, exten_item); + } else { + new_exten_item = NULL; + } + prio_iter = ast_hashtab_start_traversal(exten_item->peer_table); + while ((prio_item=ast_hashtab_next(prio_iter))) { + int res1; + + if (new_exten_item) { + new_prio_item = ast_hashtab_lookup(new_exten_item->peer_table, prio_item); + } else { + new_prio_item = NULL; + } + if (strcmp(prio_item->registrar,registrar) == 0) { + continue; + } + /* make sure the new context exists, so we have somewhere to stick this exten/prio */ + if (!new) { + new = ast_context_find_or_create(extcontexts, exttable, context->name, prio_item->registrar); /* a new context created via priority from a different context in the old dialplan, gets its registrar from the prio's registrar */ + } + if (!new) { + ast_log(LOG_ERROR,"Could not allocate a new context for %s in merge_and_delete! Danger!\n", context->name); + return; /* no sense continuing. */ + } + /* we will not replace existing entries in the new context with stuff from the old context. + but, if this is because of some sort of registrar conflict, we ought to say something... */ + res1 = ast_add_extension2(new, 0, prio_item->exten, prio_item->priority, prio_item->label, + prio_item->cidmatch, prio_item->app, prio_item->data, prio_item->datad, prio_item->registrar); + if (!res1 && new_exten_item && new_prio_item){ + ast_verb(3,"Dropping old dialplan item %s/%s/%d [%s(%s)] (registrar=%s) due to conflict with new dialplan\n", + context->name, prio_item->exten, prio_item->priority, prio_item->app, (char*)prio_item->data, prio_item->registrar); + } else { + prio_item->data = NULL; /* we pass the priority data from the old to the new */ + insert_count++; + } + } + ast_hashtab_end_traversal(prio_iter); + } + ast_hashtab_end_traversal(exten_iter); + } + + if (!insert_count && !new && (strcmp(context->registrar, registrar) != 0 || + (strcmp(context->registrar, registrar) == 0 && context->refcount > 1))) { + + /* we could have given it the registrar of the other module who incremented the refcount, + but that's not available, so we give it the registrar we know about */ + new = ast_context_find_or_create(extcontexts, exttable, context->name, context->registrar); + } +} + + /* XXX this does not check that multiple contexts are merged */ -void ast_merge_contexts_and_delete(struct ast_context **extcontexts, const char *registrar) +void ast_merge_contexts_and_delete(struct ast_context **extcontexts, struct ast_hashtab *exttable, const char *registrar) { - struct ast_context *tmp, *lasttmp = NULL; + double ft; + struct ast_context *tmp, *oldcontextslist; + struct ast_hashtab *oldtable; struct store_hints store = AST_LIST_HEAD_INIT_VALUE; struct store_hint *this; struct ast_hint *hint; struct ast_exten *exten; int length; struct ast_state_cb *thiscb, *prevcb; - + struct ast_hashtab_iter *iter; + /* it is very important that this function hold the hint list lock _and_ the conlock during its operation; not only do we need to ensure that the list of contexts and extensions does not change, but also that no hint callbacks (watchers) are @@ -5232,8 +5304,29 @@ void ast_merge_contexts_and_delete(struct ast_context **extcontexts, const char in addition, the locks _must_ be taken in this order, because there are already other code paths that use this order */ + + struct timeval begintime, writelocktime, endlocktime, enddeltime; + int wrlock_ver; + + begintime = ast_tvnow(); + ast_rdlock_contexts(); + iter = ast_hashtab_start_traversal(contexts_table); + while ((tmp=ast_hashtab_next(iter))) { + context_merge(extcontexts, exttable, tmp, registrar); + } + ast_hashtab_end_traversal(iter); + wrlock_ver = ast_wrlock_contexts_version(); + + ast_unlock_contexts(); /* this feels real retarded, but you must do + what you must do If this isn't done, the following + wrlock is a guraranteed deadlock */ ast_wrlock_contexts(); + if (ast_wrlock_contexts_version() > wrlock_ver+1) { + ast_log(LOG_WARNING,"Something changed the contexts in the middle of merging contexts!\n"); + } + AST_RWLIST_WRLOCK(&hints); + writelocktime = ast_tvnow(); /* preserve all watchers for hints associated with this registrar */ AST_RWLIST_TRAVERSE(&hints, hint, list) { @@ -5252,36 +5345,14 @@ void ast_merge_contexts_and_delete(struct ast_context **extcontexts, const char } } - tmp = *extcontexts; - if (registrar) { - /* XXX remove previous contexts from same registrar */ - ast_debug(1, "must remove any reg %s\n", registrar); - __ast_context_destroy(NULL,registrar); - while (tmp) { - lasttmp = tmp; - tmp = tmp->next; - } - } else { - /* XXX remove contexts with the same name */ - while (tmp) { - ast_log(LOG_WARNING, "must remove %s reg %s\n", tmp->name, tmp->registrar); - __ast_context_destroy(tmp,tmp->registrar); - lasttmp = tmp; - tmp = tmp->next; - } - } - tmp = *extcontexts; - while (tmp) { - ast_hashtab_insert_safe(contexts_tree, tmp); /*put this context into the tree */ - tmp = tmp->next; - } - if (lasttmp) { - lasttmp->next = contexts; - contexts = *extcontexts; - *extcontexts = NULL; - } else - ast_log(LOG_WARNING, "Requested contexts didn't get merged\n"); + /* save the old table and list */ + oldtable = contexts_table; + oldcontextslist = contexts; + /* move in the new table and list */ + contexts_table = exttable; + contexts = *extcontexts; + /* restore the watchers for hints that can be found; notify those that cannot be restored */ @@ -5316,7 +5387,36 @@ void ast_merge_contexts_and_delete(struct ast_context **extcontexts, const char AST_RWLIST_UNLOCK(&hints); ast_unlock_contexts(); + endlocktime = ast_tvnow(); + + /* the old list and hashtab no longer are relevant, delete them while the rest of asterisk + is now freely using the new stuff instead */ + + ast_hashtab_destroy(oldtable, NULL); + for (tmp = oldcontextslist; tmp; ) { + struct ast_context *next; /* next starting point */ + next = tmp->next; + __ast_internal_context_destroy(tmp); + tmp = next; + } + enddeltime = ast_tvnow(); + + ft = ast_tvdiff_us(writelocktime, begintime); + ft /= 1000000.0; + ast_verb(3,"Time to scan old dialplan and merge leftovers back into the new: %8.6f sec\n", ft); + + ft = ast_tvdiff_us(endlocktime, writelocktime); + ft /= 1000000.0; + ast_verb(3,"Time to restore hints and swap in new dialplan: %8.6f sec\n", ft); + + ft = ast_tvdiff_us(enddeltime, endlocktime); + ft /= 1000000.0; + ast_verb(3,"Time to delete the old dialplan: %8.6f sec\n", ft); + + ft = ast_tvdiff_us(enddeltime, begintime); + ft /= 1000000.0; + ast_verb(3,"Total time merge_contexts_delete: %8.6f sec\n", ft); return; } @@ -5854,6 +5954,7 @@ int ast_add_extension(const char *context, int replace, const char *extension, application, data, datad, registrar); ast_unlock_contexts(); } + return ret; } @@ -5980,9 +6081,9 @@ static int add_pri(struct ast_context *con, struct ast_exten *tmp, break; } if (!e) { /* go at the end, and ep is surely set because the list is not empty */ - ast_hashtab_insert_safe(eh->peer_tree, tmp); + ast_hashtab_insert_safe(eh->peer_table, tmp); if (tmp->label) - ast_hashtab_insert_safe(eh->peer_label_tree, tmp); + ast_hashtab_insert_safe(eh->peer_label_table, tmp); ep->peer = tmp; return 0; /* success */ } @@ -6002,25 +6103,25 @@ static int add_pri(struct ast_context *con, struct ast_exten *tmp, tmp->next = e->next; /* not meaningful if we are not first in the peer list */ 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_tree,e); + ast_hashtab_remove_object_via_lookup(eh->peer_table,e); if (e->label) - ast_hashtab_remove_object_via_lookup(eh->peer_label_tree,e); - ast_hashtab_insert_safe(eh->peer_tree,tmp); + ast_hashtab_remove_object_via_lookup(eh->peer_label_table,e); + ast_hashtab_insert_safe(eh->peer_table,tmp); if (tmp->label) - ast_hashtab_insert_safe(eh->peer_label_tree,tmp); + 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); - tmp->peer_tree = e->peer_tree; - tmp->peer_label_tree = e->peer_label_tree; - ast_hashtab_remove_object_via_lookup(tmp->peer_tree,e); - ast_hashtab_insert_safe(tmp->peer_tree,tmp); + tmp->peer_table = e->peer_table; + 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) - ast_hashtab_remove_object_via_lookup(tmp->peer_label_tree,e); + ast_hashtab_remove_object_via_lookup(tmp->peer_label_table, e); if (tmp->label) - ast_hashtab_insert_safe(tmp->peer_label_tree,tmp); - ast_hashtab_remove_object_via_lookup(con->root_tree, e); - ast_hashtab_insert_safe(con->root_tree, tmp); + 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; /* The pattern trie points to this exten; replace the pointer, and all will be well */ @@ -6032,18 +6133,18 @@ static int add_pri(struct ast_context *con, struct ast_exten *tmp, } } else { /* We're the very first extension. */ struct match_char *x = add_exten_to_pattern_tree(con, e, 1); - ast_hashtab_remove_object_via_lookup(con->root_tree,e); - ast_hashtab_insert_safe(con->root_tree,tmp); - tmp->peer_tree = e->peer_tree; - tmp->peer_label_tree = e->peer_label_tree; - ast_hashtab_remove_object_via_lookup(tmp->peer_tree,e); - ast_hashtab_insert_safe(tmp->peer_tree,tmp); + ast_hashtab_remove_object_via_lookup(con->root_table, e); + ast_hashtab_insert_safe(con->root_table, tmp); + tmp->peer_table = e->peer_table; + 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) - ast_hashtab_remove_object_via_lookup(tmp->peer_label_tree,e); + ast_hashtab_remove_object_via_lookup(tmp->peer_label_table, e); if (tmp->label) - ast_hashtab_insert_safe(tmp->peer_label_tree,tmp); - ast_hashtab_remove_object_via_lookup(con->root_tree, e); - ast_hashtab_insert_safe(con->root_tree, tmp); + 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; /* The pattern trie points to this exten; replace the pointer, and all will be well */ @@ -6064,20 +6165,20 @@ static int add_pri(struct ast_context *con, struct ast_exten *tmp, 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) - ast_hashtab_insert_safe(eh->peer_label_tree, tmp); - ast_hashtab_insert_safe(eh->peer_tree, tmp); + 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 */ - tmp->peer_tree = e->peer_tree; - tmp->peer_label_tree = e ->peer_label_tree; - e->peer_tree = 0; - e->peer_label_tree = 0; - ast_hashtab_insert_safe(tmp->peer_tree,tmp); + tmp->peer_table = e->peer_table; + tmp->peer_label_table = e->peer_label_table; + e->peer_table = 0; + e->peer_label_table = 0; + ast_hashtab_insert_safe(tmp->peer_table, tmp); if (tmp->label) { - ast_hashtab_insert_safe(tmp->peer_label_tree,tmp); + ast_hashtab_insert_safe(tmp->peer_label_table, tmp); } - ast_hashtab_remove_object_via_lookup(con->root_tree,e); - ast_hashtab_insert_safe(con->root_tree,tmp); + ast_hashtab_remove_object_via_lookup(con->root_table, e); + ast_hashtab_insert_safe(con->root_table, tmp); if (el) el->next = tmp; /* in the middle... */ else @@ -6159,6 +6260,9 @@ int ast_add_extension2(struct ast_context *con, if (!(tmp = ast_calloc(1, length))) return -1; + if (ast_strlen_zero(label)) /* let's turn empty labels to a null ptr */ + label = 0; + /* use p as dst in assignments, as the fields are const char * */ p = tmp->stuff; if (label) { @@ -6170,7 +6274,7 @@ int ast_add_extension2(struct ast_context *con, p += ext_strncpy(p, extension, strlen(extension) + 1) + 1; tmp->priority = priority; tmp->cidmatch = p; /* but use p for assignments below */ - if (callerid) { + if (!ast_strlen_zero(callerid)) { p += ext_strncpy(p, callerid, strlen(callerid) + 1) + 1; tmp->matchcid = 1; } else { @@ -6192,11 +6296,11 @@ int ast_add_extension2(struct ast_context *con, dummy_exten.exten = dummy_name; dummy_exten.matchcid = 0; dummy_exten.cidmatch = 0; - tmp2 = ast_hashtab_lookup(con->root_tree,&dummy_exten); + tmp2 = ast_hashtab_lookup(con->root_table, &dummy_exten); if (!tmp2) { /* hmmm, not in the trie; */ add_exten_to_pattern_tree(con, tmp, 0); - ast_hashtab_insert_safe(con->root_tree, tmp); /* for the sake of completeness */ + ast_hashtab_insert_safe(con->root_table, tmp); /* for the sake of completeness */ } } res = 0; /* some compilers will think it is uninitialized otherwise */ @@ -6230,48 +6334,48 @@ int ast_add_extension2(struct ast_context *con, tmp->next = e; if (el) { /* there is another exten already in this context */ el->next = tmp; - tmp->peer_tree = ast_hashtab_create(13, + tmp->peer_table = ast_hashtab_create(13, hashtab_compare_exten_numbers, ast_hashtab_resize_java, ast_hashtab_newsize_java, hashtab_hash_priority, 0); - tmp->peer_label_tree = ast_hashtab_create(7, + tmp->peer_label_table = ast_hashtab_create(7, hashtab_compare_exten_labels, ast_hashtab_resize_java, ast_hashtab_newsize_java, hashtab_hash_labels, 0); if (label) - ast_hashtab_insert_safe(tmp->peer_label_tree,tmp); - ast_hashtab_insert_safe(tmp->peer_tree, tmp); + 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_tree) - con->root_tree = ast_hashtab_create(27, + if (!con->root_table) + con->root_table = ast_hashtab_create(27, hashtab_compare_extens, ast_hashtab_resize_java, ast_hashtab_newsize_java, hashtab_hash_extens, 0); con->root = tmp; - con->root->peer_tree = ast_hashtab_create(13, + con->root->peer_table = ast_hashtab_create(13, hashtab_compare_exten_numbers, ast_hashtab_resize_java, ast_hashtab_newsize_java, hashtab_hash_priority, 0); - con->root->peer_label_tree = ast_hashtab_create(7, + con->root->peer_label_table = ast_hashtab_create(7, hashtab_compare_exten_labels, ast_hashtab_resize_java, ast_hashtab_newsize_java, hashtab_hash_labels, 0); if (label) - ast_hashtab_insert_safe(con->root->peer_label_tree,tmp); - ast_hashtab_insert_safe(con->root->peer_tree, tmp); + 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_tree, tmp); + ast_hashtab_insert_safe(con->root_table, tmp); ast_unlock_context(con); if (tmp->priority == PRIORITY_HINT) ast_add_hint(tmp); @@ -6287,7 +6391,7 @@ int ast_add_extension2(struct ast_context *con, } if (tmp->matchcid) { - ast_verb(3, "Added extension '%s' priority %d (CID match '%s')to %s\n", + ast_verb(3, "Added extension '%s' priority %d (CID match '%s') to %s\n", tmp->exten, tmp->priority, tmp->cidmatch, con->name); } else { ast_verb(3, "Added extension '%s' priority %d to %s\n", @@ -6684,68 +6788,125 @@ outgoing_app_cleanup: return res; } -void __ast_context_destroy(struct ast_context *con, const char *registrar) +/* this is the guts of destroying a context -- + freeing up the structure, traversing and destroying the + extensions, switches, ignorepats, includes, etc. etc. */ + +static void __ast_internal_context_destroy( struct ast_context *con) { - struct ast_context *tmp, *tmpl=NULL; struct ast_include *tmpi; struct ast_sw *sw; struct ast_exten *e, *el, *en; struct ast_ignorepat *ipi; + struct ast_context *tmp = con; - for (tmp = contexts; tmp; ) { - struct ast_context *next; /* next starting point */ + for (tmpi = tmp->includes; tmpi; ) { /* Free includes */ + struct ast_include *tmpil = tmpi; + tmpi = tmpi->next; + ast_free(tmpil); + } + for (ipi = tmp->ignorepats; ipi; ) { /* Free ignorepats */ + struct ast_ignorepat *ipl = ipi; + ipi = ipi->next; + ast_free(ipl); + } + /* destroy the hash tabs */ + if (tmp->root_table) { + ast_hashtab_destroy(tmp->root_table, 0); + } + /* and destroy the pattern tree */ + if (tmp->pattern_tree) + destroy_pattern_tree(tmp->pattern_tree); + + while ((sw = AST_LIST_REMOVE_HEAD(&tmp->alts, list))) + ast_free(sw); + for (e = tmp->root; e;) { + for (en = e->peer; en;) { + el = en; + en = en->peer; + destroy_exten(el); + } + el = e; + e = e->next; + destroy_exten(el); + } + tmp->root = NULL; + ast_rwlock_destroy(&tmp->lock); + ast_free(tmp); +} + + +void __ast_context_destroy(struct ast_context *list, struct ast_hashtab *contexttab, struct ast_context *con, const char *registrar) +{ + struct ast_context *tmp, *tmpl=NULL; + struct ast_exten *exten_item, *prio_item; + + + 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(registrar, tmp->registrar)) && - (!con || !strcasecmp(tmp->name, con->name)) ) + if ( registrar || (con && strcasecmp(tmp->name, con->name)) ) break; /* found it */ } if (!tmp) /* not found, we are done */ break; ast_wrlock_context(tmp); - ast_debug(1, "delete ctx %s %s\n", tmp->name, tmp->registrar); - ast_hashtab_remove_this_object(contexts_tree,tmp); - - next = tmp->next; - if (tmpl) - tmpl->next = next; - else - contexts = next; - /* Okay, now we're safe to let it go -- in a sense, we were - ready to let it go as soon as we locked it. */ - ast_unlock_context(tmp); - for (tmpi = tmp->includes; tmpi; ) { /* Free includes */ - struct ast_include *tmpil = tmpi; - tmpi = tmpi->next; - ast_free(tmpil); - } - for (ipi = tmp->ignorepats; ipi; ) { /* Free ignorepats */ - struct ast_ignorepat *ipl = ipi; - ipi = ipi->next; - ast_free(ipl); - } - /* destroy the hash tabs */ - if (tmp->root_tree) { - ast_hashtab_destroy(tmp->root_tree, 0); - } - /* and destroy the pattern tree */ - if (tmp->pattern_tree) - destroy_pattern_tree(tmp->pattern_tree); - - while ((sw = AST_LIST_REMOVE_HEAD(&tmp->alts, list))) - ast_free(sw); - for (e = tmp->root; e;) { - for (en = e->peer; en;) { - el = en; - en = en->peer; - destroy_exten(el); + + if (registrar) { + /* then search thru and remove any extens that match registrar. */ + struct ast_hashtab_iter *exten_iter; + struct ast_hashtab_iter *prio_iter; + + 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) { + 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); + } + ast_hashtab_end_traversal(prio_iter); + } + ast_hashtab_end_traversal(exten_iter); } - el = e; - e = e->next; - destroy_exten(el); + + /* 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) { + ast_debug(1, "delete ctx %s %s\n", tmp->name, tmp->registrar); + ast_hashtab_remove_this_object(contexttab, tmp); + + next = tmp->next; + if (tmpl) + tmpl->next = next; + else + contexts = next; + /* Okay, now we're safe to let it go -- in a sense, we were + ready to let it go as soon as we locked it. */ + ast_unlock_context(tmp); + __ast_internal_context_destroy(tmp); + } + } else if (con) { + ast_verb(3, "Deleting context %s registrar=%s\n", tmp->name, tmp->registrar); + ast_debug(1, "delete ctx %s %s\n", tmp->name, tmp->registrar); + ast_hashtab_remove_this_object(contexttab, tmp); + + next = tmp->next; + if (tmpl) + tmpl->next = next; + else + contexts = next; + /* Okay, now we're safe to let it go -- in a sense, we were + ready to let it go as soon as we locked it. */ + ast_unlock_context(tmp); + __ast_internal_context_destroy(tmp); } - ast_rwlock_destroy(&tmp->lock); - ast_free(tmp); + /* if we have a specific match, we are done, otherwise continue */ tmp = con ? NULL : next; } @@ -6754,7 +6915,7 @@ void __ast_context_destroy(struct ast_context *con, const char *registrar) void ast_context_destroy(struct ast_context *con, const char *registrar) { ast_wrlock_contexts(); - __ast_context_destroy(con,registrar); + __ast_context_destroy(contexts, contexts_table, con,registrar); ast_unlock_contexts(); } @@ -6899,7 +7060,7 @@ static int pbx_builtin_hangup(struct ast_channel *chan, void *data) return -1; } - ast_log(LOG_NOTICE, "Invalid cause given to Hangup(): \"%s\"\n", (char *) data); + ast_log(LOG_WARNING, "Invalid cause given to Hangup(): \"%s\"\n", (char *) data); } if (!chan->hangupcause) { @@ -7568,12 +7729,21 @@ int load_pbx(void) return 0; } +static int conlock_wrlock_version = 0; + +int ast_wrlock_contexts_version(void) +{ + return conlock_wrlock_version; +} /* * Lock context list functions ... */ int ast_wrlock_contexts() { + int res = ast_rwlock_wrlock(&conlock); + if (!res) + ast_atomic_fetchadd_int(&conlock_wrlock_version, 1); return ast_rwlock_wrlock(&conlock); } |