#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"
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];
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 */
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;
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);
};
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 */
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);
{
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];
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))
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)) ) {
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
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;
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)) ) {
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 */
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 */
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;
}
}
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);
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
/* 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);
}
} 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 */
}
} 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
*/
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;
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;
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
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;
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;
}
}
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);
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;
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
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) {
}
}
- 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
*/
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;
}
application, data, datad, registrar);
ast_unlock_contexts();
}
+
return ret;
}
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 */
}
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 */
}
} 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 */
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
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) {
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 {
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 */
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);
}
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",
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;
}
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();
}
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) {
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);
}
#include <string.h>
#include "asterisk/logger.h"
+#include "asterisk/lock.h"
+#include "asterisk/hashtab.h"
#include "asterisk/ael_structs.h"
pval * linku1(pval *head, pval *tail);
#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
typedef union YYSTYPE
-#line 54 "ael.y"
+#line 56 "ael.y"
{
int intval; /* integer value, typically flags */
char *str; /* strings */
struct pval *pval; /* full objects */
}
/* Line 198 of yacc.c. */
-#line 238 "ael.tab.c"
+#line 240 "ael.tab.c"
YYSTYPE;
# define yystype YYSTYPE /* obsolescent; will be withdrawn */
# define YYSTYPE_IS_DECLARED 1
/* Copy the second part of user declarations. */
-#line 60 "ael.y"
+#line 62 "ael.y"
/* declaring these AFTER the union makes things a lot simpler! */
void yyerror(YYLTYPE *locp, struct parse_io *parseio, char const *s);
/* Line 221 of yacc.c. */
-#line 283 "ael.tab.c"
+#line 285 "ael.tab.c"
#ifdef short
# undef short
/* YYRLINE[YYN] -- source line where rule number YYN was defined. */
static const yytype_uint16 yyrline[] =
{
- 0, 186, 186, 189, 190, 191, 194, 195, 196, 197,
- 200, 201, 204, 213, 214, 215, 216, 217, 220, 226,
- 232, 233, 234, 237, 237, 243, 243, 250, 251, 252,
- 253, 256, 257, 258, 261, 262, 263, 264, 265, 266,
- 267, 268, 269, 272, 277, 281, 289, 294, 299, 308,
- 309, 310, 316, 321, 325, 333, 333, 337, 340, 343,
- 354, 355, 362, 363, 367, 371, 377, 378, 383, 391,
- 392, 396, 402, 411, 414, 415, 416, 419, 422, 425,
- 426, 427, 425, 433, 437, 438, 439, 440, 443, 443,
- 476, 477, 478, 479, 483, 486, 487, 490, 491, 494,
- 497, 501, 505, 509, 515, 516, 520, 523, 529, 529,
- 534, 542, 542, 553, 560, 563, 564, 567, 568, 571,
- 574, 575, 578, 582, 586, 592, 593, 596, 597, 598,
- 604, 609, 614, 615, 616, 618, 621, 622, 629, 630,
- 631, 634, 637
+ 0, 188, 188, 191, 192, 193, 196, 197, 198, 199,
+ 202, 203, 206, 215, 216, 217, 218, 219, 222, 228,
+ 234, 235, 236, 239, 239, 245, 245, 252, 253, 254,
+ 255, 258, 259, 260, 263, 264, 265, 266, 267, 268,
+ 269, 270, 271, 274, 279, 283, 291, 296, 301, 310,
+ 311, 312, 318, 323, 327, 335, 335, 339, 342, 345,
+ 356, 357, 364, 365, 369, 373, 379, 380, 385, 393,
+ 394, 398, 404, 413, 416, 417, 418, 421, 424, 427,
+ 428, 429, 427, 435, 439, 440, 441, 442, 445, 445,
+ 478, 479, 480, 481, 485, 488, 489, 492, 493, 496,
+ 499, 503, 507, 511, 517, 518, 522, 525, 531, 531,
+ 536, 544, 544, 555, 562, 565, 566, 569, 570, 573,
+ 576, 577, 580, 584, 588, 594, 595, 598, 599, 600,
+ 606, 611, 616, 617, 618, 620, 623, 624, 631, 632,
+ 633, 636, 639
};
#endif
switch (yytype)
{
case 43: /* "word" */
-#line 178 "ael.y"
+#line 180 "ael.y"
{ free((yyvaluep->str));};
-#line 1456 "ael.tab.c"
+#line 1458 "ael.tab.c"
break;
case 46: /* "objects" */
-#line 165 "ael.y"
+#line 167 "ael.y"
{
destroy_pval((yyvaluep->pval));
prev_word=0;
};
-#line 1464 "ael.tab.c"
+#line 1466 "ael.tab.c"
break;
case 47: /* "object" */
-#line 165 "ael.y"
+#line 167 "ael.y"
{
destroy_pval((yyvaluep->pval));
prev_word=0;
};
-#line 1472 "ael.tab.c"
+#line 1474 "ael.tab.c"
break;
case 48: /* "context_name" */
-#line 178 "ael.y"
+#line 180 "ael.y"
{ free((yyvaluep->str));};
-#line 1477 "ael.tab.c"
+#line 1479 "ael.tab.c"
break;
case 49: /* "context" */
-#line 165 "ael.y"
+#line 167 "ael.y"
{
destroy_pval((yyvaluep->pval));
prev_word=0;
};
-#line 1485 "ael.tab.c"
+#line 1487 "ael.tab.c"
break;
case 51: /* "macro" */
-#line 165 "ael.y"
+#line 167 "ael.y"
{
destroy_pval((yyvaluep->pval));
prev_word=0;
};
-#line 1493 "ael.tab.c"
+#line 1495 "ael.tab.c"
break;
case 52: /* "globals" */
-#line 165 "ael.y"
+#line 167 "ael.y"
{
destroy_pval((yyvaluep->pval));
prev_word=0;
};
-#line 1501 "ael.tab.c"
+#line 1503 "ael.tab.c"
break;
case 53: /* "global_statements" */
-#line 165 "ael.y"
+#line 167 "ael.y"
{
destroy_pval((yyvaluep->pval));
prev_word=0;
};
-#line 1509 "ael.tab.c"
+#line 1511 "ael.tab.c"
break;
case 54: /* "assignment" */
-#line 165 "ael.y"
+#line 167 "ael.y"
{
destroy_pval((yyvaluep->pval));
prev_word=0;
};
-#line 1517 "ael.tab.c"
+#line 1519 "ael.tab.c"
break;
case 56: /* "local_assignment" */
-#line 165 "ael.y"
+#line 167 "ael.y"
{
destroy_pval((yyvaluep->pval));
prev_word=0;
};
-#line 1525 "ael.tab.c"
+#line 1527 "ael.tab.c"
break;
case 58: /* "arglist" */
-#line 165 "ael.y"
+#line 167 "ael.y"
{
destroy_pval((yyvaluep->pval));
prev_word=0;
};
-#line 1533 "ael.tab.c"
+#line 1535 "ael.tab.c"
break;
case 59: /* "elements" */
-#line 165 "ael.y"
+#line 167 "ael.y"
{
destroy_pval((yyvaluep->pval));
prev_word=0;
};
-#line 1541 "ael.tab.c"
+#line 1543 "ael.tab.c"
break;
case 60: /* "element" */
-#line 165 "ael.y"
+#line 167 "ael.y"
{
destroy_pval((yyvaluep->pval));
prev_word=0;
};
-#line 1549 "ael.tab.c"
+#line 1551 "ael.tab.c"
break;
case 61: /* "ignorepat" */
-#line 165 "ael.y"
+#line 167 "ael.y"
{
destroy_pval((yyvaluep->pval));
prev_word=0;
};
-#line 1557 "ael.tab.c"
+#line 1559 "ael.tab.c"
break;
case 62: /* "extension" */
-#line 165 "ael.y"
+#line 167 "ael.y"
{
destroy_pval((yyvaluep->pval));
prev_word=0;
};
-#line 1565 "ael.tab.c"
+#line 1567 "ael.tab.c"
break;
case 63: /* "statements" */
-#line 165 "ael.y"
+#line 167 "ael.y"
{
destroy_pval((yyvaluep->pval));
prev_word=0;
};
-#line 1573 "ael.tab.c"
+#line 1575 "ael.tab.c"
break;
case 64: /* "timerange" */
-#line 178 "ael.y"
+#line 180 "ael.y"
{ free((yyvaluep->str));};
-#line 1578 "ael.tab.c"
+#line 1580 "ael.tab.c"
break;
case 65: /* "timespec" */
-#line 165 "ael.y"
+#line 167 "ael.y"
{
destroy_pval((yyvaluep->pval));
prev_word=0;
};
-#line 1586 "ael.tab.c"
+#line 1588 "ael.tab.c"
break;
case 66: /* "test_expr" */
-#line 178 "ael.y"
+#line 180 "ael.y"
{ free((yyvaluep->str));};
-#line 1591 "ael.tab.c"
+#line 1593 "ael.tab.c"
break;
case 68: /* "if_like_head" */
-#line 165 "ael.y"
+#line 167 "ael.y"
{
destroy_pval((yyvaluep->pval));
prev_word=0;
};
-#line 1599 "ael.tab.c"
+#line 1601 "ael.tab.c"
break;
case 69: /* "word_list" */
-#line 178 "ael.y"
+#line 180 "ael.y"
{ free((yyvaluep->str));};
-#line 1604 "ael.tab.c"
+#line 1606 "ael.tab.c"
break;
case 71: /* "word3_list" */
-#line 178 "ael.y"
+#line 180 "ael.y"
{ free((yyvaluep->str));};
-#line 1609 "ael.tab.c"
+#line 1611 "ael.tab.c"
break;
case 72: /* "goto_word" */
-#line 178 "ael.y"
+#line 180 "ael.y"
{ free((yyvaluep->str));};
-#line 1614 "ael.tab.c"
+#line 1616 "ael.tab.c"
break;
case 73: /* "switch_statement" */
-#line 165 "ael.y"
+#line 167 "ael.y"
{
destroy_pval((yyvaluep->pval));
prev_word=0;
};
-#line 1622 "ael.tab.c"
+#line 1624 "ael.tab.c"
break;
case 74: /* "statement" */
-#line 165 "ael.y"
+#line 167 "ael.y"
{
destroy_pval((yyvaluep->pval));
prev_word=0;
};
-#line 1630 "ael.tab.c"
+#line 1632 "ael.tab.c"
break;
case 79: /* "opt_else" */
-#line 165 "ael.y"
+#line 167 "ael.y"
{
destroy_pval((yyvaluep->pval));
prev_word=0;
};
-#line 1638 "ael.tab.c"
+#line 1640 "ael.tab.c"
break;
case 80: /* "target" */
-#line 165 "ael.y"
+#line 167 "ael.y"
{
destroy_pval((yyvaluep->pval));
prev_word=0;
};
-#line 1646 "ael.tab.c"
+#line 1648 "ael.tab.c"
break;
case 81: /* "opt_pri" */
-#line 178 "ael.y"
+#line 180 "ael.y"
{ free((yyvaluep->str));};
-#line 1651 "ael.tab.c"
+#line 1653 "ael.tab.c"
break;
case 82: /* "jumptarget" */
-#line 165 "ael.y"
+#line 167 "ael.y"
{
destroy_pval((yyvaluep->pval));
prev_word=0;
};
-#line 1659 "ael.tab.c"
+#line 1661 "ael.tab.c"
break;
case 83: /* "macro_call" */
-#line 165 "ael.y"
+#line 167 "ael.y"
{
destroy_pval((yyvaluep->pval));
prev_word=0;
};
-#line 1667 "ael.tab.c"
+#line 1669 "ael.tab.c"
break;
case 85: /* "application_call_head" */
-#line 165 "ael.y"
+#line 167 "ael.y"
{
destroy_pval((yyvaluep->pval));
prev_word=0;
};
-#line 1675 "ael.tab.c"
+#line 1677 "ael.tab.c"
break;
case 87: /* "application_call" */
-#line 165 "ael.y"
+#line 167 "ael.y"
{
destroy_pval((yyvaluep->pval));
prev_word=0;
};
-#line 1683 "ael.tab.c"
+#line 1685 "ael.tab.c"
break;
case 88: /* "opt_word" */
-#line 178 "ael.y"
+#line 180 "ael.y"
{ free((yyvaluep->str));};
-#line 1688 "ael.tab.c"
+#line 1690 "ael.tab.c"
break;
case 89: /* "eval_arglist" */
-#line 165 "ael.y"
+#line 167 "ael.y"
{
destroy_pval((yyvaluep->pval));
prev_word=0;
};
-#line 1696 "ael.tab.c"
+#line 1698 "ael.tab.c"
break;
case 90: /* "case_statements" */
-#line 165 "ael.y"
+#line 167 "ael.y"
{
destroy_pval((yyvaluep->pval));
prev_word=0;
};
-#line 1704 "ael.tab.c"
+#line 1706 "ael.tab.c"
break;
case 91: /* "case_statement" */
-#line 165 "ael.y"
+#line 167 "ael.y"
{
destroy_pval((yyvaluep->pval));
prev_word=0;
};
-#line 1712 "ael.tab.c"
+#line 1714 "ael.tab.c"
break;
case 92: /* "macro_statements" */
-#line 165 "ael.y"
+#line 167 "ael.y"
{
destroy_pval((yyvaluep->pval));
prev_word=0;
};
-#line 1720 "ael.tab.c"
+#line 1722 "ael.tab.c"
break;
case 93: /* "macro_statement" */
-#line 165 "ael.y"
+#line 167 "ael.y"
{
destroy_pval((yyvaluep->pval));
prev_word=0;
};
-#line 1728 "ael.tab.c"
+#line 1730 "ael.tab.c"
break;
case 94: /* "switches" */
-#line 165 "ael.y"
+#line 167 "ael.y"
{
destroy_pval((yyvaluep->pval));
prev_word=0;
};
-#line 1736 "ael.tab.c"
+#line 1738 "ael.tab.c"
break;
case 95: /* "eswitches" */
-#line 165 "ael.y"
+#line 167 "ael.y"
{
destroy_pval((yyvaluep->pval));
prev_word=0;
};
-#line 1744 "ael.tab.c"
+#line 1746 "ael.tab.c"
break;
case 96: /* "switchlist" */
-#line 165 "ael.y"
+#line 167 "ael.y"
{
destroy_pval((yyvaluep->pval));
prev_word=0;
};
-#line 1752 "ael.tab.c"
+#line 1754 "ael.tab.c"
break;
case 97: /* "included_entry" */
-#line 165 "ael.y"
+#line 167 "ael.y"
{
destroy_pval((yyvaluep->pval));
prev_word=0;
};
-#line 1760 "ael.tab.c"
+#line 1762 "ael.tab.c"
break;
case 98: /* "includeslist" */
-#line 165 "ael.y"
+#line 167 "ael.y"
{
destroy_pval((yyvaluep->pval));
prev_word=0;
};
-#line 1768 "ael.tab.c"
+#line 1770 "ael.tab.c"
break;
case 99: /* "includes" */
-#line 165 "ael.y"
+#line 167 "ael.y"
{
destroy_pval((yyvaluep->pval));
prev_word=0;
};
-#line 1776 "ael.tab.c"
+#line 1778 "ael.tab.c"
break;
default:
switch (yyn)
{
case 2:
-#line 186 "ael.y"
+#line 188 "ael.y"
{ (yyval.pval) = parseio->pval = (yyvsp[(1) - (1)].pval); ;}
break;
case 3:
-#line 189 "ael.y"
+#line 191 "ael.y"
{(yyval.pval)=(yyvsp[(1) - (1)].pval);;}
break;
case 4:
-#line 190 "ael.y"
+#line 192 "ael.y"
{ (yyval.pval) = linku1((yyvsp[(1) - (2)].pval), (yyvsp[(2) - (2)].pval)); ;}
break;
case 5:
-#line 191 "ael.y"
+#line 193 "ael.y"
{(yyval.pval)=(yyvsp[(1) - (2)].pval);;}
break;
case 6:
-#line 194 "ael.y"
+#line 196 "ael.y"
{(yyval.pval)=(yyvsp[(1) - (1)].pval);;}
break;
case 7:
-#line 195 "ael.y"
+#line 197 "ael.y"
{(yyval.pval)=(yyvsp[(1) - (1)].pval);;}
break;
case 8:
-#line 196 "ael.y"
+#line 198 "ael.y"
{(yyval.pval)=(yyvsp[(1) - (1)].pval);;}
break;
case 9:
-#line 197 "ael.y"
+#line 199 "ael.y"
{(yyval.pval)=0;/* allow older docs to be read */;}
break;
case 10:
-#line 200 "ael.y"
+#line 202 "ael.y"
{ (yyval.str) = (yyvsp[(1) - (1)].str); ;}
break;
case 11:
-#line 201 "ael.y"
+#line 203 "ael.y"
{ (yyval.str) = strdup("default"); ;}
break;
case 12:
-#line 204 "ael.y"
+#line 206 "ael.y"
{
(yyval.pval) = npval2(PV_CONTEXT, &(yylsp[(1) - (6)]), &(yylsp[(6) - (6)]));
(yyval.pval)->u1.str = (yyvsp[(3) - (6)].str);
break;
case 13:
-#line 213 "ael.y"
+#line 215 "ael.y"
{ (yyval.intval) = 1; ;}
break;
case 14:
-#line 214 "ael.y"
+#line 216 "ael.y"
{ (yyval.intval) = 0; ;}
break;
case 15:
-#line 215 "ael.y"
+#line 217 "ael.y"
{ (yyval.intval) = 2; ;}
break;
case 16:
-#line 216 "ael.y"
+#line 218 "ael.y"
{ (yyval.intval)=3; ;}
break;
case 17:
-#line 217 "ael.y"
+#line 219 "ael.y"
{ (yyval.intval)=3; ;}
break;
case 18:
-#line 220 "ael.y"
+#line 222 "ael.y"
{
(yyval.pval) = npval2(PV_MACRO, &(yylsp[(1) - (8)]), &(yylsp[(8) - (8)]));
(yyval.pval)->u1.str = (yyvsp[(2) - (8)].str); (yyval.pval)->u2.arglist = (yyvsp[(4) - (8)].pval); (yyval.pval)->u3.macro_statements = (yyvsp[(7) - (8)].pval);
break;
case 19:
-#line 226 "ael.y"
+#line 228 "ael.y"
{
(yyval.pval) = npval2(PV_GLOBALS, &(yylsp[(1) - (4)]), &(yylsp[(4) - (4)]));
(yyval.pval)->u1.statements = (yyvsp[(3) - (4)].pval);
break;
case 20:
-#line 232 "ael.y"
+#line 234 "ael.y"
{ (yyval.pval) = NULL; ;}
break;
case 21:
-#line 233 "ael.y"
+#line 235 "ael.y"
{(yyval.pval) = linku1((yyvsp[(1) - (2)].pval), (yyvsp[(2) - (2)].pval)); ;}
break;
case 22:
-#line 234 "ael.y"
+#line 236 "ael.y"
{(yyval.pval)=(yyvsp[(2) - (2)].pval);;}
break;
case 23:
-#line 237 "ael.y"
+#line 239 "ael.y"
{ reset_semicount(parseio->scanner); ;}
break;
case 24:
-#line 237 "ael.y"
+#line 239 "ael.y"
{
(yyval.pval) = npval2(PV_VARDEC, &(yylsp[(1) - (5)]), &(yylsp[(5) - (5)]));
(yyval.pval)->u1.str = (yyvsp[(1) - (5)].str);
break;
case 25:
-#line 243 "ael.y"
+#line 245 "ael.y"
{ reset_semicount(parseio->scanner); ;}
break;
case 26:
-#line 243 "ael.y"
+#line 245 "ael.y"
{
(yyval.pval) = npval2(PV_LOCALVARDEC, &(yylsp[(1) - (6)]), &(yylsp[(6) - (6)]));
(yyval.pval)->u1.str = (yyvsp[(2) - (6)].str);
break;
case 27:
-#line 250 "ael.y"
+#line 252 "ael.y"
{ (yyval.pval) = NULL; ;}
break;
case 28:
-#line 251 "ael.y"
+#line 253 "ael.y"
{ (yyval.pval) = nword((yyvsp[(1) - (1)].str), &(yylsp[(1) - (1)])); ;}
break;
case 29:
-#line 252 "ael.y"
+#line 254 "ael.y"
{ (yyval.pval) = linku1((yyvsp[(1) - (3)].pval), nword((yyvsp[(3) - (3)].str), &(yylsp[(3) - (3)]))); ;}
break;
case 30:
-#line 253 "ael.y"
+#line 255 "ael.y"
{(yyval.pval)=(yyvsp[(1) - (2)].pval);;}
break;
case 31:
-#line 256 "ael.y"
+#line 258 "ael.y"
{(yyval.pval)=0;;}
break;
case 32:
-#line 257 "ael.y"
+#line 259 "ael.y"
{ (yyval.pval) = linku1((yyvsp[(1) - (2)].pval), (yyvsp[(2) - (2)].pval)); ;}
break;
case 33:
-#line 258 "ael.y"
+#line 260 "ael.y"
{ (yyval.pval)=(yyvsp[(2) - (2)].pval);;}
break;
case 34:
-#line 261 "ael.y"
+#line 263 "ael.y"
{(yyval.pval)=(yyvsp[(1) - (1)].pval);;}
break;
case 35:
-#line 262 "ael.y"
+#line 264 "ael.y"
{(yyval.pval)=(yyvsp[(1) - (1)].pval);;}
break;
case 36:
-#line 263 "ael.y"
+#line 265 "ael.y"
{(yyval.pval)=(yyvsp[(1) - (1)].pval);;}
break;
case 37:
-#line 264 "ael.y"
+#line 266 "ael.y"
{(yyval.pval)=(yyvsp[(1) - (1)].pval);;}
break;
case 38:
-#line 265 "ael.y"
+#line 267 "ael.y"
{(yyval.pval)=(yyvsp[(1) - (1)].pval);;}
break;
case 39:
-#line 266 "ael.y"
+#line 268 "ael.y"
{(yyval.pval)=(yyvsp[(1) - (1)].pval);;}
break;
case 40:
-#line 267 "ael.y"
+#line 269 "ael.y"
{(yyval.pval)=(yyvsp[(1) - (1)].pval);;}
break;
case 41:
-#line 268 "ael.y"
+#line 270 "ael.y"
{free((yyvsp[(1) - (2)].str)); (yyval.pval)=0;;}
break;
case 42:
-#line 269 "ael.y"
+#line 271 "ael.y"
{(yyval.pval)=0;/* allow older docs to be read */;}
break;
case 43:
-#line 272 "ael.y"
+#line 274 "ael.y"
{
(yyval.pval) = npval2(PV_IGNOREPAT, &(yylsp[(1) - (4)]), &(yylsp[(4) - (4)]));
(yyval.pval)->u1.str = (yyvsp[(3) - (4)].str);;}
break;
case 44:
-#line 277 "ael.y"
+#line 279 "ael.y"
{
(yyval.pval) = npval2(PV_EXTENSION, &(yylsp[(1) - (3)]), &(yylsp[(3) - (3)]));
(yyval.pval)->u1.str = (yyvsp[(1) - (3)].str);
break;
case 45:
-#line 281 "ael.y"
+#line 283 "ael.y"
{
(yyval.pval) = npval2(PV_EXTENSION, &(yylsp[(1) - (5)]), &(yylsp[(3) - (5)]));
(yyval.pval)->u1.str = malloc(strlen((yyvsp[(1) - (5)].str))+strlen((yyvsp[(3) - (5)].str))+2);
break;
case 46:
-#line 289 "ael.y"
+#line 291 "ael.y"
{
(yyval.pval) = npval2(PV_EXTENSION, &(yylsp[(1) - (4)]), &(yylsp[(4) - (4)]));
(yyval.pval)->u1.str = (yyvsp[(2) - (4)].str);
break;
case 47:
-#line 294 "ael.y"
+#line 296 "ael.y"
{
(yyval.pval) = npval2(PV_EXTENSION, &(yylsp[(1) - (7)]), &(yylsp[(7) - (7)]));
(yyval.pval)->u1.str = (yyvsp[(5) - (7)].str);
break;
case 48:
-#line 299 "ael.y"
+#line 301 "ael.y"
{
(yyval.pval) = npval2(PV_EXTENSION, &(yylsp[(1) - (8)]), &(yylsp[(8) - (8)]));
(yyval.pval)->u1.str = (yyvsp[(6) - (8)].str);
break;
case 49:
-#line 308 "ael.y"
+#line 310 "ael.y"
{ (yyval.pval) = NULL; ;}
break;
case 50:
-#line 309 "ael.y"
+#line 311 "ael.y"
{ (yyval.pval) = linku1((yyvsp[(1) - (2)].pval), (yyvsp[(2) - (2)].pval)); ;}
break;
case 51:
-#line 310 "ael.y"
+#line 312 "ael.y"
{(yyval.pval)=(yyvsp[(2) - (2)].pval);;}
break;
case 52:
-#line 316 "ael.y"
+#line 318 "ael.y"
{
asprintf(&(yyval.str), "%s:%s:%s", (yyvsp[(1) - (5)].str), (yyvsp[(3) - (5)].str), (yyvsp[(5) - (5)].str));
free((yyvsp[(1) - (5)].str));
break;
case 53:
-#line 321 "ael.y"
+#line 323 "ael.y"
{ (yyval.str) = (yyvsp[(1) - (1)].str); ;}
break;
case 54:
-#line 325 "ael.y"
+#line 327 "ael.y"
{
(yyval.pval) = nword((yyvsp[(1) - (7)].str), &(yylsp[(1) - (7)]));
(yyval.pval)->next = nword((yyvsp[(3) - (7)].str), &(yylsp[(3) - (7)]));
break;
case 55:
-#line 333 "ael.y"
+#line 335 "ael.y"
{ reset_parencount(parseio->scanner); ;}
break;
case 56:
-#line 333 "ael.y"
+#line 335 "ael.y"
{ (yyval.str) = (yyvsp[(3) - (4)].str); ;}
break;
case 57:
-#line 337 "ael.y"
+#line 339 "ael.y"
{
(yyval.pval)= npval2(PV_IF, &(yylsp[(1) - (2)]), &(yylsp[(2) - (2)]));
(yyval.pval)->u1.str = (yyvsp[(2) - (2)].str); ;}
break;
case 58:
-#line 340 "ael.y"
+#line 342 "ael.y"
{
(yyval.pval) = npval2(PV_RANDOM, &(yylsp[(1) - (2)]), &(yylsp[(2) - (2)]));
(yyval.pval)->u1.str=(yyvsp[(2) - (2)].str);;}
break;
case 59:
-#line 343 "ael.y"
+#line 345 "ael.y"
{
(yyval.pval) = npval2(PV_IFTIME, &(yylsp[(1) - (4)]), &(yylsp[(4) - (4)]));
(yyval.pval)->u1.list = (yyvsp[(3) - (4)].pval);
break;
case 60:
-#line 354 "ael.y"
+#line 356 "ael.y"
{ (yyval.str) = (yyvsp[(1) - (1)].str);;}
break;
case 61:
-#line 355 "ael.y"
+#line 357 "ael.y"
{
asprintf(&((yyval.str)), "%s%s", (yyvsp[(1) - (2)].str), (yyvsp[(2) - (2)].str));
free((yyvsp[(1) - (2)].str));
break;
case 62:
-#line 362 "ael.y"
+#line 364 "ael.y"
{ (yyval.str) = (yyvsp[(1) - (1)].str); ;}
break;
case 63:
-#line 363 "ael.y"
+#line 365 "ael.y"
{
asprintf(&((yyval.str)), "%s %s", (yyvsp[(1) - (2)].str), (yyvsp[(2) - (2)].str));
free((yyvsp[(1) - (2)].str));
break;
case 64:
-#line 367 "ael.y"
+#line 369 "ael.y"
{
asprintf(&((yyval.str)), "%s:%s", (yyvsp[(1) - (3)].str), (yyvsp[(3) - (3)].str));
free((yyvsp[(1) - (3)].str));
break;
case 65:
-#line 371 "ael.y"
+#line 373 "ael.y"
{ /* there are often '&' in hints */
asprintf(&((yyval.str)), "%s&%s", (yyvsp[(1) - (3)].str), (yyvsp[(3) - (3)].str));
free((yyvsp[(1) - (3)].str));
break;
case 66:
-#line 377 "ael.y"
+#line 379 "ael.y"
{ (yyval.str) = (yyvsp[(1) - (1)].str);;}
break;
case 67:
-#line 378 "ael.y"
+#line 380 "ael.y"
{
asprintf(&((yyval.str)), "%s%s", (yyvsp[(1) - (2)].str), (yyvsp[(2) - (2)].str));
free((yyvsp[(1) - (2)].str));
break;
case 68:
-#line 383 "ael.y"
+#line 385 "ael.y"
{
asprintf(&((yyval.str)), "%s%s%s", (yyvsp[(1) - (3)].str), (yyvsp[(2) - (3)].str), (yyvsp[(3) - (3)].str));
free((yyvsp[(1) - (3)].str));
break;
case 69:
-#line 391 "ael.y"
+#line 393 "ael.y"
{ (yyval.str) = (yyvsp[(1) - (1)].str);;}
break;
case 70:
-#line 392 "ael.y"
+#line 394 "ael.y"
{
asprintf(&((yyval.str)), "%s%s", (yyvsp[(1) - (2)].str), (yyvsp[(2) - (2)].str));
free((yyvsp[(1) - (2)].str));
break;
case 71:
-#line 396 "ael.y"
+#line 398 "ael.y"
{
asprintf(&((yyval.str)), "%s:%s", (yyvsp[(1) - (3)].str), (yyvsp[(3) - (3)].str));
free((yyvsp[(1) - (3)].str));
break;
case 72:
-#line 402 "ael.y"
+#line 404 "ael.y"
{
(yyval.pval) = npval2(PV_SWITCH, &(yylsp[(1) - (5)]), &(yylsp[(5) - (5)]));
(yyval.pval)->u1.str = (yyvsp[(2) - (5)].str);
break;
case 73:
-#line 411 "ael.y"
+#line 413 "ael.y"
{
(yyval.pval) = npval2(PV_STATEMENTBLOCK, &(yylsp[(1) - (3)]), &(yylsp[(3) - (3)]));
(yyval.pval)->u1.list = (yyvsp[(2) - (3)].pval); set_dads((yyval.pval),(yyvsp[(2) - (3)].pval));;}
break;
case 74:
-#line 414 "ael.y"
+#line 416 "ael.y"
{ (yyval.pval) = (yyvsp[(1) - (1)].pval); ;}
break;
case 75:
-#line 415 "ael.y"
+#line 417 "ael.y"
{ (yyval.pval) = (yyvsp[(1) - (1)].pval); ;}
break;
case 76:
-#line 416 "ael.y"
+#line 418 "ael.y"
{
(yyval.pval) = npval2(PV_GOTO, &(yylsp[(1) - (3)]), &(yylsp[(3) - (3)]));
(yyval.pval)->u1.list = (yyvsp[(2) - (3)].pval);;}
break;
case 77:
-#line 419 "ael.y"
+#line 421 "ael.y"
{
(yyval.pval) = npval2(PV_GOTO, &(yylsp[(1) - (3)]), &(yylsp[(3) - (3)]));
(yyval.pval)->u1.list = (yyvsp[(2) - (3)].pval);;}
break;
case 78:
-#line 422 "ael.y"
+#line 424 "ael.y"
{
(yyval.pval) = npval2(PV_LABEL, &(yylsp[(1) - (2)]), &(yylsp[(2) - (2)]));
(yyval.pval)->u1.str = (yyvsp[(1) - (2)].str); ;}
break;
case 79:
-#line 425 "ael.y"
+#line 427 "ael.y"
{reset_semicount(parseio->scanner);;}
break;
case 80:
-#line 426 "ael.y"
+#line 428 "ael.y"
{reset_semicount(parseio->scanner);;}
break;
case 81:
-#line 427 "ael.y"
+#line 429 "ael.y"
{reset_parencount(parseio->scanner);;}
break;
case 82:
-#line 427 "ael.y"
+#line 429 "ael.y"
{ /* XXX word_list maybe ? */
(yyval.pval) = npval2(PV_FOR, &(yylsp[(1) - (12)]), &(yylsp[(12) - (12)]));
(yyval.pval)->u1.for_init = (yyvsp[(4) - (12)].str);
break;
case 83:
-#line 433 "ael.y"
+#line 435 "ael.y"
{
(yyval.pval) = npval2(PV_WHILE, &(yylsp[(1) - (3)]), &(yylsp[(3) - (3)]));
(yyval.pval)->u1.str = (yyvsp[(2) - (3)].str);
break;
case 84:
-#line 437 "ael.y"
+#line 439 "ael.y"
{ (yyval.pval) = (yyvsp[(1) - (1)].pval); ;}
break;
case 85:
-#line 438 "ael.y"
+#line 440 "ael.y"
{ (yyval.pval) = update_last((yyvsp[(2) - (3)].pval), &(yylsp[(2) - (3)])); ;}
break;
case 86:
-#line 439 "ael.y"
+#line 441 "ael.y"
{ (yyval.pval) = update_last((yyvsp[(1) - (2)].pval), &(yylsp[(2) - (2)])); ;}
break;
case 87:
-#line 440 "ael.y"
+#line 442 "ael.y"
{
(yyval.pval)= npval2(PV_APPLICATION_CALL, &(yylsp[(1) - (2)]), &(yylsp[(2) - (2)]));
(yyval.pval)->u1.str = (yyvsp[(1) - (2)].str);;}
break;
case 88:
-#line 443 "ael.y"
+#line 445 "ael.y"
{reset_semicount(parseio->scanner);;}
break;
case 89:
-#line 443 "ael.y"
+#line 445 "ael.y"
{
char *bufx;
int tot=0;
break;
case 90:
-#line 476 "ael.y"
+#line 478 "ael.y"
{ (yyval.pval) = npval2(PV_BREAK, &(yylsp[(1) - (2)]), &(yylsp[(2) - (2)])); ;}
break;
case 91:
-#line 477 "ael.y"
+#line 479 "ael.y"
{ (yyval.pval) = npval2(PV_RETURN, &(yylsp[(1) - (2)]), &(yylsp[(2) - (2)])); ;}
break;
case 92:
-#line 478 "ael.y"
+#line 480 "ael.y"
{ (yyval.pval) = npval2(PV_CONTINUE, &(yylsp[(1) - (2)]), &(yylsp[(2) - (2)])); ;}
break;
case 93:
-#line 479 "ael.y"
+#line 481 "ael.y"
{
(yyval.pval) = update_last((yyvsp[(1) - (3)].pval), &(yylsp[(2) - (3)]));
(yyval.pval)->u2.statements = (yyvsp[(2) - (3)].pval); set_dads((yyval.pval),(yyvsp[(2) - (3)].pval));
break;
case 94:
-#line 483 "ael.y"
+#line 485 "ael.y"
{ (yyval.pval)=0; ;}
break;
case 95:
-#line 486 "ael.y"
+#line 488 "ael.y"
{ (yyval.pval) = (yyvsp[(2) - (2)].pval); ;}
break;
case 96:
-#line 487 "ael.y"
+#line 489 "ael.y"
{ (yyval.pval) = NULL ; ;}
break;
case 97:
-#line 490 "ael.y"
+#line 492 "ael.y"
{ (yyval.pval) = nword((yyvsp[(1) - (1)].str), &(yylsp[(1) - (1)])); ;}
break;
case 98:
-#line 491 "ael.y"
+#line 493 "ael.y"
{
(yyval.pval) = nword((yyvsp[(1) - (3)].str), &(yylsp[(1) - (3)]));
(yyval.pval)->next = nword((yyvsp[(3) - (3)].str), &(yylsp[(3) - (3)])); ;}
break;
case 99:
-#line 494 "ael.y"
+#line 496 "ael.y"
{
(yyval.pval) = nword((yyvsp[(1) - (3)].str), &(yylsp[(1) - (3)]));
(yyval.pval)->next = nword((yyvsp[(3) - (3)].str), &(yylsp[(3) - (3)])); ;}
break;
case 100:
-#line 497 "ael.y"
+#line 499 "ael.y"
{
(yyval.pval) = nword((yyvsp[(1) - (5)].str), &(yylsp[(1) - (5)]));
(yyval.pval)->next = nword((yyvsp[(3) - (5)].str), &(yylsp[(3) - (5)]));
break;
case 101:
-#line 501 "ael.y"
+#line 503 "ael.y"
{
(yyval.pval) = nword((yyvsp[(1) - (5)].str), &(yylsp[(1) - (5)]));
(yyval.pval)->next = nword((yyvsp[(3) - (5)].str), &(yylsp[(3) - (5)]));
break;
case 102:
-#line 505 "ael.y"
+#line 507 "ael.y"
{
(yyval.pval) = nword(strdup("default"), &(yylsp[(1) - (5)]));
(yyval.pval)->next = nword((yyvsp[(3) - (5)].str), &(yylsp[(3) - (5)]));
break;
case 103:
-#line 509 "ael.y"
+#line 511 "ael.y"
{
(yyval.pval) = nword(strdup("default"), &(yylsp[(1) - (5)]));
(yyval.pval)->next = nword((yyvsp[(3) - (5)].str), &(yylsp[(3) - (5)]));
break;
case 104:
-#line 515 "ael.y"
+#line 517 "ael.y"
{ (yyval.str) = strdup("1"); ;}
break;
case 105:
-#line 516 "ael.y"
+#line 518 "ael.y"
{ (yyval.str) = (yyvsp[(2) - (2)].str); ;}
break;
case 106:
-#line 520 "ael.y"
+#line 522 "ael.y"
{ /* ext[, pri] default 1 */
(yyval.pval) = nword((yyvsp[(1) - (2)].str), &(yylsp[(1) - (2)]));
(yyval.pval)->next = nword((yyvsp[(2) - (2)].str), &(yylsp[(2) - (2)])); ;}
break;
case 107:
-#line 523 "ael.y"
+#line 525 "ael.y"
{ /* context, ext, pri */
(yyval.pval) = nword((yyvsp[(4) - (4)].str), &(yylsp[(4) - (4)]));
(yyval.pval)->next = nword((yyvsp[(1) - (4)].str), &(yylsp[(1) - (4)]));
break;
case 108:
-#line 529 "ael.y"
+#line 531 "ael.y"
{reset_argcount(parseio->scanner);;}
break;
case 109:
-#line 529 "ael.y"
+#line 531 "ael.y"
{
/* XXX original code had @2 but i think we need @5 */
(yyval.pval) = npval2(PV_MACRO_CALL, &(yylsp[(1) - (5)]), &(yylsp[(5) - (5)]));
break;
case 110:
-#line 534 "ael.y"
+#line 536 "ael.y"
{
(yyval.pval)= npval2(PV_MACRO_CALL, &(yylsp[(1) - (3)]), &(yylsp[(3) - (3)]));
(yyval.pval)->u1.str = (yyvsp[(1) - (3)].str); ;}
break;
case 111:
-#line 542 "ael.y"
+#line 544 "ael.y"
{reset_argcount(parseio->scanner);;}
break;
case 112:
-#line 542 "ael.y"
+#line 544 "ael.y"
{
if (strcasecmp((yyvsp[(1) - (3)].str),"goto") == 0) {
(yyval.pval) = npval2(PV_GOTO, &(yylsp[(1) - (3)]), &(yylsp[(2) - (3)]));
break;
case 113:
-#line 553 "ael.y"
+#line 555 "ael.y"
{
(yyval.pval) = update_last((yyvsp[(1) - (3)].pval), &(yylsp[(3) - (3)]));
if( (yyval.pval)->type == PV_GOTO )
break;
case 114:
-#line 560 "ael.y"
+#line 562 "ael.y"
{ (yyval.pval) = update_last((yyvsp[(1) - (2)].pval), &(yylsp[(2) - (2)])); ;}
break;
case 115:
-#line 563 "ael.y"
+#line 565 "ael.y"
{ (yyval.str) = (yyvsp[(1) - (1)].str) ;}
break;
case 116:
-#line 564 "ael.y"
+#line 566 "ael.y"
{ (yyval.str) = strdup(""); ;}
break;
case 117:
-#line 567 "ael.y"
+#line 569 "ael.y"
{ (yyval.pval) = nword((yyvsp[(1) - (1)].str), &(yylsp[(1) - (1)])); ;}
break;
case 118:
-#line 568 "ael.y"
+#line 570 "ael.y"
{
(yyval.pval)= npval(PV_WORD,0/*@1.first_line*/,0/*@1.last_line*/,0/* @1.first_column*/, 0/*@1.last_column*/);
(yyval.pval)->u1.str = strdup(""); ;}
break;
case 119:
-#line 571 "ael.y"
+#line 573 "ael.y"
{ (yyval.pval) = linku1((yyvsp[(1) - (3)].pval), nword((yyvsp[(3) - (3)].str), &(yylsp[(3) - (3)]))); ;}
break;
case 120:
-#line 574 "ael.y"
+#line 576 "ael.y"
{ (yyval.pval) = NULL; ;}
break;
case 121:
-#line 575 "ael.y"
+#line 577 "ael.y"
{ (yyval.pval) = linku1((yyvsp[(1) - (2)].pval), (yyvsp[(2) - (2)].pval)); ;}
break;
case 122:
-#line 578 "ael.y"
+#line 580 "ael.y"
{
(yyval.pval) = npval2(PV_CASE, &(yylsp[(1) - (4)]), &(yylsp[(3) - (4)])); /* XXX 3 or 4 ? */
(yyval.pval)->u1.str = (yyvsp[(2) - (4)].str);
break;
case 123:
-#line 582 "ael.y"
+#line 584 "ael.y"
{
(yyval.pval) = npval2(PV_DEFAULT, &(yylsp[(1) - (3)]), &(yylsp[(3) - (3)]));
(yyval.pval)->u1.str = NULL;
break;
case 124:
-#line 586 "ael.y"
+#line 588 "ael.y"
{
(yyval.pval) = npval2(PV_PATTERN, &(yylsp[(1) - (4)]), &(yylsp[(4) - (4)])); /* XXX@3 or @4 ? */
(yyval.pval)->u1.str = (yyvsp[(2) - (4)].str);
break;
case 125:
-#line 592 "ael.y"
+#line 594 "ael.y"
{ (yyval.pval) = NULL; ;}
break;
case 126:
-#line 593 "ael.y"
+#line 595 "ael.y"
{ (yyval.pval) = linku1((yyvsp[(1) - (2)].pval), (yyvsp[(2) - (2)].pval)); ;}
break;
case 127:
-#line 596 "ael.y"
+#line 598 "ael.y"
{(yyval.pval)=(yyvsp[(1) - (1)].pval);;}
break;
case 128:
-#line 597 "ael.y"
+#line 599 "ael.y"
{ (yyval.pval)=(yyvsp[(1) - (1)].pval);;}
break;
case 129:
-#line 598 "ael.y"
+#line 600 "ael.y"
{
(yyval.pval) = npval2(PV_CATCH, &(yylsp[(1) - (5)]), &(yylsp[(5) - (5)]));
(yyval.pval)->u1.str = (yyvsp[(2) - (5)].str);
break;
case 130:
-#line 604 "ael.y"
+#line 606 "ael.y"
{
(yyval.pval) = npval2(PV_SWITCHES, &(yylsp[(1) - (4)]), &(yylsp[(2) - (4)]));
(yyval.pval)->u1.list = (yyvsp[(3) - (4)].pval); set_dads((yyval.pval),(yyvsp[(3) - (4)].pval));;}
break;
case 131:
-#line 609 "ael.y"
+#line 611 "ael.y"
{
(yyval.pval) = npval2(PV_ESWITCHES, &(yylsp[(1) - (4)]), &(yylsp[(2) - (4)]));
(yyval.pval)->u1.list = (yyvsp[(3) - (4)].pval); set_dads((yyval.pval),(yyvsp[(3) - (4)].pval));;}
break;
case 132:
-#line 614 "ael.y"
+#line 616 "ael.y"
{ (yyval.pval) = NULL; ;}
break;
case 133:
-#line 615 "ael.y"
+#line 617 "ael.y"
{ (yyval.pval) = linku1(nword((yyvsp[(1) - (3)].str), &(yylsp[(1) - (3)])), (yyvsp[(3) - (3)].pval)); ;}
break;
case 134:
-#line 616 "ael.y"
+#line 618 "ael.y"
{ char *x; asprintf(&x,"%s@%s", (yyvsp[(1) - (5)].str),(yyvsp[(3) - (5)].str)); free((yyvsp[(1) - (5)].str)); free((yyvsp[(3) - (5)].str));
(yyval.pval) = linku1(nword(x, &(yylsp[(1) - (5)])), (yyvsp[(5) - (5)].pval));;}
break;
case 135:
-#line 618 "ael.y"
+#line 620 "ael.y"
{(yyval.pval)=(yyvsp[(2) - (2)].pval);;}
break;
case 136:
-#line 621 "ael.y"
+#line 623 "ael.y"
{ (yyval.pval) = nword((yyvsp[(1) - (1)].str), &(yylsp[(1) - (1)])); ;}
break;
case 137:
-#line 622 "ael.y"
+#line 624 "ael.y"
{
(yyval.pval) = nword((yyvsp[(1) - (3)].str), &(yylsp[(1) - (3)]));
(yyval.pval)->u2.arglist = (yyvsp[(3) - (3)].pval);
break;
case 138:
-#line 629 "ael.y"
+#line 631 "ael.y"
{ (yyval.pval) = (yyvsp[(1) - (2)].pval); ;}
break;
case 139:
-#line 630 "ael.y"
+#line 632 "ael.y"
{ (yyval.pval) = linku1((yyvsp[(1) - (3)].pval), (yyvsp[(2) - (3)].pval)); ;}
break;
case 140:
-#line 631 "ael.y"
+#line 633 "ael.y"
{(yyval.pval)=(yyvsp[(1) - (2)].pval);;}
break;
case 141:
-#line 634 "ael.y"
+#line 636 "ael.y"
{
(yyval.pval) = npval2(PV_INCLUDES, &(yylsp[(1) - (4)]), &(yylsp[(4) - (4)]));
(yyval.pval)->u1.list = (yyvsp[(3) - (4)].pval);set_dads((yyval.pval),(yyvsp[(3) - (4)].pval));;}
break;
case 142:
-#line 637 "ael.y"
+#line 639 "ael.y"
{
(yyval.pval) = npval2(PV_INCLUDES, &(yylsp[(1) - (3)]), &(yylsp[(3) - (3)]));;}
break;
/* Line 1270 of yacc.c. */
-#line 3012 "ael.tab.c"
+#line 3014 "ael.tab.c"
default: break;
}
YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
}
-#line 642 "ael.y"
+#line 644 "ael.y"
static char *token_equivs1[] =
* for operations outside of asterisk. A huge, awful hack.
*
*/
+#undef DEBUG_THREADS
#include "asterisk/compat.h"
#include "asterisk/paths.h" /* we use AST_CONFIG_DIR */
#include "asterisk/inline_api.h"
#include "asterisk/endian.h"
#include "asterisk/ast_expr.h"
-#include "asterisk/ael_structs.h"
-#include "asterisk/pval.h"
/* logger.h */
+
#define EVENTLOG "event_log"
#define QUEUELOG "queue_log"
#define __LOG_DTMF 6
#define LOG_DTMF __LOG_DTMF, _A_
-/* from utils.h */
-
-static unsigned int __unsigned_int_flags_dummy;
+/* lock.h */
-struct ast_flags { /* stolen from utils.h */
- unsigned int flags;
-};
-#define ast_test_flag(p,flag) ({ \
- typeof ((p)->flags) __p = (p)->flags; \
- typeof (__unsigned_int_flags_dummy) __x = 0; \
- (void) (&__p == &__x); \
- ((p)->flags & (flag)); \
- })
+#ifndef HAVE_MTX_PROFILE
+#define __MTX_PROF(a) return pthread_mutex_lock((a))
+#else
+#define __MTX_PROF(a) do { \
+ int i; \
+ /* profile only non-blocking events */ \
+ ast_mark(mtx_prof, 1); \
+ i = pthread_mutex_trylock((a)); \
+ ast_mark(mtx_prof, 0); \
+ if (!i) \
+ return i; \
+ else \
+ return pthread_mutex_lock((a)); \
+ } while (0)
+#endif /* HAVE_MTX_PROFILE */
-#define ast_set2_flag(p,value,flag) do { \
- typeof ((p)->flags) __p = (p)->flags; \
- typeof (__unsigned_int_flags_dummy) __x = 0; \
- (void) (&__p == &__x); \
- if (value) \
- (p)->flags |= (flag); \
- else \
- (p)->flags &= ~(flag); \
- } while (0)
+#define AST_PTHREADT_NULL (pthread_t) -1
+#define AST_PTHREADT_STOP (pthread_t) -2
+#if defined(SOLARIS) || defined(BSD)
+#define AST_MUTEX_INIT_W_CONSTRUCTORS
+#endif /* SOLARIS || BSD */
-#ifdef __AST_DEBUG_MALLOC
-static void ast_free(void *ptr) attribute_unused;
-static void ast_free(void *ptr)
-{
- free(ptr);
-}
+/* Asterisk REQUIRES recursive (not error checking) mutexes
+ and will not run without them. */
+#if defined(PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP) && defined(PTHREAD_MUTEX_RECURSIVE_NP)
+#define PTHREAD_MUTEX_INIT_VALUE PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
+#define AST_MUTEX_KIND PTHREAD_MUTEX_RECURSIVE_NP
#else
-#define ast_free free
-#endif
-
-#ifndef __AST_DEBUG_MALLOC
-
-#define MALLOC_FAILURE_MSG \
- ast_log(LOG_ERROR, "Memory Allocation Failure in function %s at line %d of %s\n", func, lineno, file);
-/*!
- * \brief A wrapper for malloc()
- *
- * ast_malloc() is a wrapper for malloc() that will generate an Asterisk log
- * message in the case that the allocation fails.
- *
- * The argument and return value are the same as malloc()
- */
-#define ast_malloc(len) \
- _ast_malloc((len), __FILE__, __LINE__, __PRETTY_FUNCTION__)
+#define PTHREAD_MUTEX_INIT_VALUE PTHREAD_MUTEX_INITIALIZER
+#define AST_MUTEX_KIND PTHREAD_MUTEX_RECURSIVE
+#endif /* PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP */
-AST_INLINE_API(
-void * attribute_malloc _ast_malloc(size_t len, const char *file, int lineno, const char *func),
-{
- void *p;
+#ifdef DEBUG_THREADS
- if (!(p = malloc(len)))
- MALLOC_FAILURE_MSG;
+#define __ast_mutex_logger(...) do { if (canlog) ast_log(LOG_ERROR, __VA_ARGS__); else fprintf(stderr, __VA_ARGS__); } while (0)
- return p;
-}
-)
+#ifdef THREAD_CRASH
+#define DO_THREAD_CRASH do { *((int *)(0)) = 1; } while(0)
+#else
+#define DO_THREAD_CRASH do { } while (0)
+#endif
-/*!
- * \brief A wrapper for calloc()
- *
- * ast_calloc() is a wrapper for calloc() that will generate an Asterisk log
- * message in the case that the allocation fails.
- *
- * The arguments and return value are the same as calloc()
- */
-#define ast_calloc(num, len) \
- _ast_calloc((num), (len), __FILE__, __LINE__, __PRETTY_FUNCTION__)
+#define AST_MUTEX_INIT_VALUE { PTHREAD_MUTEX_INIT_VALUE, { NULL }, { 0 }, 0, { NULL }, { 0 } }
-AST_INLINE_API(
-void * attribute_malloc _ast_calloc(size_t num, size_t len, const char *file, int lineno, const char *func),
-{
- void *p;
+#define AST_MAX_REENTRANCY 10
- if (!(p = calloc(num, len)))
- MALLOC_FAILURE_MSG;
+struct ast_mutex_info {
+ pthread_mutex_t mutex;
+ /*! Track which thread holds this lock */
+ unsigned int track:1;
+ const char *file[AST_MAX_REENTRANCY];
+ int lineno[AST_MAX_REENTRANCY];
+ int reentrancy;
+ const char *func[AST_MAX_REENTRANCY];
+ pthread_t thread[AST_MAX_REENTRANCY];
+};
- return p;
-}
-)
+typedef struct ast_mutex_info ast_mutex_t;
-/*!
- * \brief A wrapper for calloc() for use in cache pools
- *
- * ast_calloc_cache() is a wrapper for calloc() that will generate an Asterisk log
- * message in the case that the allocation fails. When memory debugging is in use,
- * the memory allocated by this function will be marked as 'cache' so it can be
- * distinguished from normal memory allocations.
- *
- * The arguments and return value are the same as calloc()
- */
-#define ast_calloc_cache(num, len) \
- _ast_calloc((num), (len), __FILE__, __LINE__, __PRETTY_FUNCTION__)
+typedef pthread_cond_t ast_cond_t;
-/*!
- * \brief A wrapper for realloc()
- *
- * ast_realloc() is a wrapper for realloc() that will generate an Asterisk log
- * message in the case that the allocation fails.
- *
- * The arguments and return value are the same as realloc()
- */
-#define ast_realloc(p, len) \
- _ast_realloc((p), (len), __FILE__, __LINE__, __PRETTY_FUNCTION__)
+static pthread_mutex_t empty_mutex;
-AST_INLINE_API(
-void * attribute_malloc _ast_realloc(void *p, size_t len, const char *file, int lineno, const char *func),
+static void __attribute__((constructor)) init_empty_mutex(void)
{
- void *newp;
-
- if (!(newp = realloc(p, len)))
- MALLOC_FAILURE_MSG;
-
- return newp;
+ memset(&empty_mutex, 0, sizeof(empty_mutex));
}
-)
-
-/*!
- * \brief A wrapper for strdup()
- *
- * ast_strdup() is a wrapper for strdup() that will generate an Asterisk log
- * message in the case that the allocation fails.
- *
- * ast_strdup(), unlike strdup(), can safely accept a NULL argument. If a NULL
- * argument is provided, ast_strdup will return NULL without generating any
- * kind of error log message.
- *
- * The argument and return value are the same as strdup()
- */
-#define ast_strdup(str) \
- _ast_strdup((str), __FILE__, __LINE__, __PRETTY_FUNCTION__)
-AST_INLINE_API(
-char * attribute_malloc _ast_strdup(const char *str, const char *file, int lineno, const char *func),
+static inline int __ast_pthread_mutex_init_attr(const char *filename, int lineno, const char *func,
+ const char *mutex_name, ast_mutex_t *t,
+ pthread_mutexattr_t *attr)
{
- char *newstr = NULL;
+#ifdef AST_MUTEX_INIT_W_CONSTRUCTORS
+ int canlog = strcmp(filename, "logger.c");
- if (str) {
- if (!(newstr = strdup(str)))
- MALLOC_FAILURE_MSG;
+ if ((t->mutex) != ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) {
+ if ((t->mutex) != (empty_mutex)) {
+ __ast_mutex_logger("%s line %d (%s): Error: mutex '%s' is already initialized.\n",
+ filename, lineno, func, mutex_name);
+ __ast_mutex_logger("%s line %d (%s): Error: previously initialization of mutex '%s'.\n",
+ t->file[0], t->lineno[0], t->func[0], mutex_name);
+ DO_THREAD_CRASH;
+ return 0;
+ }
}
+#endif
- return newstr;
-}
-)
+ t->file[0] = filename;
+ t->lineno[0] = lineno;
+ t->func[0] = func;
+ t->thread[0] = 0;
+ t->reentrancy = 0;
-/*!
- * \brief A wrapper for strndup()
- *
- * ast_strndup() is a wrapper for strndup() that will generate an Asterisk log
- * message in the case that the allocation fails.
- *
- * ast_strndup(), unlike strndup(), can safely accept a NULL argument for the
- * string to duplicate. If a NULL argument is provided, ast_strdup will return
- * NULL without generating any kind of error log message.
- *
- * The arguments and return value are the same as strndup()
- */
-#define ast_strndup(str, len) \
- _ast_strndup((str), (len), __FILE__, __LINE__, __PRETTY_FUNCTION__)
+ return pthread_mutex_init(&t->mutex, attr);
+}
-AST_INLINE_API(
-char * attribute_malloc _ast_strndup(const char *str, size_t len, const char *file, int lineno, const char *func),
+static inline int __ast_pthread_mutex_init(const char *filename, int lineno, const char *func,
+ const char *mutex_name, ast_mutex_t *t)
{
- char *newstr = NULL;
+ static pthread_mutexattr_t attr;
- if (str) {
- if (!(newstr = strndup(str, len)))
- MALLOC_FAILURE_MSG;
- }
+ pthread_mutexattr_init(&attr);
+ pthread_mutexattr_settype(&attr, AST_MUTEX_KIND);
- return newstr;
+ return __ast_pthread_mutex_init_attr(filename, lineno, func, mutex_name, t, &attr);
}
-)
-
-/*!
- * \brief A wrapper for asprintf()
- *
- * ast_asprintf() is a wrapper for asprintf() that will generate an Asterisk log
- * message in the case that the allocation fails.
- *
- * The arguments and return value are the same as asprintf()
- */
-#define ast_asprintf(ret, fmt, ...) \
- _ast_asprintf((ret), __FILE__, __LINE__, __PRETTY_FUNCTION__, fmt, __VA_ARGS__)
+#define ast_mutex_init(pmutex) __ast_pthread_mutex_init(__FILE__, __LINE__, __PRETTY_FUNCTION__, #pmutex, pmutex)
-AST_INLINE_API(
-int _ast_asprintf(char **ret, const char *file, int lineno, const char *func, const char *fmt, ...),
+static inline int __ast_pthread_mutex_destroy(const char *filename, int lineno, const char *func,
+ const char *mutex_name, ast_mutex_t *t)
{
int res;
- va_list ap;
+ int canlog = strcmp(filename, "logger.c");
- va_start(ap, fmt);
- if ((res = vasprintf(ret, fmt, ap)) == -1)
- MALLOC_FAILURE_MSG;
- va_end(ap);
+#ifdef AST_MUTEX_INIT_W_CONSTRUCTORS
+ if ((t->mutex) == ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) {
+ __ast_mutex_logger("%s line %d (%s): Error: mutex '%s' is uninitialized.\n",
+ filename, lineno, func, mutex_name);
+ }
+#endif
+
+ res = pthread_mutex_trylock(&t->mutex);
+ switch (res) {
+ case 0:
+ pthread_mutex_unlock(&t->mutex);
+ break;
+ case EINVAL:
+ __ast_mutex_logger("%s line %d (%s): Error: attempt to destroy invalid mutex '%s'.\n",
+ filename, lineno, func, mutex_name);
+ break;
+ case EBUSY:
+ __ast_mutex_logger("%s line %d (%s): Error: attempt to destroy locked mutex '%s'.\n",
+ filename, lineno, func, mutex_name);
+ __ast_mutex_logger("%s line %d (%s): Error: '%s' was locked here.\n",
+ t->file[t->reentrancy-1], t->lineno[t->reentrancy-1], t->func[t->reentrancy-1], mutex_name);
+ break;
+ }
+
+ if ((res = pthread_mutex_destroy(&t->mutex)))
+ __ast_mutex_logger("%s line %d (%s): Error destroying mutex: %s\n",
+ filename, lineno, func, strerror(res));
+#ifndef PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
+ else
+ t->mutex = PTHREAD_MUTEX_INIT_VALUE;
+#endif
+ t->file[0] = filename;
+ t->lineno[0] = lineno;
+ t->func[0] = func;
return res;
}
-)
-
-/*!
- * \brief A wrapper for vasprintf()
- *
- * ast_vasprintf() is a wrapper for vasprintf() that will generate an Asterisk log
- * message in the case that the allocation fails.
- *
- * The arguments and return value are the same as vasprintf()
- */
-#define ast_vasprintf(ret, fmt, ap) \
- _ast_vasprintf((ret), __FILE__, __LINE__, __PRETTY_FUNCTION__, (fmt), (ap))
-AST_INLINE_API(
-int _ast_vasprintf(char **ret, const char *file, int lineno, const char *func, const char *fmt, va_list ap),
+static inline int __ast_pthread_mutex_lock(const char *filename, int lineno, const char *func,
+ const char* mutex_name, ast_mutex_t *t)
{
int res;
+ int canlog = strcmp(filename, "logger.c");
- if ((res = vasprintf(ret, fmt, ap)) == -1)
- MALLOC_FAILURE_MSG;
-
- return res;
-}
-)
+#if defined(AST_MUTEX_INIT_W_CONSTRUCTORS)
+ if ((t->mutex) == ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) {
+ __ast_mutex_logger("%s line %d (%s): Error: mutex '%s' is uninitialized.\n",
+ filename, lineno, func, mutex_name);
+ ast_mutex_init(t);
+ }
+#endif /* AST_MUTEX_INIT_W_CONSTRUCTORS */
+#ifdef DETECT_DEADLOCKS
+ {
+ time_t seconds = time(NULL);
+ time_t current;
+ do {
+#ifdef HAVE_MTX_PROFILE
+ ast_mark(mtx_prof, 1);
+#endif
+ res = pthread_mutex_trylock(&t->mutex);
+#ifdef HAVE_MTX_PROFILE
+ ast_mark(mtx_prof, 0);
+#endif
+ if (res == EBUSY) {
+ current = time(NULL);
+ if ((current - seconds) && (!((current - seconds) % 5))) {
+ __ast_mutex_logger("%s line %d (%s): Deadlock? waited %d sec for mutex '%s'?\n",
+ filename, lineno, func, (int)(current - seconds), mutex_name);
+ __ast_mutex_logger("%s line %d (%s): '%s' was locked here.\n",
+ t->file[t->reentrancy-1], t->lineno[t->reentrancy-1],
+ t->func[t->reentrancy-1], mutex_name);
+ }
+ usleep(200);
+ }
+ } while (res == EBUSY);
+ }
#else
+#ifdef HAVE_MTX_PROFILE
+ ast_mark(mtx_prof, 1);
+ res = pthread_mutex_trylock(&t->mutex);
+ ast_mark(mtx_prof, 0);
+ if (res)
+#endif
+ res = pthread_mutex_lock(&t->mutex);
+#endif /* DETECT_DEADLOCKS */
-/* If astmm is in use, let it handle these. Otherwise, it will report that
- all allocations are coming from this header file */
+ if (!res) {
+ if (t->reentrancy < AST_MAX_REENTRANCY) {
+ t->file[t->reentrancy] = filename;
+ t->lineno[t->reentrancy] = lineno;
+ t->func[t->reentrancy] = func;
+ t->thread[t->reentrancy] = pthread_self();
+ t->reentrancy++;
+ } else {
+ __ast_mutex_logger("%s line %d (%s): '%s' really deep reentrancy!\n",
+ filename, lineno, func, mutex_name);
+ }
+ } else {
+ __ast_mutex_logger("%s line %d (%s): Error obtaining mutex: %s\n",
+ filename, lineno, func, strerror(errno));
+ DO_THREAD_CRASH;
+ }
-#define ast_malloc(a) malloc(a)
-#define ast_calloc(a,b) calloc(a,b)
-#define ast_realloc(a,b) realloc(a,b)
-#define ast_strdup(a) strdup(a)
-#define ast_strndup(a,b) strndup(a,b)
-#define ast_asprintf(a,b,c) asprintf(a,b,c)
-#define ast_vasprintf(a,b,c) vasprintf(a,b,c)
+ return res;
+}
-#endif /* AST_DEBUG_MALLOC */
+static inline int __ast_pthread_mutex_trylock(const char *filename, int lineno, const char *func,
+ const char* mutex_name, ast_mutex_t *t)
+{
+ int res;
+ int canlog = strcmp(filename, "logger.c");
-#if !defined(ast_strdupa) && defined(__GNUC__)
-/*!
- \brief duplicate a string in memory from the stack
- \param s The string to duplicate
+#if defined(AST_MUTEX_INIT_W_CONSTRUCTORS)
+ if ((t->mutex) == ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) {
+ __ast_mutex_logger("%s line %d (%s): Error: mutex '%s' is uninitialized.\n",
+ filename, lineno, func, mutex_name);
+ ast_mutex_init(t);
+ }
+#endif /* AST_MUTEX_INIT_W_CONSTRUCTORS */
- This macro will duplicate the given string. It returns a pointer to the stack
- allocatted memory for the new string.
-*/
-#define ast_strdupa(s) \
- (__extension__ \
- ({ \
- const char *__old = (s); \
- size_t __len = strlen(__old) + 1; \
- char *__new = __builtin_alloca(__len); \
- memcpy (__new, __old, __len); \
- __new; \
- }))
-#endif
+ if (!(res = pthread_mutex_trylock(&t->mutex))) {
+ if (t->reentrancy < AST_MAX_REENTRANCY) {
+ t->file[t->reentrancy] = filename;
+ t->lineno[t->reentrancy] = lineno;
+ t->func[t->reentrancy] = func;
+ t->thread[t->reentrancy] = pthread_self();
+ t->reentrancy++;
+ } else {
+ __ast_mutex_logger("%s line %d (%s): '%s' really deep reentrancy!\n",
+ filename, lineno, func, mutex_name);
+ }
+ } else {
+ __ast_mutex_logger("%s line %d (%s): Warning: '%s' was locked here.\n",
+ t->file[t->reentrancy-1], t->lineno[t->reentrancy-1], t->func[t->reentrancy-1], mutex_name);
+ }
+ return res;
+}
-/* from config.c */
+static inline int __ast_pthread_mutex_unlock(const char *filename, int lineno, const char *func,
+ const char *mutex_name, ast_mutex_t *t)
+{
+ int res;
+ int canlog = strcmp(filename, "logger.c");
-#define MAX_NESTED_COMMENTS 128
-#define COMMENT_START ";--"
-#define COMMENT_END "--;"
-#define COMMENT_META ';'
-#define COMMENT_TAG '-'
+#ifdef AST_MUTEX_INIT_W_CONSTRUCTORS
+ if ((t->mutex) == ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) {
+ __ast_mutex_logger("%s line %d (%s): Error: mutex '%s' is uninitialized.\n",
+ filename, lineno, func, mutex_name);
+ }
+#endif
-static char *extconfig_conf = "extconfig.conf";
+ if (t->reentrancy && (t->thread[t->reentrancy-1] != pthread_self())) {
+ __ast_mutex_logger("%s line %d (%s): attempted unlock mutex '%s' without owning it!\n",
+ filename, lineno, func, mutex_name);
+ __ast_mutex_logger("%s line %d (%s): '%s' was locked here.\n",
+ t->file[t->reentrancy-1], t->lineno[t->reentrancy-1], t->func[t->reentrancy-1], mutex_name);
+ DO_THREAD_CRASH;
+ }
-/*! Growable string buffer */
-static char *comment_buffer; /*!< this will be a comment collector.*/
-static int comment_buffer_size; /*!< the amount of storage so far alloc'd for the comment_buffer */
+ if (--t->reentrancy < 0) {
+ __ast_mutex_logger("%s line %d (%s): mutex '%s' freed more times than we've locked!\n",
+ filename, lineno, func, mutex_name);
+ t->reentrancy = 0;
+ }
-static char *lline_buffer; /*!< A buffer for stuff behind the ; */
-static int lline_buffer_size;
+ if (t->reentrancy < AST_MAX_REENTRANCY) {
+ t->file[t->reentrancy] = NULL;
+ t->lineno[t->reentrancy] = 0;
+ t->func[t->reentrancy] = NULL;
+ t->thread[t->reentrancy] = 0;
+ }
-#define CB_INCR 250
+ if ((res = pthread_mutex_unlock(&t->mutex))) {
+ __ast_mutex_logger("%s line %d (%s): Error releasing mutex: %s\n",
+ filename, lineno, func, strerror(res));
+ DO_THREAD_CRASH;
+ }
-struct ast_comment {
- struct ast_comment *next;
- char cmt[0];
-};
+ return res;
+}
-static void CB_INIT(void)
+static inline int __ast_cond_init(const char *filename, int lineno, const char *func,
+ const char *cond_name, ast_cond_t *cond, pthread_condattr_t *cond_attr)
{
- if (!comment_buffer) {
- comment_buffer = ast_malloc(CB_INCR);
- if (!comment_buffer)
- return;
- comment_buffer[0] = 0;
- comment_buffer_size = CB_INCR;
- lline_buffer = ast_malloc(CB_INCR);
- if (!lline_buffer)
- return;
- lline_buffer[0] = 0;
- lline_buffer_size = CB_INCR;
- } else {
- comment_buffer[0] = 0;
- lline_buffer[0] = 0;
- }
+ return pthread_cond_init(cond, cond_attr);
}
-static void CB_ADD(char *str)
+static inline int __ast_cond_signal(const char *filename, int lineno, const char *func,
+ const char *cond_name, ast_cond_t *cond)
{
- int rem = comment_buffer_size - strlen(comment_buffer) - 1;
- int siz = strlen(str);
- if (rem < siz+1) {
- comment_buffer = ast_realloc(comment_buffer, comment_buffer_size + CB_INCR + siz + 1);
- if (!comment_buffer)
- return;
- comment_buffer_size += CB_INCR+siz+1;
- }
- strcat(comment_buffer,str);
+ return pthread_cond_signal(cond);
}
-static void CB_ADD_LEN(char *str, int len)
+static inline int __ast_cond_broadcast(const char *filename, int lineno, const char *func,
+ const char *cond_name, ast_cond_t *cond)
{
- int cbl = strlen(comment_buffer) + 1;
- int rem = comment_buffer_size - cbl;
- if (rem < len+1) {
- comment_buffer = ast_realloc(comment_buffer, comment_buffer_size + CB_INCR + len + 1);
- if (!comment_buffer)
- return;
- comment_buffer_size += CB_INCR+len+1;
- }
- strncat(comment_buffer,str,len); /* safe */
- comment_buffer[cbl+len-1] = 0;
+ return pthread_cond_broadcast(cond);
}
-static void LLB_ADD(char *str)
+static inline int __ast_cond_destroy(const char *filename, int lineno, const char *func,
+ const char *cond_name, ast_cond_t *cond)
{
- int rem = lline_buffer_size - strlen(lline_buffer) - 1;
- int siz = strlen(str);
- if (rem < siz+1) {
- lline_buffer = ast_realloc(lline_buffer, lline_buffer_size + CB_INCR + siz + 1);
- if (!lline_buffer)
- return;
- lline_buffer_size += CB_INCR + siz + 1;
- }
- strcat(lline_buffer,str);
-}
-
-static void CB_RESET(void )
-{
- comment_buffer[0] = 0;
- lline_buffer[0] = 0;
+ return pthread_cond_destroy(cond);
}
-
-/*! \brief Keep track of how many threads are currently trying to wait*() on
- * a child process */
-static unsigned int safe_system_level = 0;
-static void *safe_system_prev_handler;
-/*! \brief NULL handler so we can collect the child exit status */
-static void null_sig_handler(int signal)
+static inline int __ast_cond_wait(const char *filename, int lineno, const char *func,
+ const char *cond_name, const char *mutex_name,
+ ast_cond_t *cond, ast_mutex_t *t)
{
+ int res;
+ int canlog = strcmp(filename, "logger.c");
-}
+#ifdef AST_MUTEX_INIT_W_CONSTRUCTORS
+ if ((t->mutex) == ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) {
+ __ast_mutex_logger("%s line %d (%s): Error: mutex '%s' is uninitialized.\n",
+ filename, lineno, func, mutex_name);
+ }
+#endif
-void ast_replace_sigchld(void);
+ if (t->reentrancy && (t->thread[t->reentrancy-1] != pthread_self())) {
+ __ast_mutex_logger("%s line %d (%s): attempted unlock mutex '%s' without owning it!\n",
+ filename, lineno, func, mutex_name);
+ __ast_mutex_logger("%s line %d (%s): '%s' was locked here.\n",
+ t->file[t->reentrancy-1], t->lineno[t->reentrancy-1], t->func[t->reentrancy-1], mutex_name);
+ DO_THREAD_CRASH;
+ }
-void ast_replace_sigchld(void)
-{
- unsigned int level;
+ if (--t->reentrancy < 0) {
+ __ast_mutex_logger("%s line %d (%s): mutex '%s' freed more times than we've locked!\n",
+ filename, lineno, func, mutex_name);
+ t->reentrancy = 0;
+ }
- level = safe_system_level++;
+ if (t->reentrancy < AST_MAX_REENTRANCY) {
+ t->file[t->reentrancy] = NULL;
+ t->lineno[t->reentrancy] = 0;
+ t->func[t->reentrancy] = NULL;
+ t->thread[t->reentrancy] = 0;
+ }
- /* only replace the handler if it has not already been done */
- if (level == 0)
- safe_system_prev_handler = signal(SIGCHLD, null_sig_handler);
+ if ((res = pthread_cond_wait(cond, &t->mutex))) {
+ __ast_mutex_logger("%s line %d (%s): Error waiting on condition mutex '%s'\n",
+ filename, lineno, func, strerror(res));
+ DO_THREAD_CRASH;
+ } else {
+ if (t->reentrancy < AST_MAX_REENTRANCY) {
+ t->file[t->reentrancy] = filename;
+ t->lineno[t->reentrancy] = lineno;
+ t->func[t->reentrancy] = func;
+ t->thread[t->reentrancy] = pthread_self();
+ t->reentrancy++;
+ } else {
+ __ast_mutex_logger("%s line %d (%s): '%s' really deep reentrancy!\n",
+ filename, lineno, func, mutex_name);
+ }
+ }
+ return res;
}
-void ast_unreplace_sigchld(void);
-
-void ast_unreplace_sigchld(void)
+static inline int __ast_cond_timedwait(const char *filename, int lineno, const char *func,
+ const char *cond_name, const char *mutex_name, ast_cond_t *cond,
+ ast_mutex_t *t, const struct timespec *abstime)
{
- unsigned int level;
+ int res;
+ int canlog = strcmp(filename, "logger.c");
- level = --safe_system_level;
+#ifdef AST_MUTEX_INIT_W_CONSTRUCTORS
+ if ((t->mutex) == ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) {
+ __ast_mutex_logger("%s line %d (%s): Error: mutex '%s' is uninitialized.\n",
+ filename, lineno, func, mutex_name);
+ }
+#endif
- /* only restore the handler if we are the last one */
- if (level == 0)
- signal(SIGCHLD, safe_system_prev_handler);
+ if (t->reentrancy && (t->thread[t->reentrancy-1] != pthread_self())) {
+ __ast_mutex_logger("%s line %d (%s): attempted unlock mutex '%s' without owning it!\n",
+ filename, lineno, func, mutex_name);
+ __ast_mutex_logger("%s line %d (%s): '%s' was locked here.\n",
+ t->file[t->reentrancy-1], t->lineno[t->reentrancy-1], t->func[t->reentrancy-1], mutex_name);
+ DO_THREAD_CRASH;
+ }
+
+ if (--t->reentrancy < 0) {
+ __ast_mutex_logger("%s line %d (%s): mutex '%s' freed more times than we've locked!\n",
+ filename, lineno, func, mutex_name);
+ t->reentrancy = 0;
+ }
+
+ if (t->reentrancy < AST_MAX_REENTRANCY) {
+ t->file[t->reentrancy] = NULL;
+ t->lineno[t->reentrancy] = 0;
+ t->func[t->reentrancy] = NULL;
+ t->thread[t->reentrancy] = 0;
+ }
+
+ if ((res = pthread_cond_timedwait(cond, &t->mutex, abstime)) && (res != ETIMEDOUT)) {
+ __ast_mutex_logger("%s line %d (%s): Error waiting on condition mutex '%s'\n",
+ filename, lineno, func, strerror(res));
+ DO_THREAD_CRASH;
+ } else {
+ if (t->reentrancy < AST_MAX_REENTRANCY) {
+ t->file[t->reentrancy] = filename;
+ t->lineno[t->reentrancy] = lineno;
+ t->func[t->reentrancy] = func;
+ t->thread[t->reentrancy] = pthread_self();
+ t->reentrancy++;
+ } else {
+ __ast_mutex_logger("%s line %d (%s): '%s' really deep reentrancy!\n",
+ filename, lineno, func, mutex_name);
+ }
+ }
+ return res;
}
-int ast_safe_system(const char *s);
+#define ast_mutex_destroy(a) __ast_pthread_mutex_destroy(__FILE__, __LINE__, __PRETTY_FUNCTION__, #a, a)
+#define ast_mutex_lock(a) __ast_pthread_mutex_lock(__FILE__, __LINE__, __PRETTY_FUNCTION__, #a, a)
+#define ast_mutex_unlock(a) __ast_pthread_mutex_unlock(__FILE__, __LINE__, __PRETTY_FUNCTION__, #a, a)
+#define ast_mutex_trylock(a) __ast_pthread_mutex_trylock(__FILE__, __LINE__, __PRETTY_FUNCTION__, #a, a)
+#define ast_cond_init(cond, attr) __ast_cond_init(__FILE__, __LINE__, __PRETTY_FUNCTION__, #cond, cond, attr)
+#define ast_cond_destroy(cond) __ast_cond_destroy(__FILE__, __LINE__, __PRETTY_FUNCTION__, #cond, cond)
+#define ast_cond_signal(cond) __ast_cond_signal(__FILE__, __LINE__, __PRETTY_FUNCTION__, #cond, cond)
+#define ast_cond_broadcast(cond) __ast_cond_broadcast(__FILE__, __LINE__, __PRETTY_FUNCTION__, #cond, cond)
+#define ast_cond_wait(cond, mutex) __ast_cond_wait(__FILE__, __LINE__, __PRETTY_FUNCTION__, #cond, #mutex, cond, mutex)
+#define ast_cond_timedwait(cond, mutex, time) __ast_cond_timedwait(__FILE__, __LINE__, __PRETTY_FUNCTION__, #cond, #mutex, cond, mutex, time)
-int ast_safe_system(const char *s)
+#else /* !DEBUG_THREADS */
+
+
+typedef pthread_mutex_t ast_mutex_t;
+
+#define AST_MUTEX_INIT_VALUE ((ast_mutex_t) PTHREAD_MUTEX_INIT_VALUE)
+
+static inline int ast_mutex_init(ast_mutex_t *pmutex)
{
- pid_t pid;
-#ifdef HAVE_WORKING_FORK
- int x;
-#endif
- int res;
- struct rusage rusage;
- int status;
+ pthread_mutexattr_t attr;
-#if defined(HAVE_WORKING_FORK) || defined(HAVE_WORKING_VFORK)
- ast_replace_sigchld();
+ pthread_mutexattr_init(&attr);
+ pthread_mutexattr_settype(&attr, AST_MUTEX_KIND);
-#ifdef HAVE_WORKING_FORK
- pid = fork();
-#else
- pid = vfork();
-#endif
+ return pthread_mutex_init(pmutex, &attr);
+}
- if (pid == 0) {
-#ifdef HAVE_WORKING_FORK
- /* Close file descriptors and launch system command */
- for (x = STDERR_FILENO + 1; x < 4096; x++)
- close(x);
-#endif
- execl("/bin/sh", "/bin/sh", "-c", s, (char *) NULL);
- _exit(1);
- } else if (pid > 0) {
- for(;;) {
- res = wait4(pid, &status, 0, &rusage);
- if (res > -1) {
- res = WIFEXITED(status) ? WEXITSTATUS(status) : -1;
- break;
- } else if (errno != EINTR)
- break;
- }
- } else {
- ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(errno));
- res = -1;
- }
+#define ast_pthread_mutex_init(pmutex,a) pthread_mutex_init(pmutex,a)
- ast_unreplace_sigchld();
-#else
- res = -1;
-#endif
+static inline int ast_mutex_unlock(ast_mutex_t *pmutex)
+{
+ return pthread_mutex_unlock(pmutex);
+}
- return res;
+static inline int ast_mutex_destroy(ast_mutex_t *pmutex)
+{
+ return pthread_mutex_destroy(pmutex);
}
-static struct ast_comment *ALLOC_COMMENT(const char *buffer)
-{
- struct ast_comment *x = ast_calloc(1,sizeof(struct ast_comment)+strlen(buffer)+1);
- strcpy(x->cmt, buffer);
- return x;
+static inline int ast_mutex_lock(ast_mutex_t *pmutex)
+{
+ __MTX_PROF(pmutex);
}
-static struct ast_config_map {
- struct ast_config_map *next;
- char *name;
- char *driver;
- char *database;
- char *table;
- char stuff[0];
-} *config_maps = NULL;
+static inline int ast_mutex_trylock(ast_mutex_t *pmutex)
+{
+ return pthread_mutex_trylock(pmutex);
+}
-static struct ast_config_engine *config_engine_list;
+typedef pthread_cond_t ast_cond_t;
-#define MAX_INCLUDE_LEVEL 10
+static inline int ast_cond_init(ast_cond_t *cond, pthread_condattr_t *cond_attr)
+{
+ return pthread_cond_init(cond, cond_attr);
+}
+static inline int ast_cond_signal(ast_cond_t *cond)
+{
+ return pthread_cond_signal(cond);
+}
-struct ast_category {
- char name[80];
- int ignored; /*!< do not let user of the config see this category */
- int include_level;
- char *file; /*!< the file name from whence this declaration was read */
- int lineno;
- struct ast_comment *precomments;
- struct ast_comment *sameline;
- struct ast_variable *root;
- struct ast_variable *last;
- struct ast_category *next;
-};
-
-struct ast_config {
- struct ast_category *root;
- struct ast_category *last;
- struct ast_category *current;
- struct ast_category *last_browse; /*!< used to cache the last category supplied via category_browse */
- int include_level;
- int max_include_level;
- struct ast_config_include *includes; /*!< a list of inclusions, which should describe the entire tree */
-};
-
-struct ast_config_include {
- char *include_location_file; /*!< file name in which the include occurs */
- int include_location_lineno; /*!< lineno where include occurred */
- int exec; /*!< set to non-zero if itsa #exec statement */
- char *exec_file; /*!< if it's an exec, you'll have both the /var/tmp to read, and the original script */
- char *included_file; /*!< file name included */
- int inclusion_count; /*!< if the file is included more than once, a running count thereof -- but, worry not,
- we explode the instances and will include those-- so all entries will be unique */
- int output; /*!< a flag to indicate if the inclusion has been output */
- struct ast_config_include *next; /*!< ptr to next inclusion in the list */
-};
-
-typedef struct ast_config *config_load_func(const char *database, const char *table, const char *configfile, struct ast_config *config, int withcomments, const char *suggested_include_file);
-typedef struct ast_variable *realtime_var_get(const char *database, const char *table, va_list ap);
-typedef struct ast_config *realtime_multi_get(const char *database, const char *table, va_list ap);
-typedef int realtime_update(const char *database, const char *table, const char *keyfield, const char *entity, va_list ap);
-
-/*! \brief Configuration engine structure, used to define realtime drivers */
-struct ast_config_engine {
- char *name;
- config_load_func *load_func;
- realtime_var_get *realtime_func;
- realtime_multi_get *realtime_multi_func;
- realtime_update *update_func;
- struct ast_config_engine *next;
-};
-
-static struct ast_config_engine *config_engine_list;
-
-/* taken from strings.h */
-
-static force_inline int ast_strlen_zero(const char *s)
+static inline int ast_cond_broadcast(ast_cond_t *cond)
{
- return (!s || (*s == '\0'));
+ return pthread_cond_broadcast(cond);
}
-#define S_OR(a, b) (!ast_strlen_zero(a) ? (a) : (b))
-
-AST_INLINE_API(
-void ast_copy_string(char *dst, const char *src, size_t size),
+static inline int ast_cond_destroy(ast_cond_t *cond)
{
- while (*src && size) {
- *dst++ = *src++;
- size--;
- }
- if (__builtin_expect(!size, 0))
- dst--;
- *dst = '\0';
+ return pthread_cond_destroy(cond);
}
-)
-AST_INLINE_API(
-char *ast_skip_blanks(const char *str),
+static inline int ast_cond_wait(ast_cond_t *cond, ast_mutex_t *t)
{
- while (*str && *str < 33)
- str++;
- return (char *)str;
+ return pthread_cond_wait(cond, t);
}
-)
-/*!
- \brief Trims trailing whitespace characters from a string.
- \param ast_trim_blanks function being used
- \param str the input string
- \return a pointer to the modified string
- */
-AST_INLINE_API(
-char *ast_trim_blanks(char *str),
+static inline int ast_cond_timedwait(ast_cond_t *cond, ast_mutex_t *t, const struct timespec *abstime)
{
- char *work = str;
+ return pthread_cond_timedwait(cond, t, abstime);
+}
- if (work) {
- work += strlen(work) - 1;
- /* It's tempting to only want to erase after we exit this loop,
- but since ast_trim_blanks *could* receive a constant string
- (which we presumably wouldn't have to touch), we shouldn't
- actually set anything unless we must, and it's easier just
- to set each position to \0 than to keep track of a variable
- for it */
- while ((work >= str) && *work < 33)
- *(work--) = '\0';
- }
- return str;
+#endif /* !DEBUG_THREADS */
+
+#if defined(AST_MUTEX_INIT_W_CONSTRUCTORS)
+/* If AST_MUTEX_INIT_W_CONSTRUCTORS is defined, use file scope
+ constructors/destructors to create/destroy mutexes. */
+#define __AST_MUTEX_DEFINE(scope, mutex) \
+ scope ast_mutex_t mutex = AST_MUTEX_INIT_VALUE; \
+static void __attribute__ ((constructor)) init_##mutex(void) \
+{ \
+ ast_mutex_init(&mutex); \
+} \
+static void __attribute__ ((destructor)) fini_##mutex(void) \
+{ \
+ ast_mutex_destroy(&mutex); \
}
-)
+#else /* !AST_MUTEX_INIT_W_CONSTRUCTORS */
+/* By default, use static initialization of mutexes. */
+#define __AST_MUTEX_DEFINE(scope, mutex) \
+ scope ast_mutex_t mutex = AST_MUTEX_INIT_VALUE
+#endif /* AST_MUTEX_INIT_W_CONSTRUCTORS */
-/*!
- \brief Strip leading/trailing whitespace from a string.
- \param s The string to be stripped (will be modified).
- \return The stripped string.
+#define pthread_mutex_t use_ast_mutex_t_instead_of_pthread_mutex_t
+#define pthread_mutex_lock use_ast_mutex_lock_instead_of_pthread_mutex_lock
+#define pthread_mutex_unlock use_ast_mutex_unlock_instead_of_pthread_mutex_unlock
+#define pthread_mutex_trylock use_ast_mutex_trylock_instead_of_pthread_mutex_trylock
+#define pthread_mutex_init use_ast_mutex_init_instead_of_pthread_mutex_init
+#define pthread_mutex_destroy use_ast_mutex_destroy_instead_of_pthread_mutex_destroy
+#define pthread_cond_t use_ast_cond_t_instead_of_pthread_cond_t
+#define pthread_cond_init use_ast_cond_init_instead_of_pthread_cond_init
+#define pthread_cond_destroy use_ast_cond_destroy_instead_of_pthread_cond_destroy
+#define pthread_cond_signal use_ast_cond_signal_instead_of_pthread_cond_signal
+#define pthread_cond_broadcast use_ast_cond_broadcast_instead_of_pthread_cond_broadcast
+#define pthread_cond_wait use_ast_cond_wait_instead_of_pthread_cond_wait
+#define pthread_cond_timedwait use_ast_cond_timedwait_instead_of_pthread_cond_timedwait
- This functions strips all leading and trailing whitespace
- characters from the input string, and returns a pointer to
- the resulting string. The string is modified in place.
-*/
-AST_INLINE_API(
-char *ast_strip(char *s),
-{
- s = ast_skip_blanks(s);
- if (s)
- ast_trim_blanks(s);
- return s;
-}
-)
+#define AST_MUTEX_DEFINE_STATIC(mutex) __AST_MUTEX_DEFINE(static, mutex)
+#define AST_MUTEX_INITIALIZER __use_AST_MUTEX_DEFINE_STATIC_rather_than_AST_MUTEX_INITIALIZER__
-/* from config.h */
+#define gethostbyname __gethostbyname__is__not__reentrant__use__ast_gethostbyname__instead__
-struct ast_variable {
- char *name;
- char *value;
- char *file;
- int lineno;
- int object; /*!< 0 for variable, 1 for object */
- int blanklines; /*!< Number of blanklines following entry */
- struct ast_comment *precomments;
- struct ast_comment *sameline;
- struct ast_variable *next;
- char stuff[0];
-};
+#ifndef __linux__
+#define pthread_create __use_ast_pthread_create_instead__
+#endif
-static const char *ast_variable_retrieve(const struct ast_config *config, const char *category, const char *variable);
-static struct ast_config *config_text_file_load(const char *database, const char *table, const char *filename, struct ast_config *cfg, int withcomments, const char *suggested_include_file);
+typedef pthread_rwlock_t ast_rwlock_t;
-struct ast_config *localized_config_load_with_comments(const char *filename);
-static char *ast_category_browse(struct ast_config *config, const char *prev);
-static struct ast_variable *ast_variable_browse(const struct ast_config *config, const char *category);
-static void ast_variables_destroy(struct ast_variable *v);
-static void ast_config_destroy(struct ast_config *cfg);
-static struct ast_config_include *ast_include_new(struct ast_config *conf, const char *from_file, const char *included_file, int is_exec, const char *exec_file, int from_lineno, char *real_included_file_name, int real_included_file_name_size);
-static struct ast_config_include *ast_include_find(struct ast_config *conf, const char *included_file);
-void localized_ast_include_rename(struct ast_config *conf, const char *from_file, const char *to_file);
+static inline int ast_rwlock_init(ast_rwlock_t *prwlock)
+{
+ pthread_rwlockattr_t attr;
-static struct ast_variable *ast_variable_new(const char *name, const char *value, const char *filename);
+ pthread_rwlockattr_init(&attr);
-static struct ast_variable *ast_variable_new(const char *name, const char *value, const char *filename)
+#ifdef HAVE_PTHREAD_RWLOCK_PREFER_WRITER_NP
+ pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NP);
+#endif
+
+ return pthread_rwlock_init(prwlock, &attr);
+}
+
+static inline int ast_rwlock_destroy(ast_rwlock_t *prwlock)
{
- struct ast_variable *variable;
- int name_len = strlen(name) + 1;
+ return pthread_rwlock_destroy(prwlock);
+}
- if ((variable = ast_calloc(1, name_len + strlen(value) + 1 + strlen(filename) + 1 + sizeof(*variable)))) {
- variable->name = variable->stuff;
- variable->value = variable->stuff + name_len;
- variable->file = variable->value + strlen(value) + 1;
- strcpy(variable->name,name);
- strcpy(variable->value,value);
- strcpy(variable->file,filename);
- }
+static inline int ast_rwlock_unlock(ast_rwlock_t *prwlock)
+{
+ return pthread_rwlock_unlock(prwlock);
+}
- return variable;
+static inline int ast_rwlock_rdlock(ast_rwlock_t *prwlock)
+{
+ return pthread_rwlock_rdlock(prwlock);
}
-static struct ast_config_include *ast_include_new(struct ast_config *conf, const char *from_file, const char *included_file, int is_exec, const char *exec_file, int from_lineno, char *real_included_file_name, int real_included_file_name_size)
+static inline int ast_rwlock_tryrdlock(ast_rwlock_t *prwlock)
{
- /* a file should be included ONCE. Otherwise, if one of the instances is changed,
- then all be changed. -- how do we know to include it? -- Handling modified
- instances is possible, I'd have
- to create a new master for each instance. */
- struct ast_config_include *inc;
-
- inc = ast_include_find(conf, included_file);
- if (inc)
- {
- inc->inclusion_count++;
- snprintf(real_included_file_name, real_included_file_name_size, "%s~~%d", included_file, inc->inclusion_count);
- ast_log(LOG_WARNING,"'%s', line %d: Same File included more than once! This data will be saved in %s if saved back to disk.\n", from_file, from_lineno, real_included_file_name);
- } else
- *real_included_file_name = 0;
-
- inc = ast_calloc(1,sizeof(struct ast_config_include));
- inc->include_location_file = ast_strdup(from_file);
- inc->include_location_lineno = from_lineno;
- if (!ast_strlen_zero(real_included_file_name))
- inc->included_file = ast_strdup(real_included_file_name);
- else
- inc->included_file = ast_strdup(included_file);
-
- inc->exec = is_exec;
- if (is_exec)
- inc->exec_file = ast_strdup(exec_file);
-
- /* attach this new struct to the conf struct */
- inc->next = conf->includes;
- conf->includes = inc;
-
- return inc;
+ return pthread_rwlock_tryrdlock(prwlock);
}
-void localized_ast_include_rename(struct ast_config *conf, const char *from_file, const char *to_file)
+static inline int ast_rwlock_wrlock(ast_rwlock_t *prwlock)
{
- struct ast_config_include *incl;
- struct ast_category *cat;
- struct ast_variable *v;
-
- int from_len = strlen(from_file);
- int to_len = strlen(to_file);
-
- if (strcmp(from_file, to_file) == 0) /* no use wasting time if the name is the same */
- return;
-
- /* the manager code allows you to read in one config file, then
- write it back out under a different name. But, the new arrangement
- ties output lines to the file name. So, before you try to write
- the config file to disk, better riffle thru the data and make sure
- the file names are changed.
- */
- /* file names are on categories, includes (of course), and on variables. So,
- traverse all this and swap names */
-
- for (incl = conf->includes; incl; incl=incl->next) {
- if (strcmp(incl->include_location_file,from_file) == 0) {
- if (from_len >= to_len)
- strcpy(incl->include_location_file, to_file);
- else {
- free(incl->include_location_file);
- incl->include_location_file = strdup(to_file);
- }
- }
- }
- for (cat = conf->root; cat; cat = cat->next) {
- if (strcmp(cat->file,from_file) == 0) {
- if (from_len >= to_len)
- strcpy(cat->file, to_file);
- else {
- free(cat->file);
- cat->file = strdup(to_file);
- }
- }
- for (v = cat->root; v; v = v->next) {
- if (strcmp(v->file,from_file) == 0) {
- if (from_len >= to_len)
- strcpy(v->file, to_file);
- else {
- free(v->file);
- v->file = strdup(to_file);
- }
- }
- }
- }
+ return pthread_rwlock_wrlock(prwlock);
}
-static struct ast_config_include *ast_include_find(struct ast_config *conf, const char *included_file)
+static inline int ast_rwlock_trywrlock(ast_rwlock_t *prwlock)
{
- struct ast_config_include *x;
- for (x=conf->includes;x;x=x->next)
- {
- if (strcmp(x->included_file,included_file) == 0)
- return x;
- }
- return 0;
+ return pthread_rwlock_trywrlock(prwlock);
}
+/* Statically declared read/write locks */
-static void ast_variable_append(struct ast_category *category, struct ast_variable *variable);
-
-static void ast_variable_append(struct ast_category *category, struct ast_variable *variable)
-{
- if (!variable)
- return;
- if (category->last)
- category->last->next = variable;
- else
- category->root = variable;
- category->last = variable;
- while (category->last->next)
- category->last = category->last->next;
+#ifndef HAVE_PTHREAD_RWLOCK_INITIALIZER
+#define __AST_RWLOCK_DEFINE(scope, rwlock) \
+ scope ast_rwlock_t rwlock; \
+static void __attribute__ ((constructor)) init_##rwlock(void) \
+{ \
+ ast_rwlock_init(&rwlock); \
+} \
+static void __attribute__ ((destructor)) fini_##rwlock(void) \
+{ \
+ ast_rwlock_destroy(&rwlock); \
}
+#else
+#define AST_RWLOCK_INIT_VALUE PTHREAD_RWLOCK_INITIALIZER
+#define __AST_RWLOCK_DEFINE(scope, rwlock) \
+ scope ast_rwlock_t rwlock = AST_RWLOCK_INIT_VALUE
+#endif
-static struct ast_category *category_get(const struct ast_config *config, const char *category_name, int ignored);
-
-static struct ast_category *category_get(const struct ast_config *config, const char *category_name, int ignored)
-{
- struct ast_category *cat;
+#define AST_RWLOCK_DEFINE_STATIC(rwlock) __AST_RWLOCK_DEFINE(static, rwlock)
- /* try exact match first, then case-insensitive match */
- for (cat = config->root; cat; cat = cat->next) {
- if (cat->name == category_name && (ignored || !cat->ignored))
- return cat;
- }
+/*
+ * Initial support for atomic instructions.
+ * For platforms that have it, use the native cpu instruction to
+ * implement them. For other platforms, resort to a 'slow' version
+ * (defined in utils.c) that protects the atomic instruction with
+ * a single lock.
+ * The slow versions is always available, for testing purposes,
+ * as ast_atomic_fetchadd_int_slow()
+ */
- for (cat = config->root; cat; cat = cat->next) {
- if (!strcasecmp(cat->name, category_name) && (ignored || !cat->ignored))
- return cat;
- }
+#if defined(HAVE_OSX_ATOMICS)
+#include "libkern/OSAtomic.h"
+#endif
- return NULL;
-}
+/*! \brief Atomically add v to *p and return * the previous value of *p.
+ * This can be used to handle reference counts, and the return value
+ * can be used to generate unique identifiers.
+ */
-static struct ast_category *ast_category_get(const struct ast_config *config, const char *category_name)
+#if defined(HAVE_GCC_ATOMICS)
+AST_INLINE_API(int ast_atomic_fetchadd_int(volatile int *p, int v),
{
- return category_get(config, category_name, 0);
+ return __sync_fetch_and_add(p, v);
+})
+#elif defined(HAVE_OSX_ATOMICS) && (SIZEOF_INT == 4)
+AST_INLINE_API(int ast_atomic_fetchadd_int(volatile int *p, int v),
+{
+ return OSAtomicAdd32(v, (int32_t *) p);
+})
+#elif defined(HAVE_OSX_ATOMICS) && (SIZEOF_INT == 8)
+AST_INLINE_API(int ast_atomic_fetchadd_int(volatile int *p, int v),
+{
+ return OSAtomicAdd64(v, (int64_t *) p);
+#elif defined (__i386__) || defined(__x86_64__)
+AST_INLINE_API(int ast_atomic_fetchadd_int(volatile int *p, int v),
+{
+ __asm __volatile (
+ " lock xaddl %0, %1 ; "
+ : "+r" (v), /* 0 (result) */
+ "=m" (*p) /* 1 */
+ : "m" (*p)); /* 2 */
+ return (v);
+})
+#else
+static int ast_atomic_fetchadd_int_slow(volatile int *p, int v)
+{
+ int ret;
+ ret = *p;
+ *p += v;
+ return ret;
}
+AST_INLINE_API(int ast_atomic_fetchadd_int(volatile int *p, int v),
+{
+ return ast_atomic_fetchadd_int_slow(p, v);
+})
+#endif
-static struct ast_variable *ast_variable_browse(const struct ast_config *config, const char *category)
+/*! \brief decrement *p by 1 and return true if the variable has reached 0.
+ * Useful e.g. to check if a refcount has reached 0.
+ */
+#if defined(HAVE_GCC_ATOMICS)
+AST_INLINE_API(int ast_atomic_dec_and_test(volatile int *p),
{
- struct ast_category *cat = NULL;
+ return __sync_sub_and_fetch(p, 1) == 0;
+})
+#elif defined(HAVE_OSX_ATOMICS) && (SIZEOF_INT == 4)
+AST_INLINE_API(int ast_atomic_dec_and_test(volatile int *p),
+{
+ return OSAtomicAdd32( -1, (int32_t *) p) == 0;
+})
+#elif defined(HAVE_OSX_ATOMICS) && (SIZEOF_INT == 8)
+AST_INLINE_API(int ast_atomic_dec_and_test(volatile int *p),
+{
+ return OSAtomicAdd64( -1, (int64_t *) p) == 0;
+#else
+AST_INLINE_API(int ast_atomic_dec_and_test(volatile int *p),
+{
+ int a = ast_atomic_fetchadd_int(p, -1);
+ return a == 1; /* true if the value is 0 now (so it was 1 previously) */
+})
+#endif
- if (category && config->last_browse && (config->last_browse->name == category))
- cat = config->last_browse;
- else
- cat = ast_category_get(config, category);
+#ifndef DEBUG_CHANNEL_LOCKS
+/*! \brief Lock a channel. If DEBUG_CHANNEL_LOCKS is defined
+ in the Makefile, print relevant output for debugging */
+#define ast_channel_lock(x) ast_mutex_lock(&x->lock)
+/*! \brief Unlock a channel. If DEBUG_CHANNEL_LOCKS is defined
+ in the Makefile, print relevant output for debugging */
+#define ast_channel_unlock(x) ast_mutex_unlock(&x->lock)
+/*! \brief Try locking a channel. If DEBUG_CHANNEL_LOCKS is defined
+ in the Makefile, print relevant output for debugging */
+#define ast_channel_trylock(x) ast_mutex_trylock(&x->lock)
+#else
- return (cat) ? cat->root : NULL;
-}
+/*! \brief Lock AST channel (and print debugging output)
+\note You need to enable DEBUG_CHANNEL_LOCKS for this function */
+int ast_channel_lock(struct ast_channel *chan);
-static const char *ast_variable_retrieve(const struct ast_config *config, const char *category, const char *variable)
-{
- struct ast_variable *v;
+/*! \brief Unlock AST channel (and print debugging output)
+\note You need to enable DEBUG_CHANNEL_LOCKS for this function
+*/
+int ast_channel_unlock(struct ast_channel *chan);
- if (category) {
- for (v = ast_variable_browse(config, category); v; v = v->next) {
- if (!strcasecmp(variable, v->name))
- return v->value;
- }
- } else {
- struct ast_category *cat;
+/*! \brief Lock AST channel (and print debugging output)
+\note You need to enable DEBUG_CHANNEL_LOCKS for this function */
+int ast_channel_trylock(struct ast_channel *chan);
+#endif
- for (cat = config->root; cat; cat = cat->next)
- for (v = cat->root; v; v = v->next)
- if (!strcasecmp(variable, v->name))
- return v->value;
- }
- return NULL;
-}
+#include "asterisk/hashtab.h"
+#include "asterisk/ael_structs.h"
+#include "asterisk/pval.h"
-static struct ast_variable *variable_clone(const struct ast_variable *old)
-{
- struct ast_variable *new = ast_variable_new(old->name, old->value, old->file);
+/* from utils.h */
- if (new) {
- new->lineno = old->lineno;
- new->object = old->object;
- new->blanklines = old->blanklines;
- /* TODO: clone comments? */
- }
+static unsigned int __unsigned_int_flags_dummy;
- return new;
-}
-
-static void ast_variables_destroy(struct ast_variable *v)
-{
- struct ast_variable *vn;
+struct ast_flags { /* stolen from utils.h */
+ unsigned int flags;
+};
+#define ast_test_flag(p,flag) ({ \
+ typeof ((p)->flags) __p = (p)->flags; \
+ typeof (__unsigned_int_flags_dummy) __x = 0; \
+ (void) (&__p == &__x); \
+ ((p)->flags & (flag)); \
+ })
- while (v) {
- vn = v;
- v = v->next;
- free(vn);
- }
+#define ast_set2_flag(p,value,flag) do { \
+ typeof ((p)->flags) __p = (p)->flags; \
+ typeof (__unsigned_int_flags_dummy) __x = 0; \
+ (void) (&__p == &__x); \
+ if (value) \
+ (p)->flags |= (flag); \
+ else \
+ (p)->flags &= ~(flag); \
+ } while (0)
+
+
+#ifdef __AST_DEBUG_MALLOC
+static void ast_free(void *ptr) attribute_unused;
+static void ast_free(void *ptr)
+{
+ free(ptr);
}
+#else
+#define ast_free free
+#endif
-static void ast_includes_destroy(struct ast_config_include *incls)
+#ifndef __AST_DEBUG_MALLOC
+
+#define MALLOC_FAILURE_MSG \
+ ast_log(LOG_ERROR, "Memory Allocation Failure in function %s at line %d of %s\n", func, lineno, file);
+/*!
+ * \brief A wrapper for malloc()
+ *
+ * ast_malloc() is a wrapper for malloc() that will generate an Asterisk log
+ * message in the case that the allocation fails.
+ *
+ * The argument and return value are the same as malloc()
+ */
+#define ast_malloc(len) \
+ _ast_malloc((len), __FILE__, __LINE__, __PRETTY_FUNCTION__)
+
+AST_INLINE_API(
+void * attribute_malloc _ast_malloc(size_t len, const char *file, int lineno, const char *func),
{
- struct ast_config_include *incl,*inclnext;
-
- for (incl=incls; incl; incl = inclnext) {
- inclnext = incl->next;
- if (incl->include_location_file)
- free(incl->include_location_file);
- if (incl->exec_file)
- free(incl->exec_file);
- if (incl->included_file)
- free(incl->included_file);
- free(incl);
- }
+ void *p;
+
+ if (!(p = malloc(len)))
+ MALLOC_FAILURE_MSG;
+
+ return p;
}
+)
-static void ast_config_destroy(struct ast_config *cfg)
+/*!
+ * \brief A wrapper for calloc()
+ *
+ * ast_calloc() is a wrapper for calloc() that will generate an Asterisk log
+ * message in the case that the allocation fails.
+ *
+ * The arguments and return value are the same as calloc()
+ */
+#define ast_calloc(num, len) \
+ _ast_calloc((num), (len), __FILE__, __LINE__, __PRETTY_FUNCTION__)
+
+AST_INLINE_API(
+void * attribute_malloc _ast_calloc(size_t num, size_t len, const char *file, int lineno, const char *func),
{
- struct ast_category *cat, *catn;
+ void *p;
- if (!cfg)
- return;
+ if (!(p = calloc(num, len)))
+ MALLOC_FAILURE_MSG;
- ast_includes_destroy(cfg->includes);
-
- cat = cfg->root;
- while (cat) {
- ast_variables_destroy(cat->root);
- catn = cat;
- cat = cat->next;
- free(catn);
- }
- free(cfg);
+ return p;
}
+)
+/*!
+ * \brief A wrapper for calloc() for use in cache pools
+ *
+ * ast_calloc_cache() is a wrapper for calloc() that will generate an Asterisk log
+ * message in the case that the allocation fails. When memory debugging is in use,
+ * the memory allocated by this function will be marked as 'cache' so it can be
+ * distinguished from normal memory allocations.
+ *
+ * The arguments and return value are the same as calloc()
+ */
+#define ast_calloc_cache(num, len) \
+ _ast_calloc((num), (len), __FILE__, __LINE__, __PRETTY_FUNCTION__)
-/* options.h declars ast_options extern; I need it static? */
+/*!
+ * \brief A wrapper for realloc()
+ *
+ * ast_realloc() is a wrapper for realloc() that will generate an Asterisk log
+ * message in the case that the allocation fails.
+ *
+ * The arguments and return value are the same as realloc()
+ */
+#define ast_realloc(p, len) \
+ _ast_realloc((p), (len), __FILE__, __LINE__, __PRETTY_FUNCTION__)
-#define AST_CACHE_DIR_LEN 512
-#define AST_FILENAME_MAX 80
+AST_INLINE_API(
+void * attribute_malloc _ast_realloc(void *p, size_t len, const char *file, int lineno, const char *func),
+{
+ void *newp;
-/*! \ingroup main_options */
-enum ast_option_flags {
- /*! Allow \#exec in config files */
- AST_OPT_FLAG_EXEC_INCLUDES = (1 << 0),
- /*! Do not fork() */
- AST_OPT_FLAG_NO_FORK = (1 << 1),
- /*! Keep quiet */
- AST_OPT_FLAG_QUIET = (1 << 2),
- /*! Console mode */
- AST_OPT_FLAG_CONSOLE = (1 << 3),
- /*! Run in realtime Linux priority */
- AST_OPT_FLAG_HIGH_PRIORITY = (1 << 4),
- /*! Initialize keys for RSA authentication */
- AST_OPT_FLAG_INIT_KEYS = (1 << 5),
- /*! Remote console */
- AST_OPT_FLAG_REMOTE = (1 << 6),
- /*! Execute an asterisk CLI command upon startup */
- AST_OPT_FLAG_EXEC = (1 << 7),
- /*! Don't use termcap colors */
- AST_OPT_FLAG_NO_COLOR = (1 << 8),
- /*! Are we fully started yet? */
- AST_OPT_FLAG_FULLY_BOOTED = (1 << 9),
- /*! Trascode via signed linear */
- AST_OPT_FLAG_TRANSCODE_VIA_SLIN = (1 << 10),
- /*! Enable priority jumping in applications */
- AST_OPT_FLAG_PRIORITY_JUMPING = (1 << 11),
- /*! Dump core on a seg fault */
- AST_OPT_FLAG_DUMP_CORE = (1 << 12),
- /*! Cache sound files */
- AST_OPT_FLAG_CACHE_RECORD_FILES = (1 << 13),
- /*! Display timestamp in CLI verbose output */
- AST_OPT_FLAG_TIMESTAMP = (1 << 14),
- /*! Override config */
- AST_OPT_FLAG_OVERRIDE_CONFIG = (1 << 15),
- /*! Reconnect */
- AST_OPT_FLAG_RECONNECT = (1 << 16),
- /*! Transmit Silence during Record() */
- AST_OPT_FLAG_TRANSMIT_SILENCE = (1 << 17),
- /*! Suppress some warnings */
- AST_OPT_FLAG_DONT_WARN = (1 << 18),
- /*! End CDRs before the 'h' extension */
- AST_OPT_FLAG_END_CDR_BEFORE_H_EXTEN = (1 << 19),
- /*! Use Zaptel Timing for generators if available */
- AST_OPT_FLAG_INTERNAL_TIMING = (1 << 20),
- /*! Always fork, even if verbose or debug settings are non-zero */
- AST_OPT_FLAG_ALWAYS_FORK = (1 << 21),
- /*! Disable log/verbose output to remote consoles */
- AST_OPT_FLAG_MUTE = (1 << 22)
-};
+ if (!(newp = realloc(p, len)))
+ MALLOC_FAILURE_MSG;
-/*! These are the options that set by default when Asterisk starts */
-#define AST_DEFAULT_OPTIONS AST_OPT_FLAG_TRANSCODE_VIA_SLIN
+ return newp;
+}
+)
-#define ast_opt_exec_includes ast_test_flag(&ast_options, AST_OPT_FLAG_EXEC_INCLUDES)
-#define ast_opt_no_fork ast_test_flag(&ast_options, AST_OPT_FLAG_NO_FORK)
-#define ast_opt_quiet ast_test_flag(&ast_options, AST_OPT_FLAG_QUIET)
-#define ast_opt_console ast_test_flag(&ast_options, AST_OPT_FLAG_CONSOLE)
-#define ast_opt_high_priority ast_test_flag(&ast_options, AST_OPT_FLAG_HIGH_PRIORITY)
-#define ast_opt_init_keys ast_test_flag(&ast_options, AST_OPT_FLAG_INIT_KEYS)
-#define ast_opt_remote ast_test_flag(&ast_options, AST_OPT_FLAG_REMOTE)
-#define ast_opt_exec ast_test_flag(&ast_options, AST_OPT_FLAG_EXEC)
-#define ast_opt_no_color ast_test_flag(&ast_options, AST_OPT_FLAG_NO_COLOR)
-#define ast_fully_booted ast_test_flag(&ast_options, AST_OPT_FLAG_FULLY_BOOTED)
-#define ast_opt_transcode_via_slin ast_test_flag(&ast_options, AST_OPT_FLAG_TRANSCODE_VIA_SLIN)
-#define ast_opt_priority_jumping ast_test_flag(&ast_options, AST_OPT_FLAG_PRIORITY_JUMPING)
-#define ast_opt_dump_core ast_test_flag(&ast_options, AST_OPT_FLAG_DUMP_CORE)
-#define ast_opt_cache_record_files ast_test_flag(&ast_options, AST_OPT_FLAG_CACHE_RECORD_FILES)
-#define ast_opt_timestamp ast_test_flag(&ast_options, AST_OPT_FLAG_TIMESTAMP)
-#define ast_opt_override_config ast_test_flag(&ast_options, AST_OPT_FLAG_OVERRIDE_CONFIG)
-#define ast_opt_reconnect ast_test_flag(&ast_options, AST_OPT_FLAG_RECONNECT)
-#define ast_opt_transmit_silence ast_test_flag(&ast_options, AST_OPT_FLAG_TRANSMIT_SILENCE)
-#define ast_opt_dont_warn ast_test_flag(&ast_options, AST_OPT_FLAG_DONT_WARN)
-#define ast_opt_end_cdr_before_h_exten ast_test_flag(&ast_options, AST_OPT_FLAG_END_CDR_BEFORE_H_EXTEN)
-#define ast_opt_internal_timing ast_test_flag(&ast_options, AST_OPT_FLAG_INTERNAL_TIMING)
-#define ast_opt_always_fork ast_test_flag(&ast_options, AST_OPT_FLAG_ALWAYS_FORK)
-#define ast_opt_mute ast_test_flag(&ast_options, AST_OPT_FLAG_MUTE)
+/*!
+ * \brief A wrapper for strdup()
+ *
+ * ast_strdup() is a wrapper for strdup() that will generate an Asterisk log
+ * message in the case that the allocation fails.
+ *
+ * ast_strdup(), unlike strdup(), can safely accept a NULL argument. If a NULL
+ * argument is provided, ast_strdup will return NULL without generating any
+ * kind of error log message.
+ *
+ * The argument and return value are the same as strdup()
+ */
+#define ast_strdup(str) \
+ _ast_strdup((str), __FILE__, __LINE__, __PRETTY_FUNCTION__)
-/* IN CONFLICT: extern int option_verbose; */
-/* IN CONFLICT: extern int option_debug; */ /*!< Debugging */
-extern int option_maxcalls; /*!< Maximum number of simultaneous channels */
-extern double option_maxload;
-extern char defaultlanguage[];
+AST_INLINE_API(
+char * attribute_malloc _ast_strdup(const char *str, const char *file, int lineno, const char *func),
+{
+ char *newstr = NULL;
-extern time_t ast_startuptime;
-extern time_t ast_lastreloadtime;
-extern pid_t ast_mainpid;
+ if (str) {
+ if (!(newstr = strdup(str)))
+ MALLOC_FAILURE_MSG;
+ }
-extern char record_cache_dir[AST_CACHE_DIR_LEN];
-extern char debug_filename[AST_FILENAME_MAX];
+ return newstr;
+}
+)
-extern int ast_language_is_prefix;
+/*!
+ * \brief A wrapper for strndup()
+ *
+ * ast_strndup() is a wrapper for strndup() that will generate an Asterisk log
+ * message in the case that the allocation fails.
+ *
+ * ast_strndup(), unlike strndup(), can safely accept a NULL argument for the
+ * string to duplicate. If a NULL argument is provided, ast_strdup will return
+ * NULL without generating any kind of error log message.
+ *
+ * The arguments and return value are the same as strndup()
+ */
+#define ast_strndup(str, len) \
+ _ast_strndup((str), (len), __FILE__, __LINE__, __PRETTY_FUNCTION__)
+AST_INLINE_API(
+char * attribute_malloc _ast_strndup(const char *str, size_t len, const char *file, int lineno, const char *func),
+{
+ char *newstr = NULL;
+ if (str) {
+ if (!(newstr = strndup(str, len)))
+ MALLOC_FAILURE_MSG;
+ }
-/* lock.h */
+ return newstr;
+}
+)
-#ifndef HAVE_MTX_PROFILE
-#define __MTX_PROF(a) return pthread_mutex_lock((a))
-#else
-#define __MTX_PROF(a) do { \
- int i; \
- /* profile only non-blocking events */ \
- ast_mark(mtx_prof, 1); \
- i = pthread_mutex_trylock((a)); \
- ast_mark(mtx_prof, 0); \
- if (!i) \
- return i; \
- else \
- return pthread_mutex_lock((a)); \
- } while (0)
-#endif /* HAVE_MTX_PROFILE */
+/*!
+ * \brief A wrapper for asprintf()
+ *
+ * ast_asprintf() is a wrapper for asprintf() that will generate an Asterisk log
+ * message in the case that the allocation fails.
+ *
+ * The arguments and return value are the same as asprintf()
+ */
+#define ast_asprintf(ret, fmt, ...) \
+ _ast_asprintf((ret), __FILE__, __LINE__, __PRETTY_FUNCTION__, fmt, __VA_ARGS__)
-#define AST_PTHREADT_NULL (pthread_t) -1
-#define AST_PTHREADT_STOP (pthread_t) -2
+AST_INLINE_API(
+int _ast_asprintf(char **ret, const char *file, int lineno, const char *func, const char *fmt, ...),
+{
+ int res;
+ va_list ap;
-#if defined(SOLARIS) || defined(BSD)
-#define AST_MUTEX_INIT_W_CONSTRUCTORS
-#endif /* SOLARIS || BSD */
+ va_start(ap, fmt);
+ if ((res = vasprintf(ret, fmt, ap)) == -1)
+ MALLOC_FAILURE_MSG;
+ va_end(ap);
-/* Asterisk REQUIRES recursive (not error checking) mutexes
- and will not run without them. */
-#if defined(PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP) && defined(PTHREAD_MUTEX_RECURSIVE_NP)
-#define PTHREAD_MUTEX_INIT_VALUE PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
-#define AST_MUTEX_KIND PTHREAD_MUTEX_RECURSIVE_NP
-#else
-#define PTHREAD_MUTEX_INIT_VALUE PTHREAD_MUTEX_INITIALIZER
-#define AST_MUTEX_KIND PTHREAD_MUTEX_RECURSIVE
-#endif /* PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP */
+ return res;
+}
+)
-#ifdef DEBUG_THREADS
+/*!
+ * \brief A wrapper for vasprintf()
+ *
+ * ast_vasprintf() is a wrapper for vasprintf() that will generate an Asterisk log
+ * message in the case that the allocation fails.
+ *
+ * The arguments and return value are the same as vasprintf()
+ */
+#define ast_vasprintf(ret, fmt, ap) \
+ _ast_vasprintf((ret), __FILE__, __LINE__, __PRETTY_FUNCTION__, (fmt), (ap))
-#define __ast_mutex_logger(...) do { if (canlog) ast_log(LOG_ERROR, __VA_ARGS__); else fprintf(stderr, __VA_ARGS__); } while (0)
+AST_INLINE_API(
+int _ast_vasprintf(char **ret, const char *file, int lineno, const char *func, const char *fmt, va_list ap),
+{
+ int res;
+
+ if ((res = vasprintf(ret, fmt, ap)) == -1)
+ MALLOC_FAILURE_MSG;
+
+ return res;
+}
+)
-#ifdef THREAD_CRASH
-#define DO_THREAD_CRASH do { *((int *)(0)) = 1; } while(0)
#else
-#define DO_THREAD_CRASH do { } while (0)
+
+/* If astmm is in use, let it handle these. Otherwise, it will report that
+ all allocations are coming from this header file */
+
+#define ast_malloc(a) malloc(a)
+#define ast_calloc(a,b) calloc(a,b)
+#define ast_realloc(a,b) realloc(a,b)
+#define ast_strdup(a) strdup(a)
+#define ast_strndup(a,b) strndup(a,b)
+#define ast_asprintf(a,b,c) asprintf(a,b,c)
+#define ast_vasprintf(a,b,c) vasprintf(a,b,c)
+
+#endif /* AST_DEBUG_MALLOC */
+
+#if !defined(ast_strdupa) && defined(__GNUC__)
+/*!
+ \brief duplicate a string in memory from the stack
+ \param s The string to duplicate
+
+ This macro will duplicate the given string. It returns a pointer to the stack
+ allocatted memory for the new string.
+*/
+#define ast_strdupa(s) \
+ (__extension__ \
+ ({ \
+ const char *__old = (s); \
+ size_t __len = strlen(__old) + 1; \
+ char *__new = __builtin_alloca(__len); \
+ memcpy (__new, __old, __len); \
+ __new; \
+ }))
#endif
-#define AST_MUTEX_INIT_VALUE { PTHREAD_MUTEX_INIT_VALUE, { NULL }, { 0 }, 0, { NULL }, { 0 } }
-#define AST_MAX_REENTRANCY 10
+/* from config.c */
-struct ast_mutex_info {
- pthread_mutex_t mutex;
- /*! Track which thread holds this lock */
- unsigned int track:1;
- const char *file[AST_MAX_REENTRANCY];
- int lineno[AST_MAX_REENTRANCY];
- int reentrancy;
- const char *func[AST_MAX_REENTRANCY];
- pthread_t thread[AST_MAX_REENTRANCY];
-};
+#define MAX_NESTED_COMMENTS 128
+#define COMMENT_START ";--"
+#define COMMENT_END "--;"
+#define COMMENT_META ';'
+#define COMMENT_TAG '-'
-typedef struct ast_mutex_info ast_mutex_t;
+static char *extconfig_conf = "extconfig.conf";
-typedef pthread_cond_t ast_cond_t;
+/*! Growable string buffer */
+static char *comment_buffer; /*!< this will be a comment collector.*/
+static int comment_buffer_size; /*!< the amount of storage so far alloc'd for the comment_buffer */
-static pthread_mutex_t empty_mutex;
+static char *lline_buffer; /*!< A buffer for stuff behind the ; */
+static int lline_buffer_size;
-static void __attribute__((constructor)) init_empty_mutex(void)
+#define CB_INCR 250
+
+struct ast_comment {
+ struct ast_comment *next;
+ char cmt[0];
+};
+
+static void CB_INIT(void)
{
- memset(&empty_mutex, 0, sizeof(empty_mutex));
+ if (!comment_buffer) {
+ comment_buffer = ast_malloc(CB_INCR);
+ if (!comment_buffer)
+ return;
+ comment_buffer[0] = 0;
+ comment_buffer_size = CB_INCR;
+ lline_buffer = ast_malloc(CB_INCR);
+ if (!lline_buffer)
+ return;
+ lline_buffer[0] = 0;
+ lline_buffer_size = CB_INCR;
+ } else {
+ comment_buffer[0] = 0;
+ lline_buffer[0] = 0;
+ }
}
-static inline int __ast_pthread_mutex_init_attr(const char *filename, int lineno, const char *func,
- const char *mutex_name, ast_mutex_t *t,
- pthread_mutexattr_t *attr)
+static void CB_ADD(char *str)
{
-#ifdef AST_MUTEX_INIT_W_CONSTRUCTORS
- int canlog = strcmp(filename, "logger.c");
+ int rem = comment_buffer_size - strlen(comment_buffer) - 1;
+ int siz = strlen(str);
+ if (rem < siz+1) {
+ comment_buffer = ast_realloc(comment_buffer, comment_buffer_size + CB_INCR + siz + 1);
+ if (!comment_buffer)
+ return;
+ comment_buffer_size += CB_INCR+siz+1;
+ }
+ strcat(comment_buffer,str);
+}
- if ((t->mutex) != ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) {
- if ((t->mutex) != (empty_mutex)) {
- __ast_mutex_logger("%s line %d (%s): Error: mutex '%s' is already initialized.\n",
- filename, lineno, func, mutex_name);
- __ast_mutex_logger("%s line %d (%s): Error: previously initialization of mutex '%s'.\n",
- t->file[0], t->lineno[0], t->func[0], mutex_name);
- DO_THREAD_CRASH;
- return 0;
- }
+static void CB_ADD_LEN(char *str, int len)
+{
+ int cbl = strlen(comment_buffer) + 1;
+ int rem = comment_buffer_size - cbl;
+ if (rem < len+1) {
+ comment_buffer = ast_realloc(comment_buffer, comment_buffer_size + CB_INCR + len + 1);
+ if (!comment_buffer)
+ return;
+ comment_buffer_size += CB_INCR+len+1;
}
-#endif
+ strncat(comment_buffer,str,len); /* safe */
+ comment_buffer[cbl+len-1] = 0;
+}
+
+static void LLB_ADD(char *str)
+{
+ int rem = lline_buffer_size - strlen(lline_buffer) - 1;
+ int siz = strlen(str);
+ if (rem < siz+1) {
+ lline_buffer = ast_realloc(lline_buffer, lline_buffer_size + CB_INCR + siz + 1);
+ if (!lline_buffer)
+ return;
+ lline_buffer_size += CB_INCR + siz + 1;
+ }
+ strcat(lline_buffer,str);
+}
- t->file[0] = filename;
- t->lineno[0] = lineno;
- t->func[0] = func;
- t->thread[0] = 0;
- t->reentrancy = 0;
+static void CB_RESET(void )
+{
+ comment_buffer[0] = 0;
+ lline_buffer[0] = 0;
+}
+
+/*! \brief Keep track of how many threads are currently trying to wait*() on
+ * a child process */
+static unsigned int safe_system_level = 0;
+static void *safe_system_prev_handler;
+
+/*! \brief NULL handler so we can collect the child exit status */
+static void null_sig_handler(int signal)
+{
- return pthread_mutex_init(&t->mutex, attr);
}
-static inline int __ast_pthread_mutex_init(const char *filename, int lineno, const char *func,
- const char *mutex_name, ast_mutex_t *t)
+void ast_replace_sigchld(void);
+
+void ast_replace_sigchld(void)
{
- static pthread_mutexattr_t attr;
+ unsigned int level;
- pthread_mutexattr_init(&attr);
- pthread_mutexattr_settype(&attr, AST_MUTEX_KIND);
+ level = safe_system_level++;
+
+ /* only replace the handler if it has not already been done */
+ if (level == 0)
+ safe_system_prev_handler = signal(SIGCHLD, null_sig_handler);
- return __ast_pthread_mutex_init_attr(filename, lineno, func, mutex_name, t, &attr);
}
-#define ast_mutex_init(pmutex) __ast_pthread_mutex_init(__FILE__, __LINE__, __PRETTY_FUNCTION__, #pmutex, pmutex)
-static inline int __ast_pthread_mutex_destroy(const char *filename, int lineno, const char *func,
- const char *mutex_name, ast_mutex_t *t)
-{
- int res;
- int canlog = strcmp(filename, "logger.c");
+void ast_unreplace_sigchld(void);
-#ifdef AST_MUTEX_INIT_W_CONSTRUCTORS
- if ((t->mutex) == ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) {
- __ast_mutex_logger("%s line %d (%s): Error: mutex '%s' is uninitialized.\n",
- filename, lineno, func, mutex_name);
- }
-#endif
+void ast_unreplace_sigchld(void)
+{
+ unsigned int level;
- res = pthread_mutex_trylock(&t->mutex);
- switch (res) {
- case 0:
- pthread_mutex_unlock(&t->mutex);
- break;
- case EINVAL:
- __ast_mutex_logger("%s line %d (%s): Error: attempt to destroy invalid mutex '%s'.\n",
- filename, lineno, func, mutex_name);
- break;
- case EBUSY:
- __ast_mutex_logger("%s line %d (%s): Error: attempt to destroy locked mutex '%s'.\n",
- filename, lineno, func, mutex_name);
- __ast_mutex_logger("%s line %d (%s): Error: '%s' was locked here.\n",
- t->file[t->reentrancy-1], t->lineno[t->reentrancy-1], t->func[t->reentrancy-1], mutex_name);
- break;
- }
+ level = --safe_system_level;
- if ((res = pthread_mutex_destroy(&t->mutex)))
- __ast_mutex_logger("%s line %d (%s): Error destroying mutex: %s\n",
- filename, lineno, func, strerror(res));
-#ifndef PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
- else
- t->mutex = PTHREAD_MUTEX_INIT_VALUE;
-#endif
- t->file[0] = filename;
- t->lineno[0] = lineno;
- t->func[0] = func;
+ /* only restore the handler if we are the last one */
+ if (level == 0)
+ signal(SIGCHLD, safe_system_prev_handler);
- return res;
}
-static inline int __ast_pthread_mutex_lock(const char *filename, int lineno, const char *func,
- const char* mutex_name, ast_mutex_t *t)
+int ast_safe_system(const char *s);
+
+int ast_safe_system(const char *s)
{
+ pid_t pid;
+#ifdef HAVE_WORKING_FORK
+ int x;
+#endif
int res;
- int canlog = strcmp(filename, "logger.c");
+ struct rusage rusage;
+ int status;
-#if defined(AST_MUTEX_INIT_W_CONSTRUCTORS)
- if ((t->mutex) == ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) {
- __ast_mutex_logger("%s line %d (%s): Error: mutex '%s' is uninitialized.\n",
- filename, lineno, func, mutex_name);
- ast_mutex_init(t);
- }
-#endif /* AST_MUTEX_INIT_W_CONSTRUCTORS */
+#if defined(HAVE_WORKING_FORK) || defined(HAVE_WORKING_VFORK)
+ ast_replace_sigchld();
-#ifdef DETECT_DEADLOCKS
- {
- time_t seconds = time(NULL);
- time_t current;
- do {
-#ifdef HAVE_MTX_PROFILE
- ast_mark(mtx_prof, 1);
-#endif
- res = pthread_mutex_trylock(&t->mutex);
-#ifdef HAVE_MTX_PROFILE
- ast_mark(mtx_prof, 0);
-#endif
- if (res == EBUSY) {
- current = time(NULL);
- if ((current - seconds) && (!((current - seconds) % 5))) {
- __ast_mutex_logger("%s line %d (%s): Deadlock? waited %d sec for mutex '%s'?\n",
- filename, lineno, func, (int)(current - seconds), mutex_name);
- __ast_mutex_logger("%s line %d (%s): '%s' was locked here.\n",
- t->file[t->reentrancy-1], t->lineno[t->reentrancy-1],
- t->func[t->reentrancy-1], mutex_name);
- }
- usleep(200);
- }
- } while (res == EBUSY);
- }
+#ifdef HAVE_WORKING_FORK
+ pid = fork();
#else
-#ifdef HAVE_MTX_PROFILE
- ast_mark(mtx_prof, 1);
- res = pthread_mutex_trylock(&t->mutex);
- ast_mark(mtx_prof, 0);
- if (res)
-#endif
- res = pthread_mutex_lock(&t->mutex);
-#endif /* DETECT_DEADLOCKS */
+ pid = vfork();
+#endif
- if (!res) {
- if (t->reentrancy < AST_MAX_REENTRANCY) {
- t->file[t->reentrancy] = filename;
- t->lineno[t->reentrancy] = lineno;
- t->func[t->reentrancy] = func;
- t->thread[t->reentrancy] = pthread_self();
- t->reentrancy++;
- } else {
- __ast_mutex_logger("%s line %d (%s): '%s' really deep reentrancy!\n",
- filename, lineno, func, mutex_name);
+ if (pid == 0) {
+#ifdef HAVE_WORKING_FORK
+ /* Close file descriptors and launch system command */
+ for (x = STDERR_FILENO + 1; x < 4096; x++)
+ close(x);
+#endif
+ execl("/bin/sh", "/bin/sh", "-c", s, (char *) NULL);
+ _exit(1);
+ } else if (pid > 0) {
+ for(;;) {
+ res = wait4(pid, &status, 0, &rusage);
+ if (res > -1) {
+ res = WIFEXITED(status) ? WEXITSTATUS(status) : -1;
+ break;
+ } else if (errno != EINTR)
+ break;
}
} else {
- __ast_mutex_logger("%s line %d (%s): Error obtaining mutex: %s\n",
- filename, lineno, func, strerror(errno));
- DO_THREAD_CRASH;
+ ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(errno));
+ res = -1;
}
+ ast_unreplace_sigchld();
+#else
+ res = -1;
+#endif
+
return res;
}
-static inline int __ast_pthread_mutex_trylock(const char *filename, int lineno, const char *func,
- const char* mutex_name, ast_mutex_t *t)
-{
- int res;
- int canlog = strcmp(filename, "logger.c");
+static struct ast_comment *ALLOC_COMMENT(const char *buffer)
+{
+ struct ast_comment *x = ast_calloc(1,sizeof(struct ast_comment)+strlen(buffer)+1);
+ strcpy(x->cmt, buffer);
+ return x;
+}
-#if defined(AST_MUTEX_INIT_W_CONSTRUCTORS)
- if ((t->mutex) == ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) {
- __ast_mutex_logger("%s line %d (%s): Error: mutex '%s' is uninitialized.\n",
- filename, lineno, func, mutex_name);
- ast_mutex_init(t);
- }
-#endif /* AST_MUTEX_INIT_W_CONSTRUCTORS */
+static struct ast_config_map {
+ struct ast_config_map *next;
+ char *name;
+ char *driver;
+ char *database;
+ char *table;
+ char stuff[0];
+} *config_maps = NULL;
- if (!(res = pthread_mutex_trylock(&t->mutex))) {
- if (t->reentrancy < AST_MAX_REENTRANCY) {
- t->file[t->reentrancy] = filename;
- t->lineno[t->reentrancy] = lineno;
- t->func[t->reentrancy] = func;
- t->thread[t->reentrancy] = pthread_self();
- t->reentrancy++;
- } else {
- __ast_mutex_logger("%s line %d (%s): '%s' really deep reentrancy!\n",
- filename, lineno, func, mutex_name);
- }
- } else {
- __ast_mutex_logger("%s line %d (%s): Warning: '%s' was locked here.\n",
- t->file[t->reentrancy-1], t->lineno[t->reentrancy-1], t->func[t->reentrancy-1], mutex_name);
- }
+static struct ast_config_engine *config_engine_list;
- return res;
-}
+#define MAX_INCLUDE_LEVEL 10
-static inline int __ast_pthread_mutex_unlock(const char *filename, int lineno, const char *func,
- const char *mutex_name, ast_mutex_t *t)
-{
- int res;
- int canlog = strcmp(filename, "logger.c");
-#ifdef AST_MUTEX_INIT_W_CONSTRUCTORS
- if ((t->mutex) == ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) {
- __ast_mutex_logger("%s line %d (%s): Error: mutex '%s' is uninitialized.\n",
- filename, lineno, func, mutex_name);
- }
-#endif
+struct ast_category {
+ char name[80];
+ int ignored; /*!< do not let user of the config see this category */
+ int include_level;
+ char *file; /*!< the file name from whence this declaration was read */
+ int lineno;
+ struct ast_comment *precomments;
+ struct ast_comment *sameline;
+ struct ast_variable *root;
+ struct ast_variable *last;
+ struct ast_category *next;
+};
+
+struct ast_config {
+ struct ast_category *root;
+ struct ast_category *last;
+ struct ast_category *current;
+ struct ast_category *last_browse; /*!< used to cache the last category supplied via category_browse */
+ int include_level;
+ int max_include_level;
+ struct ast_config_include *includes; /*!< a list of inclusions, which should describe the entire tree */
+};
- if (t->reentrancy && (t->thread[t->reentrancy-1] != pthread_self())) {
- __ast_mutex_logger("%s line %d (%s): attempted unlock mutex '%s' without owning it!\n",
- filename, lineno, func, mutex_name);
- __ast_mutex_logger("%s line %d (%s): '%s' was locked here.\n",
- t->file[t->reentrancy-1], t->lineno[t->reentrancy-1], t->func[t->reentrancy-1], mutex_name);
- DO_THREAD_CRASH;
- }
+struct ast_config_include {
+ char *include_location_file; /*!< file name in which the include occurs */
+ int include_location_lineno; /*!< lineno where include occurred */
+ int exec; /*!< set to non-zero if itsa #exec statement */
+ char *exec_file; /*!< if it's an exec, you'll have both the /var/tmp to read, and the original script */
+ char *included_file; /*!< file name included */
+ int inclusion_count; /*!< if the file is included more than once, a running count thereof -- but, worry not,
+ we explode the instances and will include those-- so all entries will be unique */
+ int output; /*!< a flag to indicate if the inclusion has been output */
+ struct ast_config_include *next; /*!< ptr to next inclusion in the list */
+};
- if (--t->reentrancy < 0) {
- __ast_mutex_logger("%s line %d (%s): mutex '%s' freed more times than we've locked!\n",
- filename, lineno, func, mutex_name);
- t->reentrancy = 0;
- }
+typedef struct ast_config *config_load_func(const char *database, const char *table, const char *configfile, struct ast_config *config, int withcomments, const char *suggested_include_file);
+typedef struct ast_variable *realtime_var_get(const char *database, const char *table, va_list ap);
+typedef struct ast_config *realtime_multi_get(const char *database, const char *table, va_list ap);
+typedef int realtime_update(const char *database, const char *table, const char *keyfield, const char *entity, va_list ap);
- if (t->reentrancy < AST_MAX_REENTRANCY) {
- t->file[t->reentrancy] = NULL;
- t->lineno[t->reentrancy] = 0;
- t->func[t->reentrancy] = NULL;
- t->thread[t->reentrancy] = 0;
- }
+/*! \brief Configuration engine structure, used to define realtime drivers */
+struct ast_config_engine {
+ char *name;
+ config_load_func *load_func;
+ realtime_var_get *realtime_func;
+ realtime_multi_get *realtime_multi_func;
+ realtime_update *update_func;
+ struct ast_config_engine *next;
+};
- if ((res = pthread_mutex_unlock(&t->mutex))) {
- __ast_mutex_logger("%s line %d (%s): Error releasing mutex: %s\n",
- filename, lineno, func, strerror(res));
- DO_THREAD_CRASH;
- }
+static struct ast_config_engine *config_engine_list;
- return res;
-}
+/* taken from strings.h */
-static inline int __ast_cond_init(const char *filename, int lineno, const char *func,
- const char *cond_name, ast_cond_t *cond, pthread_condattr_t *cond_attr)
+static force_inline int ast_strlen_zero(const char *s)
{
- return pthread_cond_init(cond, cond_attr);
+ return (!s || (*s == '\0'));
}
-static inline int __ast_cond_signal(const char *filename, int lineno, const char *func,
- const char *cond_name, ast_cond_t *cond)
-{
- return pthread_cond_signal(cond);
-}
+#define S_OR(a, b) (!ast_strlen_zero(a) ? (a) : (b))
-static inline int __ast_cond_broadcast(const char *filename, int lineno, const char *func,
- const char *cond_name, ast_cond_t *cond)
+AST_INLINE_API(
+void ast_copy_string(char *dst, const char *src, size_t size),
{
- return pthread_cond_broadcast(cond);
+ while (*src && size) {
+ *dst++ = *src++;
+ size--;
+ }
+ if (__builtin_expect(!size, 0))
+ dst--;
+ *dst = '\0';
}
+)
-static inline int __ast_cond_destroy(const char *filename, int lineno, const char *func,
- const char *cond_name, ast_cond_t *cond)
+AST_INLINE_API(
+char *ast_skip_blanks(const char *str),
{
- return pthread_cond_destroy(cond);
+ while (*str && *str < 33)
+ str++;
+ return (char *)str;
}
+)
-static inline int __ast_cond_wait(const char *filename, int lineno, const char *func,
- const char *cond_name, const char *mutex_name,
- ast_cond_t *cond, ast_mutex_t *t)
+/*!
+ \brief Trims trailing whitespace characters from a string.
+ \param ast_trim_blanks function being used
+ \param str the input string
+ \return a pointer to the modified string
+ */
+AST_INLINE_API(
+char *ast_trim_blanks(char *str),
{
- int res;
- int canlog = strcmp(filename, "logger.c");
-
-#ifdef AST_MUTEX_INIT_W_CONSTRUCTORS
- if ((t->mutex) == ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) {
- __ast_mutex_logger("%s line %d (%s): Error: mutex '%s' is uninitialized.\n",
- filename, lineno, func, mutex_name);
- }
-#endif
-
- if (t->reentrancy && (t->thread[t->reentrancy-1] != pthread_self())) {
- __ast_mutex_logger("%s line %d (%s): attempted unlock mutex '%s' without owning it!\n",
- filename, lineno, func, mutex_name);
- __ast_mutex_logger("%s line %d (%s): '%s' was locked here.\n",
- t->file[t->reentrancy-1], t->lineno[t->reentrancy-1], t->func[t->reentrancy-1], mutex_name);
- DO_THREAD_CRASH;
- }
-
- if (--t->reentrancy < 0) {
- __ast_mutex_logger("%s line %d (%s): mutex '%s' freed more times than we've locked!\n",
- filename, lineno, func, mutex_name);
- t->reentrancy = 0;
- }
-
- if (t->reentrancy < AST_MAX_REENTRANCY) {
- t->file[t->reentrancy] = NULL;
- t->lineno[t->reentrancy] = 0;
- t->func[t->reentrancy] = NULL;
- t->thread[t->reentrancy] = 0;
- }
+ char *work = str;
- if ((res = pthread_cond_wait(cond, &t->mutex))) {
- __ast_mutex_logger("%s line %d (%s): Error waiting on condition mutex '%s'\n",
- filename, lineno, func, strerror(res));
- DO_THREAD_CRASH;
- } else {
- if (t->reentrancy < AST_MAX_REENTRANCY) {
- t->file[t->reentrancy] = filename;
- t->lineno[t->reentrancy] = lineno;
- t->func[t->reentrancy] = func;
- t->thread[t->reentrancy] = pthread_self();
- t->reentrancy++;
- } else {
- __ast_mutex_logger("%s line %d (%s): '%s' really deep reentrancy!\n",
- filename, lineno, func, mutex_name);
- }
+ if (work) {
+ work += strlen(work) - 1;
+ /* It's tempting to only want to erase after we exit this loop,
+ but since ast_trim_blanks *could* receive a constant string
+ (which we presumably wouldn't have to touch), we shouldn't
+ actually set anything unless we must, and it's easier just
+ to set each position to \0 than to keep track of a variable
+ for it */
+ while ((work >= str) && *work < 33)
+ *(work--) = '\0';
}
-
- return res;
+ return str;
}
+)
-static inline int __ast_cond_timedwait(const char *filename, int lineno, const char *func,
- const char *cond_name, const char *mutex_name, ast_cond_t *cond,
- ast_mutex_t *t, const struct timespec *abstime)
-{
- int res;
- int canlog = strcmp(filename, "logger.c");
-
-#ifdef AST_MUTEX_INIT_W_CONSTRUCTORS
- if ((t->mutex) == ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) {
- __ast_mutex_logger("%s line %d (%s): Error: mutex '%s' is uninitialized.\n",
- filename, lineno, func, mutex_name);
- }
-#endif
-
- if (t->reentrancy && (t->thread[t->reentrancy-1] != pthread_self())) {
- __ast_mutex_logger("%s line %d (%s): attempted unlock mutex '%s' without owning it!\n",
- filename, lineno, func, mutex_name);
- __ast_mutex_logger("%s line %d (%s): '%s' was locked here.\n",
- t->file[t->reentrancy-1], t->lineno[t->reentrancy-1], t->func[t->reentrancy-1], mutex_name);
- DO_THREAD_CRASH;
- }
-
- if (--t->reentrancy < 0) {
- __ast_mutex_logger("%s line %d (%s): mutex '%s' freed more times than we've locked!\n",
- filename, lineno, func, mutex_name);
- t->reentrancy = 0;
- }
-
- if (t->reentrancy < AST_MAX_REENTRANCY) {
- t->file[t->reentrancy] = NULL;
- t->lineno[t->reentrancy] = 0;
- t->func[t->reentrancy] = NULL;
- t->thread[t->reentrancy] = 0;
- }
+/*!
+ \brief Strip leading/trailing whitespace from a string.
+ \param s The string to be stripped (will be modified).
+ \return The stripped string.
- if ((res = pthread_cond_timedwait(cond, &t->mutex, abstime)) && (res != ETIMEDOUT)) {
- __ast_mutex_logger("%s line %d (%s): Error waiting on condition mutex '%s'\n",
- filename, lineno, func, strerror(res));
- DO_THREAD_CRASH;
- } else {
- if (t->reentrancy < AST_MAX_REENTRANCY) {
- t->file[t->reentrancy] = filename;
- t->lineno[t->reentrancy] = lineno;
- t->func[t->reentrancy] = func;
- t->thread[t->reentrancy] = pthread_self();
- t->reentrancy++;
- } else {
- __ast_mutex_logger("%s line %d (%s): '%s' really deep reentrancy!\n",
- filename, lineno, func, mutex_name);
- }
- }
+ This functions strips all leading and trailing whitespace
+ characters from the input string, and returns a pointer to
+ the resulting string. The string is modified in place.
+*/
+AST_INLINE_API(
+char *ast_strip(char *s),
+{
+ s = ast_skip_blanks(s);
+ if (s)
+ ast_trim_blanks(s);
+ return s;
+}
+)
- return res;
-}
-#define ast_mutex_destroy(a) __ast_pthread_mutex_destroy(__FILE__, __LINE__, __PRETTY_FUNCTION__, #a, a)
-#define ast_mutex_lock(a) __ast_pthread_mutex_lock(__FILE__, __LINE__, __PRETTY_FUNCTION__, #a, a)
-#define ast_mutex_unlock(a) __ast_pthread_mutex_unlock(__FILE__, __LINE__, __PRETTY_FUNCTION__, #a, a)
-#define ast_mutex_trylock(a) __ast_pthread_mutex_trylock(__FILE__, __LINE__, __PRETTY_FUNCTION__, #a, a)
-#define ast_cond_init(cond, attr) __ast_cond_init(__FILE__, __LINE__, __PRETTY_FUNCTION__, #cond, cond, attr)
-#define ast_cond_destroy(cond) __ast_cond_destroy(__FILE__, __LINE__, __PRETTY_FUNCTION__, #cond, cond)
-#define ast_cond_signal(cond) __ast_cond_signal(__FILE__, __LINE__, __PRETTY_FUNCTION__, #cond, cond)
-#define ast_cond_broadcast(cond) __ast_cond_broadcast(__FILE__, __LINE__, __PRETTY_FUNCTION__, #cond, cond)
-#define ast_cond_wait(cond, mutex) __ast_cond_wait(__FILE__, __LINE__, __PRETTY_FUNCTION__, #cond, #mutex, cond, mutex)
-#define ast_cond_timedwait(cond, mutex, time) __ast_cond_timedwait(__FILE__, __LINE__, __PRETTY_FUNCTION__, #cond, #mutex, cond, mutex, time)
+/* from config.h */
-#else /* !DEBUG_THREADS */
+struct ast_variable {
+ char *name;
+ char *value;
+ char *file;
+ int lineno;
+ int object; /*!< 0 for variable, 1 for object */
+ int blanklines; /*!< Number of blanklines following entry */
+ struct ast_comment *precomments;
+ struct ast_comment *sameline;
+ struct ast_variable *next;
+ char stuff[0];
+};
+static const char *ast_variable_retrieve(const struct ast_config *config, const char *category, const char *variable);
+static struct ast_config *config_text_file_load(const char *database, const char *table, const char *filename, struct ast_config *cfg, int withcomments, const char *suggested_include_file);
-typedef pthread_mutex_t ast_mutex_t;
+struct ast_config *localized_config_load_with_comments(const char *filename);
+static char *ast_category_browse(struct ast_config *config, const char *prev);
+static struct ast_variable *ast_variable_browse(const struct ast_config *config, const char *category);
+static void ast_variables_destroy(struct ast_variable *v);
+static void ast_config_destroy(struct ast_config *cfg);
+static struct ast_config_include *ast_include_new(struct ast_config *conf, const char *from_file, const char *included_file, int is_exec, const char *exec_file, int from_lineno, char *real_included_file_name, int real_included_file_name_size);
+static struct ast_config_include *ast_include_find(struct ast_config *conf, const char *included_file);
+void localized_ast_include_rename(struct ast_config *conf, const char *from_file, const char *to_file);
-#define AST_MUTEX_INIT_VALUE ((ast_mutex_t) PTHREAD_MUTEX_INIT_VALUE)
+static struct ast_variable *ast_variable_new(const char *name, const char *value, const char *filename);
-static inline int ast_mutex_init(ast_mutex_t *pmutex)
+static struct ast_variable *ast_variable_new(const char *name, const char *value, const char *filename)
{
- pthread_mutexattr_t attr;
+ struct ast_variable *variable;
+ int name_len = strlen(name) + 1;
- pthread_mutexattr_init(&attr);
- pthread_mutexattr_settype(&attr, AST_MUTEX_KIND);
+ if ((variable = ast_calloc(1, name_len + strlen(value) + 1 + strlen(filename) + 1 + sizeof(*variable)))) {
+ variable->name = variable->stuff;
+ variable->value = variable->stuff + name_len;
+ variable->file = variable->value + strlen(value) + 1;
+ strcpy(variable->name,name);
+ strcpy(variable->value,value);
+ strcpy(variable->file,filename);
+ }
- return pthread_mutex_init(pmutex, &attr);
+ return variable;
}
-#define ast_pthread_mutex_init(pmutex,a) pthread_mutex_init(pmutex,a)
-
-static inline int ast_mutex_unlock(ast_mutex_t *pmutex)
+static struct ast_config_include *ast_include_new(struct ast_config *conf, const char *from_file, const char *included_file, int is_exec, const char *exec_file, int from_lineno, char *real_included_file_name, int real_included_file_name_size)
{
- return pthread_mutex_unlock(pmutex);
+ /* a file should be included ONCE. Otherwise, if one of the instances is changed,
+ then all be changed. -- how do we know to include it? -- Handling modified
+ instances is possible, I'd have
+ to create a new master for each instance. */
+ struct ast_config_include *inc;
+
+ inc = ast_include_find(conf, included_file);
+ if (inc)
+ {
+ inc->inclusion_count++;
+ snprintf(real_included_file_name, real_included_file_name_size, "%s~~%d", included_file, inc->inclusion_count);
+ ast_log(LOG_WARNING,"'%s', line %d: Same File included more than once! This data will be saved in %s if saved back to disk.\n", from_file, from_lineno, real_included_file_name);
+ } else
+ *real_included_file_name = 0;
+
+ inc = ast_calloc(1,sizeof(struct ast_config_include));
+ inc->include_location_file = ast_strdup(from_file);
+ inc->include_location_lineno = from_lineno;
+ if (!ast_strlen_zero(real_included_file_name))
+ inc->included_file = ast_strdup(real_included_file_name);
+ else
+ inc->included_file = ast_strdup(included_file);
+
+ inc->exec = is_exec;
+ if (is_exec)
+ inc->exec_file = ast_strdup(exec_file);
+
+ /* attach this new struct to the conf struct */
+ inc->next = conf->includes;
+ conf->includes = inc;
+
+ return inc;
}
-static inline int ast_mutex_destroy(ast_mutex_t *pmutex)
+void localized_ast_include_rename(struct ast_config *conf, const char *from_file, const char *to_file)
{
- return pthread_mutex_destroy(pmutex);
+ struct ast_config_include *incl;
+ struct ast_category *cat;
+ struct ast_variable *v;
+
+ int from_len = strlen(from_file);
+ int to_len = strlen(to_file);
+
+ if (strcmp(from_file, to_file) == 0) /* no use wasting time if the name is the same */
+ return;
+
+ /* the manager code allows you to read in one config file, then
+ write it back out under a different name. But, the new arrangement
+ ties output lines to the file name. So, before you try to write
+ the config file to disk, better riffle thru the data and make sure
+ the file names are changed.
+ */
+ /* file names are on categories, includes (of course), and on variables. So,
+ traverse all this and swap names */
+
+ for (incl = conf->includes; incl; incl=incl->next) {
+ if (strcmp(incl->include_location_file,from_file) == 0) {
+ if (from_len >= to_len)
+ strcpy(incl->include_location_file, to_file);
+ else {
+ free(incl->include_location_file);
+ incl->include_location_file = strdup(to_file);
+ }
+ }
+ }
+ for (cat = conf->root; cat; cat = cat->next) {
+ if (strcmp(cat->file,from_file) == 0) {
+ if (from_len >= to_len)
+ strcpy(cat->file, to_file);
+ else {
+ free(cat->file);
+ cat->file = strdup(to_file);
+ }
+ }
+ for (v = cat->root; v; v = v->next) {
+ if (strcmp(v->file,from_file) == 0) {
+ if (from_len >= to_len)
+ strcpy(v->file, to_file);
+ else {
+ free(v->file);
+ v->file = strdup(to_file);
+ }
+ }
+ }
+ }
}
-static inline int ast_mutex_lock(ast_mutex_t *pmutex)
+static struct ast_config_include *ast_include_find(struct ast_config *conf, const char *included_file)
{
- __MTX_PROF(pmutex);
+ struct ast_config_include *x;
+ for (x=conf->includes;x;x=x->next)
+ {
+ if (strcmp(x->included_file,included_file) == 0)
+ return x;
+ }
+ return 0;
}
-static inline int ast_mutex_trylock(ast_mutex_t *pmutex)
-{
- return pthread_mutex_trylock(pmutex);
-}
-typedef pthread_cond_t ast_cond_t;
+static void ast_variable_append(struct ast_category *category, struct ast_variable *variable);
-static inline int ast_cond_init(ast_cond_t *cond, pthread_condattr_t *cond_attr)
+static void ast_variable_append(struct ast_category *category, struct ast_variable *variable)
{
- return pthread_cond_init(cond, cond_attr);
+ if (!variable)
+ return;
+ if (category->last)
+ category->last->next = variable;
+ else
+ category->root = variable;
+ category->last = variable;
+ while (category->last->next)
+ category->last = category->last->next;
}
-static inline int ast_cond_signal(ast_cond_t *cond)
-{
- return pthread_cond_signal(cond);
-}
+static struct ast_category *category_get(const struct ast_config *config, const char *category_name, int ignored);
-static inline int ast_cond_broadcast(ast_cond_t *cond)
+static struct ast_category *category_get(const struct ast_config *config, const char *category_name, int ignored)
{
- return pthread_cond_broadcast(cond);
-}
+ struct ast_category *cat;
-static inline int ast_cond_destroy(ast_cond_t *cond)
-{
- return pthread_cond_destroy(cond);
+ /* try exact match first, then case-insensitive match */
+ for (cat = config->root; cat; cat = cat->next) {
+ if (cat->name == category_name && (ignored || !cat->ignored))
+ return cat;
+ }
+
+ for (cat = config->root; cat; cat = cat->next) {
+ if (!strcasecmp(cat->name, category_name) && (ignored || !cat->ignored))
+ return cat;
+ }
+
+ return NULL;
}
-static inline int ast_cond_wait(ast_cond_t *cond, ast_mutex_t *t)
+static struct ast_category *ast_category_get(const struct ast_config *config, const char *category_name)
{
- return pthread_cond_wait(cond, t);
+ return category_get(config, category_name, 0);
}
-static inline int ast_cond_timedwait(ast_cond_t *cond, ast_mutex_t *t, const struct timespec *abstime)
+static struct ast_variable *ast_variable_browse(const struct ast_config *config, const char *category)
{
- return pthread_cond_timedwait(cond, t, abstime);
-}
+ struct ast_category *cat = NULL;
-#endif /* !DEBUG_THREADS */
+ if (category && config->last_browse && (config->last_browse->name == category))
+ cat = config->last_browse;
+ else
+ cat = ast_category_get(config, category);
-#if defined(AST_MUTEX_INIT_W_CONSTRUCTORS)
-/* If AST_MUTEX_INIT_W_CONSTRUCTORS is defined, use file scope
- constructors/destructors to create/destroy mutexes. */
-#define __AST_MUTEX_DEFINE(scope, mutex) \
- scope ast_mutex_t mutex = AST_MUTEX_INIT_VALUE; \
-static void __attribute__ ((constructor)) init_##mutex(void) \
-{ \
- ast_mutex_init(&mutex); \
-} \
-static void __attribute__ ((destructor)) fini_##mutex(void) \
-{ \
- ast_mutex_destroy(&mutex); \
+ return (cat) ? cat->root : NULL;
}
-#else /* !AST_MUTEX_INIT_W_CONSTRUCTORS */
-/* By default, use static initialization of mutexes. */
-#define __AST_MUTEX_DEFINE(scope, mutex) \
- scope ast_mutex_t mutex = AST_MUTEX_INIT_VALUE
-#endif /* AST_MUTEX_INIT_W_CONSTRUCTORS */
-
-#define pthread_mutex_t use_ast_mutex_t_instead_of_pthread_mutex_t
-#define pthread_mutex_lock use_ast_mutex_lock_instead_of_pthread_mutex_lock
-#define pthread_mutex_unlock use_ast_mutex_unlock_instead_of_pthread_mutex_unlock
-#define pthread_mutex_trylock use_ast_mutex_trylock_instead_of_pthread_mutex_trylock
-#define pthread_mutex_init use_ast_mutex_init_instead_of_pthread_mutex_init
-#define pthread_mutex_destroy use_ast_mutex_destroy_instead_of_pthread_mutex_destroy
-#define pthread_cond_t use_ast_cond_t_instead_of_pthread_cond_t
-#define pthread_cond_init use_ast_cond_init_instead_of_pthread_cond_init
-#define pthread_cond_destroy use_ast_cond_destroy_instead_of_pthread_cond_destroy
-#define pthread_cond_signal use_ast_cond_signal_instead_of_pthread_cond_signal
-#define pthread_cond_broadcast use_ast_cond_broadcast_instead_of_pthread_cond_broadcast
-#define pthread_cond_wait use_ast_cond_wait_instead_of_pthread_cond_wait
-#define pthread_cond_timedwait use_ast_cond_timedwait_instead_of_pthread_cond_timedwait
-
-#define AST_MUTEX_DEFINE_STATIC(mutex) __AST_MUTEX_DEFINE(static, mutex)
-
-#define AST_MUTEX_INITIALIZER __use_AST_MUTEX_DEFINE_STATIC_rather_than_AST_MUTEX_INITIALIZER__
-
-#define gethostbyname __gethostbyname__is__not__reentrant__use__ast_gethostbyname__instead__
-#ifndef __linux__
-#define pthread_create __use_ast_pthread_create_instead__
-#endif
-
-typedef pthread_rwlock_t ast_rwlock_t;
-
-static inline int ast_rwlock_init(ast_rwlock_t *prwlock)
+static const char *ast_variable_retrieve(const struct ast_config *config, const char *category, const char *variable)
{
- pthread_rwlockattr_t attr;
+ struct ast_variable *v;
- pthread_rwlockattr_init(&attr);
+ if (category) {
+ for (v = ast_variable_browse(config, category); v; v = v->next) {
+ if (!strcasecmp(variable, v->name))
+ return v->value;
+ }
+ } else {
+ struct ast_category *cat;
-#ifdef HAVE_PTHREAD_RWLOCK_PREFER_WRITER_NP
- pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NP);
-#endif
+ for (cat = config->root; cat; cat = cat->next)
+ for (v = cat->root; v; v = v->next)
+ if (!strcasecmp(variable, v->name))
+ return v->value;
+ }
- return pthread_rwlock_init(prwlock, &attr);
+ return NULL;
}
-static inline int ast_rwlock_destroy(ast_rwlock_t *prwlock)
+static struct ast_variable *variable_clone(const struct ast_variable *old)
{
- return pthread_rwlock_destroy(prwlock);
-}
+ struct ast_variable *new = ast_variable_new(old->name, old->value, old->file);
+
+ if (new) {
+ new->lineno = old->lineno;
+ new->object = old->object;
+ new->blanklines = old->blanklines;
+ /* TODO: clone comments? */
+ }
-static inline int ast_rwlock_unlock(ast_rwlock_t *prwlock)
-{
- return pthread_rwlock_unlock(prwlock);
+ return new;
}
-
-static inline int ast_rwlock_rdlock(ast_rwlock_t *prwlock)
+
+static void ast_variables_destroy(struct ast_variable *v)
{
- return pthread_rwlock_rdlock(prwlock);
-}
+ struct ast_variable *vn;
-static inline int ast_rwlock_tryrdlock(ast_rwlock_t *prwlock)
-{
- return pthread_rwlock_tryrdlock(prwlock);
+ while (v) {
+ vn = v;
+ v = v->next;
+ free(vn);
+ }
}
-static inline int ast_rwlock_wrlock(ast_rwlock_t *prwlock)
+static void ast_includes_destroy(struct ast_config_include *incls)
{
- return pthread_rwlock_wrlock(prwlock);
+ struct ast_config_include *incl,*inclnext;
+
+ for (incl=incls; incl; incl = inclnext) {
+ inclnext = incl->next;
+ if (incl->include_location_file)
+ free(incl->include_location_file);
+ if (incl->exec_file)
+ free(incl->exec_file);
+ if (incl->included_file)
+ free(incl->included_file);
+ free(incl);
+ }
}
-static inline int ast_rwlock_trywrlock(ast_rwlock_t *prwlock)
+static void ast_config_destroy(struct ast_config *cfg)
{
- return pthread_rwlock_trywrlock(prwlock);
-}
+ struct ast_category *cat, *catn;
-/* Statically declared read/write locks */
+ if (!cfg)
+ return;
-#ifndef HAVE_PTHREAD_RWLOCK_INITIALIZER
-#define __AST_RWLOCK_DEFINE(scope, rwlock) \
- scope ast_rwlock_t rwlock; \
-static void __attribute__ ((constructor)) init_##rwlock(void) \
-{ \
- ast_rwlock_init(&rwlock); \
-} \
-static void __attribute__ ((destructor)) fini_##rwlock(void) \
-{ \
- ast_rwlock_destroy(&rwlock); \
+ ast_includes_destroy(cfg->includes);
+
+ cat = cfg->root;
+ while (cat) {
+ ast_variables_destroy(cat->root);
+ catn = cat;
+ cat = cat->next;
+ free(catn);
+ }
+ free(cfg);
}
-#else
-#define AST_RWLOCK_INIT_VALUE PTHREAD_RWLOCK_INITIALIZER
-#define __AST_RWLOCK_DEFINE(scope, rwlock) \
- scope ast_rwlock_t rwlock = AST_RWLOCK_INIT_VALUE
-#endif
-#define AST_RWLOCK_DEFINE_STATIC(rwlock) __AST_RWLOCK_DEFINE(static, rwlock)
-/*
- * Initial support for atomic instructions.
- * For platforms that have it, use the native cpu instruction to
- * implement them. For other platforms, resort to a 'slow' version
- * (defined in utils.c) that protects the atomic instruction with
- * a single lock.
- * The slow versions is always available, for testing purposes,
- * as ast_atomic_fetchadd_int_slow()
- */
+/* options.h declars ast_options extern; I need it static? */
-#if defined(HAVE_OSX_ATOMICS)
-#include "libkern/OSAtomic.h"
-#endif
+#define AST_CACHE_DIR_LEN 512
+#define AST_FILENAME_MAX 80
-/*! \brief Atomically add v to *p and return * the previous value of *p.
- * This can be used to handle reference counts, and the return value
- * can be used to generate unique identifiers.
- */
+/*! \ingroup main_options */
+enum ast_option_flags {
+ /*! Allow \#exec in config files */
+ AST_OPT_FLAG_EXEC_INCLUDES = (1 << 0),
+ /*! Do not fork() */
+ AST_OPT_FLAG_NO_FORK = (1 << 1),
+ /*! Keep quiet */
+ AST_OPT_FLAG_QUIET = (1 << 2),
+ /*! Console mode */
+ AST_OPT_FLAG_CONSOLE = (1 << 3),
+ /*! Run in realtime Linux priority */
+ AST_OPT_FLAG_HIGH_PRIORITY = (1 << 4),
+ /*! Initialize keys for RSA authentication */
+ AST_OPT_FLAG_INIT_KEYS = (1 << 5),
+ /*! Remote console */
+ AST_OPT_FLAG_REMOTE = (1 << 6),
+ /*! Execute an asterisk CLI command upon startup */
+ AST_OPT_FLAG_EXEC = (1 << 7),
+ /*! Don't use termcap colors */
+ AST_OPT_FLAG_NO_COLOR = (1 << 8),
+ /*! Are we fully started yet? */
+ AST_OPT_FLAG_FULLY_BOOTED = (1 << 9),
+ /*! Trascode via signed linear */
+ AST_OPT_FLAG_TRANSCODE_VIA_SLIN = (1 << 10),
+ /*! Enable priority jumping in applications */
+ AST_OPT_FLAG_PRIORITY_JUMPING = (1 << 11),
+ /*! Dump core on a seg fault */
+ AST_OPT_FLAG_DUMP_CORE = (1 << 12),
+ /*! Cache sound files */
+ AST_OPT_FLAG_CACHE_RECORD_FILES = (1 << 13),
+ /*! Display timestamp in CLI verbose output */
+ AST_OPT_FLAG_TIMESTAMP = (1 << 14),
+ /*! Override config */
+ AST_OPT_FLAG_OVERRIDE_CONFIG = (1 << 15),
+ /*! Reconnect */
+ AST_OPT_FLAG_RECONNECT = (1 << 16),
+ /*! Transmit Silence during Record() */
+ AST_OPT_FLAG_TRANSMIT_SILENCE = (1 << 17),
+ /*! Suppress some warnings */
+ AST_OPT_FLAG_DONT_WARN = (1 << 18),
+ /*! End CDRs before the 'h' extension */
+ AST_OPT_FLAG_END_CDR_BEFORE_H_EXTEN = (1 << 19),
+ /*! Use Zaptel Timing for generators if available */
+ AST_OPT_FLAG_INTERNAL_TIMING = (1 << 20),
+ /*! Always fork, even if verbose or debug settings are non-zero */
+ AST_OPT_FLAG_ALWAYS_FORK = (1 << 21),
+ /*! Disable log/verbose output to remote consoles */
+ AST_OPT_FLAG_MUTE = (1 << 22)
+};
-#if defined(HAVE_GCC_ATOMICS)
-AST_INLINE_API(int ast_atomic_fetchadd_int(volatile int *p, int v),
-{
- return __sync_fetch_and_add(p, v);
-})
-#elif defined(HAVE_OSX_ATOMICS) && (SIZEOF_INT == 4)
-AST_INLINE_API(int ast_atomic_fetchadd_int(volatile int *p, int v),
-{
- return OSAtomicAdd32(v, (int32_t *) p);
-})
-#elif defined(HAVE_OSX_ATOMICS) && (SIZEOF_INT == 8)
-AST_INLINE_API(int ast_atomic_fetchadd_int(volatile int *p, int v),
-{
- return OSAtomicAdd64(v, (int64_t *) p);
-#elif defined (__i386__) || defined(__x86_64__)
-AST_INLINE_API(int ast_atomic_fetchadd_int(volatile int *p, int v),
-{
- __asm __volatile (
- " lock xaddl %0, %1 ; "
- : "+r" (v), /* 0 (result) */
- "=m" (*p) /* 1 */
- : "m" (*p)); /* 2 */
- return (v);
-})
-#else
-static int ast_atomic_fetchadd_int_slow(volatile int *p, int v)
-{
- int ret;
- ret = *p;
- *p += v;
- return ret;
-}
-AST_INLINE_API(int ast_atomic_fetchadd_int(volatile int *p, int v),
-{
- return ast_atomic_fetchadd_int_slow(p, v);
-})
-#endif
+/*! These are the options that set by default when Asterisk starts */
+#define AST_DEFAULT_OPTIONS AST_OPT_FLAG_TRANSCODE_VIA_SLIN
-/*! \brief decrement *p by 1 and return true if the variable has reached 0.
- * Useful e.g. to check if a refcount has reached 0.
- */
-#if defined(HAVE_GCC_ATOMICS)
-AST_INLINE_API(int ast_atomic_dec_and_test(volatile int *p),
-{
- return __sync_sub_and_fetch(p, 1) == 0;
-})
-#elif defined(HAVE_OSX_ATOMICS) && (SIZEOF_INT == 4)
-AST_INLINE_API(int ast_atomic_dec_and_test(volatile int *p),
-{
- return OSAtomicAdd32( -1, (int32_t *) p) == 0;
-})
-#elif defined(HAVE_OSX_ATOMICS) && (SIZEOF_INT == 8)
-AST_INLINE_API(int ast_atomic_dec_and_test(volatile int *p),
-{
- return OSAtomicAdd64( -1, (int64_t *) p) == 0;
-#else
-AST_INLINE_API(int ast_atomic_dec_and_test(volatile int *p),
-{
- int a = ast_atomic_fetchadd_int(p, -1);
- return a == 1; /* true if the value is 0 now (so it was 1 previously) */
-})
-#endif
+#define ast_opt_exec_includes ast_test_flag(&ast_options, AST_OPT_FLAG_EXEC_INCLUDES)
+#define ast_opt_no_fork ast_test_flag(&ast_options, AST_OPT_FLAG_NO_FORK)
+#define ast_opt_quiet ast_test_flag(&ast_options, AST_OPT_FLAG_QUIET)
+#define ast_opt_console ast_test_flag(&ast_options, AST_OPT_FLAG_CONSOLE)
+#define ast_opt_high_priority ast_test_flag(&ast_options, AST_OPT_FLAG_HIGH_PRIORITY)
+#define ast_opt_init_keys ast_test_flag(&ast_options, AST_OPT_FLAG_INIT_KEYS)
+#define ast_opt_remote ast_test_flag(&ast_options, AST_OPT_FLAG_REMOTE)
+#define ast_opt_exec ast_test_flag(&ast_options, AST_OPT_FLAG_EXEC)
+#define ast_opt_no_color ast_test_flag(&ast_options, AST_OPT_FLAG_NO_COLOR)
+#define ast_fully_booted ast_test_flag(&ast_options, AST_OPT_FLAG_FULLY_BOOTED)
+#define ast_opt_transcode_via_slin ast_test_flag(&ast_options, AST_OPT_FLAG_TRANSCODE_VIA_SLIN)
+#define ast_opt_priority_jumping ast_test_flag(&ast_options, AST_OPT_FLAG_PRIORITY_JUMPING)
+#define ast_opt_dump_core ast_test_flag(&ast_options, AST_OPT_FLAG_DUMP_CORE)
+#define ast_opt_cache_record_files ast_test_flag(&ast_options, AST_OPT_FLAG_CACHE_RECORD_FILES)
+#define ast_opt_timestamp ast_test_flag(&ast_options, AST_OPT_FLAG_TIMESTAMP)
+#define ast_opt_override_config ast_test_flag(&ast_options, AST_OPT_FLAG_OVERRIDE_CONFIG)
+#define ast_opt_reconnect ast_test_flag(&ast_options, AST_OPT_FLAG_RECONNECT)
+#define ast_opt_transmit_silence ast_test_flag(&ast_options, AST_OPT_FLAG_TRANSMIT_SILENCE)
+#define ast_opt_dont_warn ast_test_flag(&ast_options, AST_OPT_FLAG_DONT_WARN)
+#define ast_opt_end_cdr_before_h_exten ast_test_flag(&ast_options, AST_OPT_FLAG_END_CDR_BEFORE_H_EXTEN)
+#define ast_opt_internal_timing ast_test_flag(&ast_options, AST_OPT_FLAG_INTERNAL_TIMING)
+#define ast_opt_always_fork ast_test_flag(&ast_options, AST_OPT_FLAG_ALWAYS_FORK)
+#define ast_opt_mute ast_test_flag(&ast_options, AST_OPT_FLAG_MUTE)
-#ifndef DEBUG_CHANNEL_LOCKS
-/*! \brief Lock a channel. If DEBUG_CHANNEL_LOCKS is defined
- in the Makefile, print relevant output for debugging */
-#define ast_channel_lock(x) ast_mutex_lock(&x->lock)
-/*! \brief Unlock a channel. If DEBUG_CHANNEL_LOCKS is defined
- in the Makefile, print relevant output for debugging */
-#define ast_channel_unlock(x) ast_mutex_unlock(&x->lock)
-/*! \brief Try locking a channel. If DEBUG_CHANNEL_LOCKS is defined
- in the Makefile, print relevant output for debugging */
-#define ast_channel_trylock(x) ast_mutex_trylock(&x->lock)
-#else
+/* IN CONFLICT: extern int option_verbose; */
+/* IN CONFLICT: extern int option_debug; */ /*!< Debugging */
+extern int option_maxcalls; /*!< Maximum number of simultaneous channels */
+extern double option_maxload;
+extern char defaultlanguage[];
-/*! \brief Lock AST channel (and print debugging output)
-\note You need to enable DEBUG_CHANNEL_LOCKS for this function */
-int ast_channel_lock(struct ast_channel *chan);
+extern time_t ast_startuptime;
+extern time_t ast_lastreloadtime;
+extern pid_t ast_mainpid;
-/*! \brief Unlock AST channel (and print debugging output)
-\note You need to enable DEBUG_CHANNEL_LOCKS for this function
-*/
-int ast_channel_unlock(struct ast_channel *chan);
+extern char record_cache_dir[AST_CACHE_DIR_LEN];
+extern char debug_filename[AST_FILENAME_MAX];
+
+extern int ast_language_is_prefix;
-/*! \brief Lock AST channel (and print debugging output)
-\note You need to enable DEBUG_CHANNEL_LOCKS for this function */
-int ast_channel_trylock(struct ast_channel *chan);
-#endif
/* linkedlists.h */