diff --git a/apps/app_dial.c b/apps/app_dial.c index b3d0a05bad..60b1611c78 100644 --- a/apps/app_dial.c +++ b/apps/app_dial.c @@ -2084,9 +2084,7 @@ static int load_module(void) int res; struct ast_context *con; - con = ast_context_find("app_dial_gosub_virtual_context"); - if (!con) - con = ast_context_create(NULL, "app_dial_gosub_virtual_context", "app_dial"); + con = ast_context_find_or_create(NULL, NULL, "app_dial_gosub_virtual_context", "app_dial"); if (!con) ast_log(LOG_ERROR, "Dial virtual context 'app_dial_gosub_virtual_context' does not exist and unable to create\n"); else diff --git a/apps/app_meetme.c b/apps/app_meetme.c index cb5301f2fb..418d56b368 100644 --- a/apps/app_meetme.c +++ b/apps/app_meetme.c @@ -5298,7 +5298,7 @@ static int sla_build_trunk(struct ast_config *cfg, const char *cat) if (!ast_strlen_zero(trunk->autocontext)) { struct ast_context *context; - context = ast_context_find_or_create(NULL, trunk->autocontext, sla_registrar); + context = ast_context_find_or_create(NULL, NULL, trunk->autocontext, sla_registrar); if (!context) { ast_log(LOG_ERROR, "Failed to automatically find or create " "context '%s' for SLA!\n", trunk->autocontext); @@ -5435,7 +5435,7 @@ static int sla_build_station(struct ast_config *cfg, const char *cat) if (!ast_strlen_zero(station->autocontext)) { struct ast_context *context; struct sla_trunk_ref *trunk_ref; - context = ast_context_find_or_create(NULL, station->autocontext, sla_registrar); + context = ast_context_find_or_create(NULL, NULL, station->autocontext, sla_registrar); if (!context) { ast_log(LOG_ERROR, "Failed to automatically find or create " "context '%s' for SLA!\n", station->autocontext); diff --git a/apps/app_queue.c b/apps/app_queue.c index 9e5f8736f4..acf259c83a 100644 --- a/apps/app_queue.c +++ b/apps/app_queue.c @@ -6368,9 +6368,7 @@ static int load_module(void) if (!reload_queues(0)) return AST_MODULE_LOAD_DECLINE; - con = ast_context_find("app_queue_gosub_virtual_context"); - if (!con) - con = ast_context_create(NULL, "app_queue_gosub_virtual_context", "app_queue"); + con = ast_context_find_or_create(NULL, NULL, "app_queue_gosub_virtual_context", "app_queue"); if (!con) ast_log(LOG_ERROR, "Queue virtual context 'app_queue_gosub_virtual_context' does not exist and unable to create\n"); else diff --git a/apps/app_stack.c b/apps/app_stack.c index 3dc06904fd..f770808495 100644 --- a/apps/app_stack.c +++ b/apps/app_stack.c @@ -520,7 +520,7 @@ static int load_module(void) } if (agi_loaded) { - con = ast_context_find_or_create(NULL, "app_stack_gosub_virtual_context", "app_stack"); + con = ast_context_find_or_create(NULL, NULL, "app_stack_gosub_virtual_context", "app_stack"); if (!con) { ast_log(LOG_ERROR, "Virtual context 'app_stack_gosub_virtual_context' does not exist and unable to create\n"); return AST_MODULE_LOAD_DECLINE; diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c index 8fb226c4b5..de0073ef84 100644 --- a/channels/chan_iax2.c +++ b/channels/chan_iax2.c @@ -11023,8 +11023,7 @@ static int set_config(char *config_file, int reload) } else if (!strcasecmp(v->name, "regcontext")) { ast_copy_string(regcontext, v->value, sizeof(regcontext)); /* Create context if it doesn't exist already */ - if (!ast_context_find(regcontext)) - ast_context_create(NULL, regcontext, "IAX2"); + ast_context_find_or_create(NULL, NULL, regcontext, "IAX2"); } else if (!strcasecmp(v->name, "tos")) { if (ast_str2tos(v->value, &tos)) ast_log(LOG_WARNING, "Invalid tos value at line %d, refer to QoS documentation\n", v->lineno); diff --git a/channels/chan_sip.c b/channels/chan_sip.c index 90b15e1619..79b1e18899 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -3441,6 +3441,7 @@ static void register_peer_exten(struct sip_peer *peer, int onoff) { char multi[256]; char *stringp, *ext, *context; + struct pbx_find_info q = { .stacklen = 0 }; /* XXX note that global_regcontext is both a global 'enable' flag and * the name of the global regexten context, if not specified @@ -3461,11 +3462,12 @@ static void register_peer_exten(struct sip_peer *peer, int onoff) } else { context = global_regcontext; } - if (onoff) + if (onoff) { ast_add_extension(context, 1, ext, 1, NULL, NULL, "Noop", ast_strdup(peer->name), ast_free_ptr, "SIP"); - else + } else if (pbx_find_extension(NULL, NULL, &q, context, ext, 1, NULL, "", E_MATCH)) { ast_context_remove_extension(context, ext, 1, NULL); + } } } @@ -20616,8 +20618,7 @@ static int reload_config(enum channelreloadreason reason) /* Create contexts if they don't exist already */ while ((context = strsep(&stringp, "&"))) { ast_copy_string(used_context, context, sizeof(used_context)); - if (!ast_context_find(context)) - ast_context_create(NULL, context, "SIP"); + ast_context_find_or_create(NULL, NULL, context, "SIP"); } ast_copy_string(global_regcontext, v->value, sizeof(global_regcontext)); } else if (!strcasecmp(v->name, "regextenonqualify")) { diff --git a/channels/chan_skinny.c b/channels/chan_skinny.c index 0c9827baf7..8dab03f9fb 100644 --- a/channels/chan_skinny.c +++ b/channels/chan_skinny.c @@ -5913,8 +5913,7 @@ static int reload_config(void) /* Create contexts if they don't exist already */ while ((context = strsep(&stringp, "&"))) { ast_copy_string(used_context, context, sizeof(used_context)); - if (!ast_context_find(context)) - ast_context_create(NULL, context, "Skinny"); + ast_context_find_or_create(NULL, NULL, context, "Skinny"); } ast_copy_string(regcontext, v->value, sizeof(regcontext)); } else if (!strcasecmp(v->name, "dateformat")) { diff --git a/include/asterisk/pbx.h b/include/asterisk/pbx.h index d8fcbae07c..574a4b3125 100644 --- a/include/asterisk/pbx.h +++ b/include/asterisk/pbx.h @@ -25,6 +25,7 @@ #include "asterisk/sched.h" #include "asterisk/chanvars.h" +#include "asterisk/hashtab.h" #if defined(__cplusplus) || defined(c_plusplus) extern "C" { @@ -166,46 +167,37 @@ struct ast_app *pbx_findapp(const char *app); */ int pbx_exec(struct ast_channel *c, struct ast_app *app, void *data); -/*! - * \brief Register a new context - * - * \param extcontexts pointer to the ast_context structure pointer - * \param name name of the new context - * \param registrar registrar of the context - * - * This will first search for a context with your name. If it exists already, it will not - * create a new one. If it does not exist, it will create a new one with the given name - * and registrar. - * - * \return NULL on failure, and an ast_context structure on success - */ -struct ast_context *ast_context_create(struct ast_context **extcontexts, const char *name, const char *registrar); - /*! * \brief Register a new context or find an existing one * * \param extcontexts pointer to the ast_context structure pointer + * \param exttable pointer to the hashtable that contains all the elements in extcontexts * \param name name of the new context * \param registrar registrar of the context * + * This function allows you to play in two environments: the global contexts (active dialplan) + * or an external context set of your choosing. To act on the external set, make sure extcontexts + * and exttable are set; for the globals, make sure both extcontexts and exttable are NULL. + * * This will first search for a context with your name. If it exists already, it will not * create a new one. If it does not exist, it will create a new one with the given name * and registrar. * * \return NULL on failure, and an ast_context structure on success */ -struct ast_context *ast_context_find_or_create(struct ast_context **extcontexts, const char *name, const char *registrar); +struct ast_context *ast_context_find_or_create(struct ast_context **extcontexts, struct ast_hashtab *exttable, const char *name, const char *registrar); /*! * \brief Merge the temporary contexts into a global contexts list and delete from the * global list the ones that are being added * - * \param extcontexts pointer to the ast_context structure pointer + * \param extcontexts pointer to the ast_context structure + * \param exttable pointer to the ast_hashtab structure that contains all the elements in extcontexts * \param registrar of the context; if it's set the routine will delete all contexts * that belong to that registrar; if NULL only the contexts that are specified * in extcontexts */ -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); /*! * \brief Destroy a context (matches the specified context (or ANY context if NULL) @@ -775,6 +767,8 @@ const char *ast_get_include_name(struct ast_include *include); const char *ast_get_ignorepat_name(struct ast_ignorepat *ip); const char *ast_get_switch_name(struct ast_sw *sw); const char *ast_get_switch_data(struct ast_sw *sw); +int ast_get_switch_eval(struct ast_sw *sw); + /*! @} */ /*! @name Other Extension stuff */ @@ -997,8 +991,18 @@ struct ast_exten *pbx_find_extension(struct ast_channel *chan, struct ast_context *bypass, struct pbx_find_info *q, const char *context, const char *exten, int priority, const char *label, const char *callerid, enum ext_match_t action); + + +/* every time a write lock is obtained for contexts, + a counter is incremented. You can check this via the + following func */ + +int ast_wrlock_contexts_version(void); +/* hashtable functions for contexts */ +int ast_hashtab_compare_contexts(const void *ah_a, const void *ah_b); +unsigned int ast_hashtab_hash_contexts(const void *obj); #if defined(__cplusplus) || defined(c_plusplus) } diff --git a/include/asterisk/pval.h b/include/asterisk/pval.h index 2dff24ddca..acc9b30e1c 100644 --- a/include/asterisk/pval.h +++ b/include/asterisk/pval.h @@ -1,6 +1,7 @@ #ifndef _ASTERISK_PVAL_H #define _ASTERISK_PVAL_H +/* whatever includes this, better include asterisk/lock.h and asterisk/hashtab.h */ typedef enum { @@ -143,7 +144,7 @@ void destroy_extensions(struct ael_extension *exten); static void gen_prios(struct ael_extension *exten, char *label, pval *statement, struct ael_extension *mother_exten, struct ast_context *context ); */ void set_priorities(struct ael_extension *exten); void add_extensions(struct ael_extension *exten); -void ast_compile_ael2(struct ast_context **local_contexts, struct pval *root); +void ast_compile_ael2(struct ast_context **local_contexts, struct ast_hashtab *local_table, struct pval *root); void destroy_pval(pval *item); void destroy_pval_item(pval *item); int is_float(char *arg ); diff --git a/main/features.c b/main/features.c index eca00f65de..6f3b31fb0d 100644 --- a/main/features.c +++ b/main/features.c @@ -490,9 +490,7 @@ static int park_call_full(struct ast_channel *chan, struct ast_channel *peer, in ast_adsi_unload_session(peer); } - con = ast_context_find(parking_con); - if (!con) - con = ast_context_create(NULL, parking_con, registrar); + con = ast_context_find_or_create(NULL, NULL, parking_con, registrar); if (!con) /* Still no context? Bad */ ast_log(LOG_ERROR, "Parking context '%s' does not exist and unable to create\n", parking_con); /* Tell the peer channel the number of the parking space */ @@ -2205,12 +2203,9 @@ static void *do_parking_thread(void *ignore) if (peername_flat[i] == '/') peername_flat[i]= '0'; } - con = ast_context_find(parking_con_dial); - if (!con) { - con = ast_context_create(NULL, parking_con_dial, registrar); - if (!con) - ast_log(LOG_ERROR, "Parking dial context '%s' does not exist and unable to create\n", parking_con_dial); - } + con = ast_context_find_or_create(NULL, NULL, parking_con_dial, registrar); + if (!con) + ast_log(LOG_ERROR, "Parking dial context '%s' does not exist and unable to create\n", parking_con_dial); if (con) { char returnexten[AST_MAX_EXTENSION]; struct ast_datastore *features_datastore; @@ -2799,7 +2794,7 @@ static int load_config(void) ast_debug(1, "Removed old parking extension %s@%s\n", old_parking_ext, old_parking_con); } - if (!(con = ast_context_find(parking_con)) && !(con = ast_context_create(NULL, parking_con, registrar))) { + if (!(con = ast_context_find_or_create(NULL, NULL, parking_con, registrar))) { ast_log(LOG_ERROR, "Parking context '%s' does not exist and unable to create\n", parking_con); return -1; } diff --git a/main/pbx.c b/main/pbx.c index b847e90315..7de2e46f60 100644 --- a/main/pbx.c +++ b/main/pbx.c @@ -48,6 +48,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/cdr.h" #include "asterisk/config.h" #include "asterisk/term.h" +#include "asterisk/time.h" #include "asterisk/manager.h" #include "asterisk/ast_expr.h" #include "asterisk/linkedlists.h" @@ -142,8 +143,8 @@ struct ast_exten { void *data; /*!< Data to use (arguments) */ void (*datad)(void *); /*!< Data destructor */ struct ast_exten *peer; /*!< Next higher priority with our extension */ - struct ast_hashtab *peer_tree; /*!< Priorities list in tree form -- only on the head of the peer list */ - struct ast_hashtab *peer_label_tree; /*!< labeled priorities in the peer list -- only on the head of the peer list */ + struct ast_hashtab *peer_table; /*!< Priorities list in hashtab form -- only on the head of the peer list */ + struct ast_hashtab *peer_label_table; /*!< labeled priorities in the peers -- only on the head of the peer list */ const char *registrar; /*!< Registrar */ struct ast_exten *next; /*!< Extension with a greater ID */ char stuff[0]; @@ -204,12 +205,13 @@ struct scoreboard /* make sure all fields are 0 before calling new_find_extensi struct ast_context { ast_rwlock_t lock; /*!< A lock to prevent multiple threads from clobbering the context */ struct ast_exten *root; /*!< The root of the list of extensions */ - struct ast_hashtab *root_tree; /*!< For exact matches on the extensions in the pattern tree, and for traversals of the pattern_tree */ + struct ast_hashtab *root_table; /*!< For exact matches on the extensions in the pattern tree, and for traversals of the pattern_tree */ struct match_char *pattern_tree; /*!< A tree to speed up extension pattern matching */ struct ast_context *next; /*!< Link them together */ struct ast_include *includes; /*!< Include other contexts */ struct ast_ignorepat *ignorepats; /*!< Patterns for which to continue playing dialtone */ - const char *registrar; /*!< Registrar */ + char *registrar; /*!< Registrar -- make sure you malloc this, as the registrar may have to survive module unloads */ + 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 */ @@ -329,14 +331,15 @@ static struct match_char *add_pattern_node(struct ast_context *con, struct match static void create_match_char_tree(struct ast_context *con); static struct ast_exten *get_canmatch_exten(struct match_char *node); static void destroy_pattern_tree(struct match_char *pattern_tree); -static int hashtab_compare_contexts(const void *ah_a, const void *ah_b); +int ast_hashtab_compare_contexts(const void *ah_a, const void *ah_b); static int hashtab_compare_extens(const void *ha_a, const void *ah_b); static int hashtab_compare_exten_numbers(const void *ah_a, const void *ah_b); static int hashtab_compare_exten_labels(const void *ah_a, const void *ah_b); -static unsigned int hashtab_hash_contexts(const void *obj); +unsigned int ast_hashtab_hash_contexts(const void *obj); static unsigned int hashtab_hash_extens(const void *obj); static unsigned int hashtab_hash_priority(const void *obj); static unsigned int hashtab_hash_labels(const void *obj); +static void __ast_internal_context_destroy( struct ast_context *con); /* a func for qsort to use to sort a char array */ static int compare_char(const void *a, const void *b) @@ -352,7 +355,7 @@ static int compare_char(const void *a, const void *b) } /* 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; @@ -365,8 +368,10 @@ static int hashtab_compare_extens(const void *ah_a, const void *ah_b) const struct ast_exten *ac = ah_a; const struct ast_exten *bc = ah_b; int x = strcmp(ac->exten, bc->exten); - if (x) /* if exten names are diff, then return */ + if (x) { /* if exten names are diff, then return */ return x; + } + /* but if they are the same, do the cidmatch values match? */ if (ac->matchcid && bc->matchcid) { return strcmp(ac->cidmatch,bc->cidmatch); @@ -391,7 +396,7 @@ static int hashtab_compare_exten_labels(const void *ah_a, const void *ah_b) return strcmp(ac->label, bc->label); } -static unsigned int hashtab_hash_contexts(const void *obj) +unsigned int ast_hashtab_hash_contexts(const void *obj) { const struct ast_context *ac = obj; return ast_hashtab_hash_string(ac->name); @@ -708,7 +713,7 @@ static struct pbx_builtin { }; static struct ast_context *contexts; -static struct ast_hashtab *contexts_tree = NULL; +static struct ast_hashtab *contexts_table = NULL; AST_RWLOCK_DEFINE_STATIC(conlock); /*!< Lock for the ast_context list */ @@ -726,6 +731,185 @@ static int stateid = 1; static AST_RWLIST_HEAD_STATIC(hints, ast_hint); struct ast_state_cb *statecbs; +#ifdef CONTEXT_DEBUG + +/* these routines are provided for doing run-time checks + on the extension structures, in case you are having + problems, this routine might help you localize where + the problem is occurring. It's kinda like a debug memory + allocator's arena checker... It'll eat up your cpu cycles! + but you'll see, if you call it in the right places, + right where your problems began... +*/ + +/* you can break on the check_contexts_trouble() +routine in your debugger to stop at the moment +there's a problem */ +void check_contexts_trouble(void); + +void check_contexts_trouble(void) +{ + int x = 1; + x = 2; +} + +static struct ast_context *find_context_locked(const char *context); +int check_contexts(char *, int); + +int check_contexts(char *file, int line ) +{ + struct ast_hashtab_iter *t1; + struct ast_context *c1, *c2; + int found = 0; + struct ast_exten *e1, *e2, *e3; + struct ast_exten ex; + + /* try to find inconsistencies */ + /* is every context in the context table in the context list and vice-versa ? */ + + if (!contexts_table) { + ast_log(LOG_NOTICE,"Called from: %s:%d: No contexts_table!\n", file, line); + usleep(500000); + } + + t1 = ast_hashtab_start_traversal(contexts_table); + while( (c1 = ast_hashtab_next(t1))) { + for(c2=contexts;c2;c2=c2->next) { + if (!strcmp(c1->name, c2->name)) { + found = 1; + break; + } + } + if (!found) { + ast_log(LOG_NOTICE,"Called from: %s:%d: Could not find the %s context in the linked list\n", file, line, c1->name); + check_contexts_trouble(); + } + } + ast_hashtab_end_traversal(t1); + for(c2=contexts;c2;c2=c2->next) { + c1 = find_context_locked(c2->name); + if (!c1) { + ast_log(LOG_NOTICE,"Could not find the %s context in the hashtab\n", c2->name); + check_contexts_trouble(); + } else + ast_unlock_contexts(); + } + + /* loop thru all contexts, and verify the exten structure compares to the + hashtab structure */ + for(c2=contexts;c2;c2=c2->next) { + c1 = find_context_locked(c2->name); + if (c1) + { + + ast_unlock_contexts(); + + /* is every entry in the root list also in the root_table? */ + for(e1 = c1->root; e1; e1=e1->next) + { + char dummy_name[1024]; + ex.exten = dummy_name; + ex.matchcid = e1->matchcid; + ex.cidmatch = e1->cidmatch; + ast_copy_string(dummy_name, e1->exten, sizeof(dummy_name)); + e2 = ast_hashtab_lookup(c1->root_table, &ex); + if (!e2) { + if (e1->matchcid) { + ast_log(LOG_NOTICE,"Called from: %s:%d: The %s context records the exten %s (CID match: %s) but it is not in its root_table\n", file, line, c2->name, dummy_name, e1->cidmatch ); + } else { + ast_log(LOG_NOTICE,"Called from: %s:%d: The %s context records the exten %s but it is not in its root_table\n", file, line, c2->name, dummy_name ); + } + check_contexts_trouble(); + } + } + + /* is every entry in the root_table also in the root list? */ + if (!c2->root_table) { + if (c2->root) { + ast_log(LOG_NOTICE,"Called from: %s:%d: No c2->root_table for context %s!\n", file, line, c2->name); + usleep(500000); + } + } else { + t1 = ast_hashtab_start_traversal(c2->root_table); + while( (e2 = ast_hashtab_next(t1)) ) { + for(e1=c2->root;e1;e1=e1->next) { + if (!strcmp(e1->exten, e2->exten)) { + found = 1; + break; + } + } + if (!found) { + ast_log(LOG_NOTICE,"Called from: %s:%d: The %s context records the exten %s but it is not in its root_table\n", file, line, c2->name, e2->exten); + check_contexts_trouble(); + } + + } + ast_hashtab_end_traversal(t1); + } + } + /* is every priority reflected in the peer_table at the head of the list? */ + + /* is every entry in the root list also in the root_table? */ + /* are the per-extension peer_tables in the right place? */ + + for(e1 = c2->root; e1; e1 = e1->next) { + + for(e2=e1;e2;e2=e2->peer) { + ex.priority = e2->priority; + if (e2 != e1 && e2->peer_table) { + ast_log(LOG_NOTICE,"Called from: %s:%d: The %s context, %s exten, %d priority has a peer_table entry, and shouldn't!\n", file, line, c2->name, e1->exten, e2->priority ); + check_contexts_trouble(); + } + + if (e2 != e1 && e2->peer_label_table) { + ast_log(LOG_NOTICE,"Called from: %s:%d: The %s context, %s exten, %d priority has a peer_label_table entry, and shouldn't!\n", file, line, c2->name, e1->exten, e2->priority ); + check_contexts_trouble(); + } + + if (e2 == e1 && !e2->peer_table){ + ast_log(LOG_NOTICE,"Called from: %s:%d: The %s context, %s exten, %d priority doesn't have a peer_table!\n", file, line, c2->name, e1->exten, e2->priority ); + check_contexts_trouble(); + } + + if (e2 == e1 && !e2->peer_label_table) { + ast_log(LOG_NOTICE,"Called from: %s:%d: The %s context, %s exten, %d priority doesn't have a peer_label_table!\n", file, line, c2->name, e1->exten, e2->priority ); + check_contexts_trouble(); + } + + + e3 = ast_hashtab_lookup(e1->peer_table, &ex); + if (!e3) { + ast_log(LOG_NOTICE,"Called from: %s:%d: The %s context, %s exten, %d priority is not reflected in the peer_table\n", file, line, c2->name, e1->exten, e2->priority ); + check_contexts_trouble(); + } + } + + if (!e1->peer_table){ + ast_log(LOG_NOTICE,"Called from: %s:%d: No e1->peer_table!\n", file, line); + usleep(500000); + } + + /* is every entry in the peer_table also in the peer list? */ + t1 = ast_hashtab_start_traversal(e1->peer_table); + while( (e2 = ast_hashtab_next(t1)) ) { + for(e3=e1;e3;e3=e3->peer) { + if (e3->priority == e2->priority) { + found = 1; + break; + } + } + if (!found) { + ast_log(LOG_NOTICE,"Called from: %s:%d: The %s context, %s exten, %d priority is not reflected in the peer list\n", file, line, c2->name, e1->exten, e2->priority ); + check_contexts_trouble(); + } + } + ast_hashtab_end_traversal(t1); + } + } + return 0; +} +#endif + /* \note This function is special. It saves the stack so that no matter how many times it is called, it returns to the same place */ @@ -1044,7 +1228,7 @@ static void new_find_extension(const char *str, struct scoreboard *score, struct update_scoreboard(score, length+1, spec+p->specificity, p->exten,0,callerid, p->deleted, p); \ if (!p->deleted) { \ if (action == E_FINDLABEL) { \ - if (ast_hashtab_lookup(score->exten->peer_label_tree, &pattern)) { \ + if (ast_hashtab_lookup(score->exten->peer_label_table, &pattern)) { \ ast_debug(4, "Found label in preferred extension\n"); \ return; \ } \ @@ -1392,11 +1576,11 @@ static void create_match_char_tree(struct ast_context *con) int biggest_bucket, resizes, numobjs, numbucks; ast_log(LOG_DEBUG,"Creating Extension Trie for context %s\n", con->name); - ast_hashtab_get_stats(con->root_tree, &biggest_bucket, &resizes, &numobjs, &numbucks); + ast_hashtab_get_stats(con->root_table, &biggest_bucket, &resizes, &numobjs, &numbucks); ast_log(LOG_DEBUG,"This tree has %d objects in %d bucket lists, longest list=%d objects, and has resized %d times\n", numobjs, numbucks, biggest_bucket, resizes); #endif - t1 = ast_hashtab_start_traversal(con->root_tree); + t1 = ast_hashtab_start_traversal(con->root_table); while( (e1 = ast_hashtab_next(t1)) ) { if (e1->exten) add_exten_to_pattern_tree(con, e1, 0); @@ -1787,12 +1971,13 @@ struct fake_context /* this struct is purely for matching in the hashtab */ { ast_rwlock_t lock; struct ast_exten *root; - struct ast_hashtab *root_tree; + struct ast_hashtab *root_table; struct match_char *pattern_tree; struct ast_context *next; struct ast_include *includes; struct ast_ignorepat *ignorepats; - const char *registrar; + const char *registrar; + int refcount; AST_LIST_HEAD_NOLOCK(, ast_sw) alts; ast_mutex_t macrolock; char name[256]; @@ -1804,8 +1989,8 @@ struct ast_context *ast_context_find(const char *name) struct fake_context item; strncpy(item.name,name,256); ast_rdlock_contexts(); - if( contexts_tree ) { - tmp = ast_hashtab_lookup(contexts_tree,&item); + if( contexts_table ) { + tmp = ast_hashtab_lookup(contexts_table,&item); } else { while ( (tmp = ast_walk_contexts(tmp)) ) { if (!name || !strcasecmp(name, tmp->name)) @@ -1874,7 +2059,7 @@ struct ast_exten *pbx_find_extension(struct ast_channel *chan, else { /* look in contexts */ struct fake_context item; strncpy(item.name,context,256); - tmp = ast_hashtab_lookup(contexts_tree,&item); + tmp = ast_hashtab_lookup(contexts_table,&item); #ifdef NOTNOW tmp = NULL; while ((tmp = ast_walk_contexts(tmp)) ) { @@ -1896,7 +2081,7 @@ struct ast_exten *pbx_find_extension(struct ast_channel *chan, score.total_specificity = 0; score.exten = 0; score.total_length = 0; - if (!tmp->pattern_tree && tmp->root_tree) + if (!tmp->pattern_tree && tmp->root_table) { create_match_char_tree(tmp); #ifdef NEED_DEBUG @@ -1966,9 +2151,9 @@ struct ast_exten *pbx_find_extension(struct ast_channel *chan, if (action == E_FINDLABEL && label ) { if (q->status < STATUS_NO_LABEL) q->status = STATUS_NO_LABEL; - e = ast_hashtab_lookup(eroot->peer_label_tree, &pattern); + e = ast_hashtab_lookup(eroot->peer_label_table, &pattern); } else { - e = ast_hashtab_lookup(eroot->peer_tree, &pattern); + e = ast_hashtab_lookup(eroot->peer_table, &pattern); } if (e) { /* found a valid match */ q->status = STATUS_SUCCESS; @@ -2002,9 +2187,9 @@ struct ast_exten *pbx_find_extension(struct ast_channel *chan, if (action == E_FINDLABEL && label ) { if (q->status < STATUS_NO_LABEL) q->status = STATUS_NO_LABEL; - e = ast_hashtab_lookup(eroot->peer_label_tree, &pattern); + e = ast_hashtab_lookup(eroot->peer_label_table, &pattern); } else { - e = ast_hashtab_lookup(eroot->peer_tree, &pattern); + e = ast_hashtab_lookup(eroot->peer_table, &pattern); } #ifdef NOTNOW while ( (e = ast_walk_extension_priorities(eroot, e)) ) { @@ -2686,7 +2871,7 @@ static void pbx_substitute_variables_helper_full(struct ast_channel *c, struct v vare++; } if (brackets) - ast_log(LOG_NOTICE, "Error in extension logic (missing '}')\n"); + ast_log(LOG_WARNING, "Error in extension logic (missing '}')\n"); len = vare - vars - 1; /* Skip totally over variable string */ @@ -2773,7 +2958,7 @@ static void pbx_substitute_variables_helper_full(struct ast_channel *c, struct v vare++; } if (brackets) - ast_log(LOG_NOTICE, "Error in extension logic (missing ']')\n"); + ast_log(LOG_WARNING, "Error in extension logic (missing ']')\n"); len = vare - vars - 1; /* Skip totally over expression */ @@ -3689,14 +3874,14 @@ static int increase_call_count(const struct ast_channel *c) ast_mutex_lock(&maxcalllock); if (option_maxcalls) { if (countcalls >= option_maxcalls) { - ast_log(LOG_NOTICE, "Maximum call limit of %d calls exceeded by '%s'!\n", option_maxcalls, c->name); + ast_log(LOG_WARNING, "Maximum call limit of %d calls exceeded by '%s'!\n", option_maxcalls, c->name); failed = -1; } } if (option_maxload) { getloadavg(&curloadavg, 1); if (curloadavg >= option_maxload) { - ast_log(LOG_NOTICE, "Maximum loadavg limit of %f load exceeded by '%s' (currently %f)!\n", option_maxload, c->name, curloadavg); + ast_log(LOG_WARNING, "Maximum loadavg limit of %f load exceeded by '%s' (currently %f)!\n", option_maxload, c->name, curloadavg); failed = -1; } } @@ -3737,10 +3922,10 @@ static void destroy_exten(struct ast_exten *e) if (e->priority == PRIORITY_HINT) ast_remove_hint(e); - if (e->peer_tree) - ast_hashtab_destroy(e->peer_tree,0); - if (e->peer_label_tree) - ast_hashtab_destroy(e->peer_label_tree, 0); + if (e->peer_table) + ast_hashtab_destroy(e->peer_table,0); + if (e->peer_label_table) + ast_hashtab_destroy(e->peer_label_table, 0); if (e->datad) e->datad(e->data); ast_free(e); @@ -3837,8 +4022,7 @@ static struct ast_context *find_context_locked(const char *context) ast_copy_string(item.name, context, sizeof(item.name)); ast_rdlock_contexts(); - - c = ast_hashtab_lookup(contexts_tree,&item); + c = ast_hashtab_lookup(contexts_table,&item); #ifdef NOTNOW @@ -4016,19 +4200,21 @@ int ast_context_remove_extension_callerid2(struct ast_context *con, const char * * peers, not just those matching the callerid. */ #ifdef NEED_DEBUG ast_verb(3,"Removing %s/%s/%d%s%s from trees, registrar=%s\n", con->name, extension, priority, matchcid ? "/" : "", matchcid ? callerid : "", registrar); +#endif +#ifdef CONTEXT_DEBUG + check_contexts(__FILE__, __LINE__); #endif /* find this particular extension */ ex.exten = dummy_name; - ex.matchcid = matchcid; + ex.matchcid = matchcid && !ast_strlen_zero(callerid); /* don't say match if there's no callerid */ ex.cidmatch = callerid; ast_copy_string(dummy_name, extension, sizeof(dummy_name)); - exten = ast_hashtab_lookup(con->root_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); + if (priority == 0) { + exten2 = ast_hashtab_remove_this_object(con->root_table, exten); if (!exten2) - ast_log(LOG_ERROR,"Trying to delete the exten %s from context %s, but could not remove from the root_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); @@ -4042,24 +4228,28 @@ int ast_context_remove_extension_callerid2(struct ast_context *con, const char * } } 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 */ @@ -4075,7 +4265,7 @@ int ast_context_remove_extension_callerid2(struct ast_context *con, const char * } } 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 @@ -4088,7 +4278,8 @@ int ast_context_remove_extension_callerid2(struct ast_context *con, const char * /* scan the extension list to find first matching extension-registrar */ for (exten = con->root; exten; prev_exten = exten, exten = exten->next) { if (!strcmp(exten->exten, extension) && - (!registrar || !strcmp(exten->registrar, registrar))) + (!registrar || !strcmp(exten->registrar, registrar)) && + (!matchcid || (!ast_strlen_zero(callerid) && !ast_strlen_zero(exten->cidmatch) && !strcmp(exten->cidmatch, callerid)) || (ast_strlen_zero(callerid) && ast_strlen_zero(exten->cidmatch)))) break; } if (!exten) { @@ -4100,7 +4291,7 @@ int ast_context_remove_extension_callerid2(struct ast_context *con, const char * /* scan the priority list to remove extension with exten->priority == priority */ for (peer = exten, next_peer = exten->peer ? exten->peer : exten->next; - peer && !strcmp(peer->exten, extension); + peer && !strcmp(peer->exten, extension) && (!matchcid || (!ast_strlen_zero(callerid) && !ast_strlen_zero(peer->cidmatch) && !strcmp(peer->cidmatch,callerid)) || (ast_strlen_zero(callerid) && ast_strlen_zero(peer->cidmatch))); peer = next_peer, next_peer = next_peer ? (next_peer->peer ? next_peer->peer : next_peer->next) : NULL) { if ((priority == 0 || peer->priority == priority) && (!callerid || !matchcid || (matchcid && !strcmp(peer->cidmatch, callerid))) && @@ -4114,7 +4305,14 @@ int ast_context_remove_extension_callerid2(struct ast_context *con, const char * * The next node is either the next priority or the next extension */ struct ast_exten *next_node = peer->peer ? peer->peer : peer->next; - + if (peer->peer) { + /* move the peer_table and peer_label_table down to the next peer, if + it is there */ + peer->peer->peer_table = peer->peer_table; + peer->peer->peer_label_table = peer->peer_label_table; + peer->peer_table = NULL; + peer->peer_label_table = NULL; + } if (!prev_exten) { /* change the root... */ con->root = next_node; } else { @@ -4133,6 +4331,9 @@ int ast_context_remove_extension_callerid2(struct ast_context *con, const char * previous_peer = peer; } } +#ifdef CONTEXT_DEBUG + check_contexts(__FILE__, __LINE__); +#endif if (!already_locked) ast_unlock_context(con); return found ? 0 : -1; @@ -4153,7 +4354,7 @@ int ast_context_lockmacro(const char *context) ast_rdlock_contexts(); strncpy(item.name,context,256); - c = ast_hashtab_lookup(contexts_tree,&item); + c = ast_hashtab_lookup(contexts_table,&item); if (c) ret = 0; @@ -4191,7 +4392,7 @@ int ast_context_unlockmacro(const char *context) ast_rdlock_contexts(); strncpy(item.name, context, 256); - c = ast_hashtab_lookup(contexts_tree,&item); + c = ast_hashtab_lookup(contexts_table,&item); if (c) ret = 0; #ifdef NOTNOW @@ -4785,7 +4986,7 @@ static int show_dialplan_helper(int fd, const char *context, const char *exten, if (exten) { /* Check all includes for the requested extension */ if (includecount >= AST_PBX_MAX_STACK) { - ast_log(LOG_NOTICE, "Maximum include depth exceeded!\n"); + ast_log(LOG_WARNING, "Maximum include depth exceeded!\n"); } else { int dupe = 0; int x; @@ -5334,42 +5535,37 @@ int ast_unregister_application(const char *app) return tmp ? 0 : -1; } -static struct ast_context *__ast_context_create(struct ast_context **extcontexts, const char *name, const char *registrar, int existsokay) +struct ast_context *ast_context_find_or_create(struct ast_context **extcontexts, struct ast_hashtab *exttable, const char *name, const char *registrar) { struct ast_context *tmp, **local_contexts; struct fake_context search; int length = sizeof(struct ast_context) + strlen(name) + 1; - if (!contexts_tree) { - contexts_tree = ast_hashtab_create(17, - hashtab_compare_contexts, + if (!contexts_table) { + contexts_table = ast_hashtab_create(17, + ast_hashtab_compare_contexts, ast_hashtab_resize_java, ast_hashtab_newsize_java, - hashtab_hash_contexts, + ast_hashtab_hash_contexts, 0); } + strncpy(search.name,name,sizeof(search.name)); if (!extcontexts) { ast_rdlock_contexts(); local_contexts = &contexts; - strncpy(search.name,name,sizeof(search.name)); - tmp = ast_hashtab_lookup(contexts_tree, &search); - if (!existsokay && tmp) { - ast_log(LOG_WARNING, "Tried to register context '%s', already in use\n", name); - } + tmp = ast_hashtab_lookup(contexts_table, &search); ast_unlock_contexts(); - if (tmp) + if (tmp) { + tmp->refcount++; return tmp; + } } else { /* local contexts just in a linked list; search there for the new context; slow, linear search, but not frequent */ local_contexts = extcontexts; - for (tmp = *local_contexts; tmp; tmp = tmp->next) { - if (!strcasecmp(tmp->name, name)) { - if (!existsokay) { - ast_log(LOG_WARNING, "Tried to register context '%s', already in use\n", name); - tmp = NULL; - } - return tmp; - } + tmp = ast_hashtab_lookup(exttable, &search); + if (tmp) { + tmp->refcount++; + return tmp; } } @@ -5378,19 +5574,26 @@ static struct ast_context *__ast_context_create(struct ast_context **extcontexts ast_mutex_init(&tmp->macrolock); strcpy(tmp->name, name); tmp->root = NULL; - tmp->root_tree = NULL; - tmp->registrar = registrar; + tmp->root_table = NULL; + tmp->registrar = ast_strdup(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); @@ -5398,16 +5601,7 @@ static struct ast_context *__ast_context_create(struct ast_context **extcontexts return tmp; } -struct ast_context *ast_context_create(struct ast_context **extcontexts, const char *name, const char *registrar) -{ - return __ast_context_create(extcontexts, name, registrar, 0); -} - -struct ast_context *ast_context_find_or_create(struct ast_context **extcontexts, const char *name, const char *registrar) -{ - return __ast_context_create(extcontexts, name, registrar, 1); -} -void __ast_context_destroy(struct ast_context *con, const char *registrar); +void __ast_context_destroy(struct ast_context *list, struct ast_hashtab *contexttab, struct ast_context *con, const char *registrar); struct store_hint { char *context; @@ -5420,17 +5614,125 @@ struct store_hint { AST_LIST_HEAD(store_hints, store_hint); +static void context_merge_incls_swits_igps_other_registrars(struct ast_context *new, struct ast_context *old, const char *registrar) +{ + struct ast_include *i; + struct ast_ignorepat *ip; + struct ast_sw *sw; + + /* copy in the includes, switches, and ignorepats */ + /* walk through includes */ + for (i = NULL; (i = ast_walk_context_includes(old, i)) ; ) { + if (strcmp(ast_get_include_registrar(i), registrar) == 0) + continue; /* not mine */ + ast_context_add_include2(new, ast_get_include_name(i), ast_get_include_registrar(i)); + } + + /* walk through switches */ + for (sw = NULL; (sw = ast_walk_context_switches(old, sw)) ; ) { + if (strcmp(ast_get_switch_registrar(sw), registrar) == 0) + continue; /* not mine */ + ast_context_add_switch2(new, ast_get_switch_name(sw), ast_get_switch_data(sw), ast_get_switch_eval(sw), ast_get_switch_registrar(sw)); + } + + /* walk thru ignorepats ... */ + for (ip = NULL; (ip = ast_walk_context_ignorepats(old, ip)); ) { + if (strcmp(ast_get_ignorepat_registrar(ip), registrar) == 0) + continue; /* not mine */ + ast_context_add_ignorepat2(new, ast_get_ignorepat_name(ip), ast_get_ignorepat_registrar(ip)); + } +} + + +/* 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 */ + + /* copy in the includes, switches, and ignorepats */ + context_merge_incls_swits_igps_other_registrars(new, context, 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); + + /* copy in the includes, switches, and ignorepats */ + context_merge_incls_swits_igps_other_registrars(new, 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 @@ -5439,8 +5741,29 @@ void ast_merge_contexts_and_delete(struct ast_context **extcontexts, const char in addition, the locks _must_ be taken in this order, because there are already other code paths that use this order */ + + struct timeval begintime, writelocktime, endlocktime, enddeltime; + int wrlock_ver; + + begintime = ast_tvnow(); + ast_rdlock_contexts(); + iter = ast_hashtab_start_traversal(contexts_table); + while ((tmp=ast_hashtab_next(iter))) { + context_merge(extcontexts, exttable, tmp, registrar); + } + ast_hashtab_end_traversal(iter); + wrlock_ver = ast_wrlock_contexts_version(); + + ast_unlock_contexts(); /* this feels real retarded, but you must do + what you must do If this isn't done, the following + wrlock is a guraranteed deadlock */ ast_wrlock_contexts(); + if (ast_wrlock_contexts_version() > wrlock_ver+1) { + ast_log(LOG_WARNING,"Something changed the contexts in the middle of merging contexts!\n"); + } + AST_RWLIST_WRLOCK(&hints); + writelocktime = ast_tvnow(); /* preserve all watchers for hints associated with this registrar */ AST_RWLIST_TRAVERSE(&hints, hint, list) { @@ -5459,36 +5782,14 @@ void ast_merge_contexts_and_delete(struct ast_context **extcontexts, const char } } - tmp = *extcontexts; - if (registrar) { - /* XXX remove previous contexts from same registrar */ - ast_debug(1, "must remove any reg %s\n", registrar); - __ast_context_destroy(NULL,registrar); - while (tmp) { - lasttmp = tmp; - tmp = tmp->next; - } - } else { - /* XXX remove contexts with the same name */ - while (tmp) { - ast_log(LOG_WARNING, "must remove %s reg %s\n", tmp->name, tmp->registrar); - __ast_context_destroy(tmp,tmp->registrar); - lasttmp = tmp; - tmp = tmp->next; - } - } - tmp = *extcontexts; - while (tmp) { - ast_hashtab_insert_safe(contexts_tree, tmp); /*put this context into the tree */ - tmp = tmp->next; - } - if (lasttmp) { - lasttmp->next = contexts; - contexts = *extcontexts; - *extcontexts = NULL; - } else - ast_log(LOG_WARNING, "Requested contexts didn't get merged\n"); + /* save the old table and list */ + oldtable = contexts_table; + oldcontextslist = contexts; + /* move in the new table and list */ + contexts_table = exttable; + contexts = *extcontexts; + /* restore the watchers for hints that can be found; notify those that cannot be restored */ @@ -5523,7 +5824,36 @@ void ast_merge_contexts_and_delete(struct ast_context **extcontexts, const char AST_RWLIST_UNLOCK(&hints); ast_unlock_contexts(); + endlocktime = ast_tvnow(); + + /* the old list and hashtab no longer are relevant, delete them while the rest of asterisk + is now freely using the new stuff instead */ + + ast_hashtab_destroy(oldtable, NULL); + + for (tmp = oldcontextslist; tmp; ) { + struct ast_context *next; /* next starting point */ + next = tmp->next; + __ast_internal_context_destroy(tmp); + tmp = next; + } + enddeltime = ast_tvnow(); + + ft = ast_tvdiff_us(writelocktime, begintime); + ft /= 1000000.0; + ast_verb(3,"Time to scan old dialplan and merge leftovers back into the new: %8.6f sec\n", ft); + + ft = ast_tvdiff_us(endlocktime, writelocktime); + ft /= 1000000.0; + ast_verb(3,"Time to restore hints and swap in new dialplan: %8.6f sec\n", ft); + + ft = ast_tvdiff_us(enddeltime, endlocktime); + ft /= 1000000.0; + ast_verb(3,"Time to delete the old dialplan: %8.6f sec\n", ft); + ft = ast_tvdiff_us(enddeltime, begintime); + ft /= 1000000.0; + ast_verb(3,"Total time merge_contexts_delete: %8.6f sec\n", ft); return; } @@ -6054,6 +6384,7 @@ int ast_add_extension(const char *context, int replace, const char *extension, application, data, datad, registrar); ast_unlock_contexts(); } + return ret; } @@ -6180,9 +6511,11 @@ static int add_pri(struct ast_context *con, struct ast_exten *tmp, break; } if (!e) { /* go at the end, and ep is surely set because the list is not empty */ - ast_hashtab_insert_safe(eh->peer_tree, tmp); - if (tmp->label) - ast_hashtab_insert_safe(eh->peer_label_tree, tmp); + ast_hashtab_insert_safe(eh->peer_table, tmp); + + if (tmp->label) { + ast_hashtab_insert_safe(eh->peer_label_table, tmp); + } ep->peer = tmp; return 0; /* success */ } @@ -6202,25 +6535,33 @@ static int add_pri(struct ast_context *con, struct ast_exten *tmp, tmp->next = e->next; /* not meaningful if we are not first in the peer list */ tmp->peer = e->peer; /* always meaningful */ if (ep) { /* We're in the peer list, just insert ourselves */ - ast_hashtab_remove_object_via_lookup(eh->peer_tree,e); - if (e->label) - ast_hashtab_remove_object_via_lookup(eh->peer_label_tree,e); - ast_hashtab_insert_safe(eh->peer_tree,tmp); - if (tmp->label) - ast_hashtab_insert_safe(eh->peer_label_tree,tmp); + ast_hashtab_remove_object_via_lookup(eh->peer_table,e); + + if (e->label) { + ast_hashtab_remove_object_via_lookup(eh->peer_label_table,e); + } + + ast_hashtab_insert_safe(eh->peer_table,tmp); + if (tmp->label) { + 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); - if (e->label) - ast_hashtab_remove_object_via_lookup(tmp->peer_label_tree,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); + 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_table, e); + } + if (tmp->label) { + ast_hashtab_insert_safe(tmp->peer_label_table, tmp); + } + + ast_hashtab_remove_object_via_lookup(con->root_table, e); + ast_hashtab_insert_safe(con->root_table, tmp); el->next = tmp; /* The pattern trie points to this exten; replace the pointer, and all will be well */ @@ -6233,18 +6574,21 @@ static int add_pri(struct ast_context *con, struct ast_exten *tmp, } } else { /* We're the very first extension. */ struct match_char *x = add_exten_to_pattern_tree(con, e, 1); - ast_hashtab_remove_object_via_lookup(con->root_tree,e); - ast_hashtab_insert_safe(con->root_tree,tmp); - tmp->peer_tree = e->peer_tree; - tmp->peer_label_tree = e->peer_label_tree; - ast_hashtab_remove_object_via_lookup(tmp->peer_tree,e); - ast_hashtab_insert_safe(tmp->peer_tree,tmp); - if (e->label) - ast_hashtab_remove_object_via_lookup(tmp->peer_label_tree,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_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_table, e); + } + if (tmp->label) { + ast_hashtab_insert_safe(tmp->peer_label_table, tmp); + } + + ast_hashtab_remove_object_via_lookup(con->root_table, e); + ast_hashtab_insert_safe(con->root_table, tmp); con->root = tmp; /* The pattern trie points to this exten; replace the pointer, and all will be well */ @@ -6266,21 +6610,22 @@ static int add_pri(struct ast_context *con, struct ast_exten *tmp, tmp->peer = e; tmp->next = e->next; /* extension chain, or NULL if e is not the first extension */ if (ep) { /* Easy enough, we're just in the peer list */ - if (tmp->label) - ast_hashtab_insert_safe(eh->peer_label_tree, tmp); - ast_hashtab_insert_safe(eh->peer_tree, tmp); + if (tmp->label) { + ast_hashtab_insert_safe(eh->peer_label_table, tmp); + } + ast_hashtab_insert_safe(eh->peer_table, tmp); ep->peer = tmp; } else { /* we are the first in some peer list, so link in the ext list */ - 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 @@ -6362,6 +6707,9 @@ int ast_add_extension2(struct ast_context *con, if (!(tmp = ast_calloc(1, length))) return -1; + if (ast_strlen_zero(label)) /* let's turn empty labels to a null ptr */ + label = 0; + /* use p as dst in assignments, as the fields are const char * */ p = tmp->stuff; if (label) { @@ -6373,7 +6721,7 @@ int ast_add_extension2(struct ast_context *con, p += ext_strncpy(p, extension, strlen(extension) + 1) + 1; tmp->priority = priority; tmp->cidmatch = p; /* but use p for assignments below */ - if (callerid) { + if (!ast_strlen_zero(callerid)) { p += ext_strncpy(p, callerid, strlen(callerid) + 1) + 1; tmp->matchcid = 1; } else { @@ -6395,11 +6743,11 @@ int ast_add_extension2(struct ast_context *con, dummy_exten.exten = dummy_name; dummy_exten.matchcid = 0; dummy_exten.cidmatch = 0; - tmp2 = ast_hashtab_lookup(con->root_tree,&dummy_exten); + tmp2 = ast_hashtab_lookup(con->root_table, &dummy_exten); if (!tmp2) { /* hmmm, not in the trie; */ add_exten_to_pattern_tree(con, tmp, 0); - ast_hashtab_insert_safe(con->root_tree, tmp); /* for the sake of completeness */ + ast_hashtab_insert_safe(con->root_table, tmp); /* for the sake of completeness */ } } res = 0; /* some compilers will think it is uninitialized otherwise */ @@ -6433,48 +6781,50 @@ int ast_add_extension2(struct ast_context *con, tmp->next = e; if (el) { /* there is another exten already in this context */ el->next = tmp; - tmp->peer_tree = ast_hashtab_create(13, + tmp->peer_table = ast_hashtab_create(13, hashtab_compare_exten_numbers, ast_hashtab_resize_java, ast_hashtab_newsize_java, hashtab_hash_priority, 0); - tmp->peer_label_tree = ast_hashtab_create(7, + tmp->peer_label_table = ast_hashtab_create(7, hashtab_compare_exten_labels, ast_hashtab_resize_java, ast_hashtab_newsize_java, hashtab_hash_labels, 0); - if (label) - ast_hashtab_insert_safe(tmp->peer_label_tree,tmp); - ast_hashtab_insert_safe(tmp->peer_tree, tmp); - + if (label) { + ast_hashtab_insert_safe(tmp->peer_label_table, tmp); + } + ast_hashtab_insert_safe(tmp->peer_table, tmp); } else { /* this is the first exten in this context */ - if (!con->root_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); + if (label) { + ast_hashtab_insert_safe(con->root->peer_label_table, tmp); + } + ast_hashtab_insert_safe(con->root->peer_table, tmp); + } - ast_hashtab_insert_safe(con->root_tree, tmp); + ast_hashtab_insert_safe(con->root_table, tmp); ast_unlock_context(con); if (tmp->priority == PRIORITY_HINT) ast_add_hint(tmp); @@ -6490,7 +6840,7 @@ int ast_add_extension2(struct ast_context *con, } if (tmp->matchcid) { - ast_verb(3, "Added extension '%s' priority %d (CID match '%s')to %s\n", + ast_verb(3, "Added extension '%s' priority %d (CID match '%s') to %s\n", tmp->exten, tmp->priority, tmp->cidmatch, con->name); } else { ast_verb(3, "Added extension '%s' priority %d to %s\n", @@ -6882,68 +7232,191 @@ outgoing_app_cleanup: return res; } -void __ast_context_destroy(struct ast_context *con, const char *registrar) +/* this is the guts of destroying a context -- + freeing up the structure, traversing and destroying the + extensions, switches, ignorepats, includes, etc. etc. */ + +static void __ast_internal_context_destroy( struct ast_context *con) { - struct ast_context *tmp, *tmpl=NULL; struct ast_include *tmpi; struct ast_sw *sw; struct ast_exten *e, *el, *en; struct ast_ignorepat *ipi; + struct ast_context *tmp = con; - for (tmp = contexts; tmp; ) { - struct ast_context *next; /* next starting point */ - for (; 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)) ) - break; /* found it */ + 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); + } + if (tmp->registrar) + ast_free(tmp->registrar); + + /* 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 */ + /* The following code used to skip forward to the next + context with matching registrar, but this didn't + make sense; individual priorities registrar'd to + the matching registrar could occur in any context! */ + ast_debug(1, "Investigate ctx %s %s\n", tmp->name, tmp->registrar); + if (con) { + for (; tmp; tmpl = tmp, tmp = tmp->next) { /* skip to the matching context */ + ast_debug(1, "check ctx %s %s\n", tmp->name, tmp->registrar); + if ( !strcasecmp(tmp->name, con->name) ) { + break; /* found it */ + } + } + } + if (!tmp) /* not found, we are done */ break; ast_wrlock_context(tmp); - 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; + struct ast_ignorepat *ip, *ipl = NULL, *ipn = NULL; + struct ast_include *i, *pi = NULL, *ni = NULL; + struct ast_sw *sw = NULL; + + /* remove any ignorepats whose registrar matches */ + for (ip = tmp->ignorepats; ip; ip = ipn) { + ipn = ip->next; + if (!strcmp(ip->registrar, registrar)) { + if (ipl) { + ipl->next = ip->next; + ast_free(ip); + continue; /* don't change ipl */ + } else { + tmp->ignorepats = ip->next; + ast_free(ip); + continue; /* don't change ipl */ + } + } + ipl = ip; + } + /* remove any includes whose registrar matches */ + for (i = tmp->includes; i; i = ni) { + ni = i->next; + if (strcmp(i->registrar, registrar) == 0) { + /* remove from list */ + if (pi) { + pi->next = i->next; + /* free include */ + ast_free(i); + continue; /* don't change pi */ + } else { + tmp->includes = i->next; + /* free include */ + ast_free(i); + continue; /* don't change pi */ + } + } + pi = i; + } + /* remove any switches whose registrar matches */ + AST_LIST_TRAVERSE_SAFE_BEGIN(&tmp->alts, sw, list) { + if (strcmp(sw->registrar,registrar) == 0) { + AST_LIST_REMOVE_CURRENT(list); + ast_free(sw); + } + } + AST_LIST_TRAVERSE_SAFE_END + + if (tmp->root_table) { /* it is entirely possible that the context is EMPTY */ + exten_iter = ast_hashtab_start_traversal(tmp->root_table); + while ((exten_item=ast_hashtab_next(exten_iter))) { + prio_iter = ast_hashtab_start_traversal(exten_item->peer_table); + while ((prio_item=ast_hashtab_next(prio_iter))) { + if (!prio_item->registrar || strcmp(prio_item->registrar, registrar) != 0) { + continue; + } + ast_verb(3, "Remove %s/%s/%d, registrar=%s; con=%s(%p); con->root=%p\n", + tmp->name, prio_item->exten, prio_item->priority, registrar, con? con->name : "", con, con? con->root_table: NULL); + /* set matchcid to 1 to insure we get a direct match, and NULL registrar to make sure no wildcarding is done */ + ast_context_remove_extension_callerid2(tmp, prio_item->exten, prio_item->priority, prio_item->cidmatch, 1, NULL, 1); + } + ast_hashtab_end_traversal(prio_iter); + } + 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, */ + /* it's not empty, if it has includes, ignorepats, or switches that are registered from + another registrar. It's not empty if there are any extensions */ + if (strcmp(tmp->registrar, registrar) == 0 && tmp->refcount < 2 && !tmp->root && !tmp->ignorepats && !tmp->includes && AST_LIST_EMPTY(&tmp->alts)) { + ast_debug(1, "delete ctx %s %s\n", tmp->name, tmp->registrar); + ast_hashtab_remove_this_object(contexttab, tmp); + + 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 { + ast_debug(1,"Couldn't delete ctx %s/%s; refc=%d; tmp.root=%p\n", tmp->name, tmp->registrar, + tmp->refcount, tmp->root); + ast_unlock_context(tmp); + next = tmp->next; + tmpl = tmp; + } + } else if (con) { + ast_verb(3, "Deleting context %s registrar=%s\n", tmp->name, tmp->registrar); + 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; } @@ -6952,7 +7425,7 @@ void __ast_context_destroy(struct ast_context *con, const char *registrar) void ast_context_destroy(struct ast_context *con, const char *registrar) { ast_wrlock_contexts(); - __ast_context_destroy(con,registrar); + __ast_context_destroy(contexts, contexts_table, con,registrar); ast_unlock_contexts(); } @@ -7106,7 +7579,7 @@ static int pbx_builtin_hangup(struct ast_channel *chan, void *data) return -1; } - ast_log(LOG_NOTICE, "Invalid cause given to Hangup(): \"%s\"\n", (char *) data); + ast_log(LOG_WARNING, "Invalid cause given to Hangup(): \"%s\"\n", (char *) data); } if (!chan->hangupcause) { @@ -7786,13 +8259,22 @@ int load_pbx(void) return 0; } +static int conlock_wrlock_version = 0; + +int ast_wrlock_contexts_version(void) +{ + return conlock_wrlock_version; +} /* * Lock context list functions ... */ int ast_wrlock_contexts() { - return ast_rwlock_wrlock(&conlock); + int res = ast_rwlock_wrlock(&conlock); + if (!res) + ast_atomic_fetchadd_int(&conlock_wrlock_version, 1); + return res; } int ast_rdlock_contexts() @@ -7914,6 +8396,11 @@ const char *ast_get_switch_data(struct ast_sw *sw) return sw ? sw->data : NULL; } +int ast_get_switch_eval(struct ast_sw *sw) +{ + return sw->eval; +} + const char *ast_get_switch_registrar(struct ast_sw *sw) { return sw ? sw->registrar : NULL; diff --git a/pbx/pbx_ael.c b/pbx/pbx_ael.c index 6226ca4a15..22ec24105d 100644 --- a/pbx/pbx_ael.c +++ b/pbx/pbx_ael.c @@ -41,6 +41,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/cli.h" #include "asterisk/app.h" #include "asterisk/callerid.h" +#include "asterisk/hashtab.h" #include "asterisk/ael_structs.h" #include "asterisk/pval.h" #ifdef AAL_ARGCHECK @@ -87,7 +88,7 @@ struct ael_extension *new_exten(void); void destroy_extensions(struct ael_extension *exten); void set_priorities(struct ael_extension *exten); void add_extensions(struct ael_extension *exten); -void ast_compile_ael2(struct ast_context **local_contexts, struct pval *root); +void ast_compile_ael2(struct ast_context **local_contexts, struct ast_hashtab *local_table, struct pval *root); void destroy_pval(pval *item); void destroy_pval_item(pval *item); int is_float(char *arg ); @@ -107,6 +108,8 @@ static int pbx_load_module(void) int errs=0, sem_err=0, sem_warn=0, sem_note=0; char *rfilename; struct ast_context *local_contexts=NULL, *con; + struct ast_hashtab *local_table=NULL; + struct pval *parse_tree; ast_log(LOG_NOTICE, "Starting AEL load process.\n"); @@ -126,10 +129,13 @@ static int pbx_load_module(void) ael2_semantic_check(parse_tree, &sem_err, &sem_warn, &sem_note); if (errs == 0 && sem_err == 0) { ast_log(LOG_NOTICE, "AEL load process: checked config file name '%s'.\n", rfilename); - ast_compile_ael2(&local_contexts, parse_tree); + local_table = ast_hashtab_create(11, ast_hashtab_compare_contexts, ast_hashtab_resize_java, ast_hashtab_newsize_java, ast_hashtab_hash_contexts, 0); + ast_compile_ael2(&local_contexts, local_table, parse_tree); ast_log(LOG_NOTICE, "AEL load process: compiled config file name '%s'.\n", rfilename); - ast_merge_contexts_and_delete(&local_contexts, registrar); + ast_merge_contexts_and_delete(&local_contexts, local_table, registrar); + local_table = NULL; /* it's the dialplan global now */ + local_contexts = NULL; ast_log(LOG_NOTICE, "AEL load process: merged config file name '%s'.\n", rfilename); for (con = ast_walk_contexts(NULL); con; con = ast_walk_contexts(con)) ast_context_verify_includes(con); diff --git a/pbx/pbx_config.c b/pbx/pbx_config.c index a4d8c38cb4..cbed1e0203 100644 --- a/pbx/pbx_config.c +++ b/pbx/pbx_config.c @@ -51,7 +51,7 @@ static int extenpatternmatchnew_config = 0; AST_MUTEX_DEFINE_STATIC(save_dialplan_lock); static struct ast_context *local_contexts = NULL; - +static struct ast_hashtab *local_table = NULL; /* * Prototypes for our completion functions */ @@ -1402,7 +1402,6 @@ static int pbx_load_config(const char *config_file) const char *aft; const char *newpm; struct ast_flags config_flags = { 0 }; - cfg = ast_config_load(config_file, config_flags); if (!cfg) return 0; @@ -1430,7 +1429,7 @@ static int pbx_load_config(const char *config_file) /* All categories but "general" or "globals" are considered contexts */ if (!strcasecmp(cxt, "general") || !strcasecmp(cxt, "globals")) continue; - con=ast_context_find_or_create(&local_contexts,cxt, registrar); + con=ast_context_find_or_create(&local_contexts, local_table, cxt, registrar); if (con == NULL) continue; @@ -1642,7 +1641,7 @@ static void pbx_load_users(void) /* Only create a context here when it is really needed. Otherwise default empty context created by pbx_config may conflict with the one explicitly created by pbx_ael */ if (!con) - con = ast_context_find_or_create(&local_contexts, userscontext, registrar); + con = ast_context_find_or_create(&local_contexts, local_table, userscontext, registrar); if (!con) { ast_log(LOG_ERROR, "Can't find/create user context '%s'\n", userscontext); @@ -1667,12 +1666,17 @@ static int pbx_load_module(void) { struct ast_context *con; - if(!pbx_load_config(config)) + if (!local_table) + local_table = ast_hashtab_create(17, ast_hashtab_compare_contexts, ast_hashtab_resize_java, ast_hashtab_newsize_java, ast_hashtab_hash_contexts, 0); + + if (!pbx_load_config(config)) return AST_MODULE_LOAD_DECLINE; pbx_load_users(); - ast_merge_contexts_and_delete(&local_contexts, registrar); + ast_merge_contexts_and_delete(&local_contexts, local_table, registrar); + local_table = NULL; /* the local table has been moved into the global one. */ + local_contexts = NULL; for (con = NULL; (con = ast_walk_contexts(con));) ast_context_verify_includes(con); diff --git a/res/ael/ael.flex b/res/ael/ael.flex index 35ca126aae..0e92db91e3 100644 --- a/res/ael/ael.flex +++ b/res/ael/ael.flex @@ -60,6 +60,7 @@ %option bison-locations %{ +#include @@ -70,9 +71,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #define GLOB_ABORTED GLOB_ABEND #endif # include - #include "asterisk/logger.h" #include "asterisk/utils.h" +#include "asterisk/lock.h" +#include "asterisk/hashtab.h" #include "ael/ael.tab.h" #include "asterisk/ael_structs.h" diff --git a/res/ael/ael.tab.c b/res/ael/ael.tab.c index aa1ec0fadc..16c62ba710 100644 --- a/res/ael/ael.tab.c +++ b/res/ael/ael.tab.c @@ -188,6 +188,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include #include "asterisk/logger.h" +#include "asterisk/lock.h" +#include "asterisk/hashtab.h" #include "asterisk/ael_structs.h" pval * linku1(pval *head, pval *tail); @@ -227,14 +229,14 @@ static char *ael_token_subst(const char *mess); #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 @@ -256,7 +258,7 @@ typedef struct YYLTYPE /* 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); @@ -279,7 +281,7 @@ static pval *update_last(pval *, YYLTYPE *); /* Line 221 of yacc.c. */ -#line 283 "ael.tab.c" +#line 285 "ael.tab.c" #ifdef short # undef short @@ -625,21 +627,21 @@ static const yytype_int8 yyrhs[] = /* 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 @@ -1450,329 +1452,329 @@ yydestruct (yymsg, yytype, yyvaluep, yylocationp, parseio) 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: @@ -2095,57 +2097,57 @@ yyreduce: 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); @@ -2155,32 +2157,32 @@ yyreduce: 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); @@ -2188,7 +2190,7 @@ yyreduce: 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); @@ -2196,27 +2198,27 @@ yyreduce: 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); @@ -2224,12 +2226,12 @@ yyreduce: 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); @@ -2237,94 +2239,94 @@ yyreduce: 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); @@ -2332,7 +2334,7 @@ yyreduce: 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); @@ -2344,7 +2346,7 @@ yyreduce: 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); @@ -2353,7 +2355,7 @@ yyreduce: 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); @@ -2362,7 +2364,7 @@ yyreduce: 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); @@ -2372,22 +2374,22 @@ yyreduce: 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)); @@ -2396,12 +2398,12 @@ yyreduce: 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)])); @@ -2410,31 +2412,31 @@ yyreduce: 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); @@ -2442,12 +2444,12 @@ yyreduce: 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)); @@ -2456,12 +2458,12 @@ yyreduce: 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)); @@ -2469,7 +2471,7 @@ yyreduce: 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)); @@ -2477,7 +2479,7 @@ yyreduce: 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)); @@ -2485,12 +2487,12 @@ yyreduce: 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)); @@ -2499,7 +2501,7 @@ yyreduce: 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)); @@ -2509,12 +2511,12 @@ yyreduce: 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)); @@ -2522,7 +2524,7 @@ yyreduce: 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)); @@ -2530,7 +2532,7 @@ yyreduce: 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); @@ -2538,60 +2540,60 @@ yyreduce: 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); @@ -2601,7 +2603,7 @@ yyreduce: 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); @@ -2609,34 +2611,34 @@ yyreduce: 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; @@ -2673,22 +2675,22 @@ yyreduce: 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)); @@ -2696,41 +2698,41 @@ yyreduce: 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)])); @@ -2738,7 +2740,7 @@ yyreduce: 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)])); @@ -2746,7 +2748,7 @@ yyreduce: 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)])); @@ -2754,7 +2756,7 @@ yyreduce: 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)])); @@ -2762,24 +2764,24 @@ yyreduce: 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)])); @@ -2787,12 +2789,12 @@ yyreduce: 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)])); @@ -2801,19 +2803,19 @@ yyreduce: 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)])); @@ -2826,7 +2828,7 @@ yyreduce: 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 ) @@ -2837,49 +2839,49 @@ yyreduce: 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); @@ -2887,7 +2889,7 @@ yyreduce: 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; @@ -2895,7 +2897,7 @@ yyreduce: 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); @@ -2903,27 +2905,27 @@ yyreduce: 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); @@ -2931,47 +2933,47 @@ yyreduce: 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); @@ -2979,36 +2981,36 @@ yyreduce: 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); @@ -3227,7 +3229,7 @@ yyreturn: } -#line 642 "ael.y" +#line 644 "ael.y" static char *token_equivs1[] = diff --git a/res/ael/ael.tab.h b/res/ael/ael.tab.h index 95b0118523..02c09dbb9f 100644 --- a/res/ael/ael.tab.h +++ b/res/ael/ael.tab.h @@ -120,7 +120,7 @@ #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 */ diff --git a/res/ael/ael.y b/res/ael/ael.y index a8df1cb511..97558eca44 100644 --- a/res/ael/ael.y +++ b/res/ael/ael.y @@ -31,6 +31,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include #include "asterisk/logger.h" +#include "asterisk/lock.h" +#include "asterisk/hashtab.h" #include "asterisk/ael_structs.h" pval * linku1(pval *head, pval *tail); diff --git a/res/ael/ael_lex.c b/res/ael/ael_lex.c index 1f96e33778..b96cda20f8 100644 --- a/res/ael/ael_lex.c +++ b/res/ael/ael_lex.c @@ -804,9 +804,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #define GLOB_ABORTED GLOB_ABEND #endif # include - #include "asterisk/logger.h" #include "asterisk/utils.h" +#include "asterisk/lock.h" +#include "asterisk/hashtab.h" #include "ael/ael.tab.h" #include "asterisk/ael_structs.h" @@ -907,7 +908,7 @@ static void pbcwhere(const char *text, int *line, int *col ) #define STORE_POS #define STORE_LOC #endif -#line 909 "ael_lex.c" +#line 911 "ael_lex.c" #define INITIAL 0 #define paren 1 @@ -1146,10 +1147,10 @@ YY_DECL register int yy_act; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; -#line 187 "ael.flex" +#line 189 "ael.flex" -#line 1151 "ael_lex.c" +#line 1153 "ael_lex.c" yylval = yylval_param; @@ -1240,260 +1241,260 @@ do_action: /* This label is used only to access EOF actions. */ case 1: YY_RULE_SETUP -#line 189 "ael.flex" +#line 191 "ael.flex" { STORE_POS; return LC;} YY_BREAK case 2: YY_RULE_SETUP -#line 190 "ael.flex" +#line 192 "ael.flex" { STORE_POS; return RC;} YY_BREAK case 3: YY_RULE_SETUP -#line 191 "ael.flex" +#line 193 "ael.flex" { STORE_POS; return LP;} YY_BREAK case 4: YY_RULE_SETUP -#line 192 "ael.flex" +#line 194 "ael.flex" { STORE_POS; return RP;} YY_BREAK case 5: YY_RULE_SETUP -#line 193 "ael.flex" +#line 195 "ael.flex" { STORE_POS; return SEMI;} YY_BREAK case 6: YY_RULE_SETUP -#line 194 "ael.flex" +#line 196 "ael.flex" { STORE_POS; return EQ;} YY_BREAK case 7: YY_RULE_SETUP -#line 195 "ael.flex" +#line 197 "ael.flex" { STORE_POS; return COMMA;} YY_BREAK case 8: YY_RULE_SETUP -#line 196 "ael.flex" +#line 198 "ael.flex" { STORE_POS; return COLON;} YY_BREAK case 9: YY_RULE_SETUP -#line 197 "ael.flex" +#line 199 "ael.flex" { STORE_POS; return AMPER;} YY_BREAK case 10: YY_RULE_SETUP -#line 198 "ael.flex" +#line 200 "ael.flex" { STORE_POS; return BAR;} YY_BREAK case 11: YY_RULE_SETUP -#line 199 "ael.flex" +#line 201 "ael.flex" { STORE_POS; return EXTENMARK;} YY_BREAK case 12: YY_RULE_SETUP -#line 200 "ael.flex" +#line 202 "ael.flex" { STORE_POS; return AT;} YY_BREAK case 13: YY_RULE_SETUP -#line 201 "ael.flex" +#line 203 "ael.flex" {/*comment*/} YY_BREAK case 14: YY_RULE_SETUP -#line 202 "ael.flex" +#line 204 "ael.flex" { STORE_POS; return KW_CONTEXT;} YY_BREAK case 15: YY_RULE_SETUP -#line 203 "ael.flex" +#line 205 "ael.flex" { STORE_POS; return KW_ABSTRACT;} YY_BREAK case 16: YY_RULE_SETUP -#line 204 "ael.flex" +#line 206 "ael.flex" { STORE_POS; return KW_EXTEND;} YY_BREAK case 17: YY_RULE_SETUP -#line 205 "ael.flex" +#line 207 "ael.flex" { STORE_POS; return KW_MACRO;}; YY_BREAK case 18: YY_RULE_SETUP -#line 206 "ael.flex" +#line 208 "ael.flex" { STORE_POS; return KW_GLOBALS;} YY_BREAK case 19: YY_RULE_SETUP -#line 207 "ael.flex" +#line 209 "ael.flex" { STORE_POS; return KW_LOCAL;} YY_BREAK case 20: YY_RULE_SETUP -#line 208 "ael.flex" +#line 210 "ael.flex" { STORE_POS; return KW_IGNOREPAT;} YY_BREAK case 21: YY_RULE_SETUP -#line 209 "ael.flex" +#line 211 "ael.flex" { STORE_POS; return KW_SWITCH;} YY_BREAK case 22: YY_RULE_SETUP -#line 210 "ael.flex" +#line 212 "ael.flex" { STORE_POS; return KW_IF;} YY_BREAK case 23: YY_RULE_SETUP -#line 211 "ael.flex" +#line 213 "ael.flex" { STORE_POS; return KW_IFTIME;} YY_BREAK case 24: YY_RULE_SETUP -#line 212 "ael.flex" +#line 214 "ael.flex" { STORE_POS; return KW_RANDOM;} YY_BREAK case 25: YY_RULE_SETUP -#line 213 "ael.flex" +#line 215 "ael.flex" { STORE_POS; return KW_REGEXTEN;} YY_BREAK case 26: YY_RULE_SETUP -#line 214 "ael.flex" +#line 216 "ael.flex" { STORE_POS; return KW_HINT;} YY_BREAK case 27: YY_RULE_SETUP -#line 215 "ael.flex" +#line 217 "ael.flex" { STORE_POS; return KW_ELSE;} YY_BREAK case 28: YY_RULE_SETUP -#line 216 "ael.flex" +#line 218 "ael.flex" { STORE_POS; return KW_GOTO;} YY_BREAK case 29: YY_RULE_SETUP -#line 217 "ael.flex" +#line 219 "ael.flex" { STORE_POS; return KW_JUMP;} YY_BREAK case 30: YY_RULE_SETUP -#line 218 "ael.flex" +#line 220 "ael.flex" { STORE_POS; return KW_RETURN;} YY_BREAK case 31: YY_RULE_SETUP -#line 219 "ael.flex" +#line 221 "ael.flex" { STORE_POS; return KW_BREAK;} YY_BREAK case 32: YY_RULE_SETUP -#line 220 "ael.flex" +#line 222 "ael.flex" { STORE_POS; return KW_CONTINUE;} YY_BREAK case 33: YY_RULE_SETUP -#line 221 "ael.flex" +#line 223 "ael.flex" { STORE_POS; return KW_FOR;} YY_BREAK case 34: YY_RULE_SETUP -#line 222 "ael.flex" +#line 224 "ael.flex" { STORE_POS; return KW_WHILE;} YY_BREAK case 35: YY_RULE_SETUP -#line 223 "ael.flex" +#line 225 "ael.flex" { STORE_POS; return KW_CASE;} YY_BREAK case 36: YY_RULE_SETUP -#line 224 "ael.flex" +#line 226 "ael.flex" { STORE_POS; return KW_DEFAULT;} YY_BREAK case 37: YY_RULE_SETUP -#line 225 "ael.flex" +#line 227 "ael.flex" { STORE_POS; return KW_PATTERN;} YY_BREAK case 38: YY_RULE_SETUP -#line 226 "ael.flex" +#line 228 "ael.flex" { STORE_POS; return KW_CATCH;} YY_BREAK case 39: YY_RULE_SETUP -#line 227 "ael.flex" +#line 229 "ael.flex" { STORE_POS; return KW_SWITCHES;} YY_BREAK case 40: YY_RULE_SETUP -#line 228 "ael.flex" +#line 230 "ael.flex" { STORE_POS; return KW_ESWITCHES;} YY_BREAK case 41: YY_RULE_SETUP -#line 229 "ael.flex" +#line 231 "ael.flex" { STORE_POS; return KW_INCLUDES;} YY_BREAK case 42: YY_RULE_SETUP -#line 230 "ael.flex" +#line 232 "ael.flex" { BEGIN(comment); my_col += 2; } YY_BREAK case 43: YY_RULE_SETUP -#line 232 "ael.flex" +#line 234 "ael.flex" { my_col += yyleng; } YY_BREAK case 44: /* rule 44 can match eol */ YY_RULE_SETUP -#line 233 "ael.flex" +#line 235 "ael.flex" { ++my_lineno; my_col=1;} YY_BREAK case 45: YY_RULE_SETUP -#line 234 "ael.flex" +#line 236 "ael.flex" { my_col += yyleng; } YY_BREAK case 46: /* rule 46 can match eol */ YY_RULE_SETUP -#line 235 "ael.flex" +#line 237 "ael.flex" { ++my_lineno; my_col=1;} YY_BREAK case 47: YY_RULE_SETUP -#line 236 "ael.flex" +#line 238 "ael.flex" { my_col += 2; BEGIN(INITIAL); } YY_BREAK case 48: /* rule 48 can match eol */ YY_RULE_SETUP -#line 238 "ael.flex" +#line 240 "ael.flex" { my_lineno++; my_col = 1; } YY_BREAK case 49: YY_RULE_SETUP -#line 239 "ael.flex" +#line 241 "ael.flex" { my_col += yyleng; } YY_BREAK case 50: YY_RULE_SETUP -#line 240 "ael.flex" +#line 242 "ael.flex" { my_col += (yyleng*8)-(my_col%8); } YY_BREAK case 51: YY_RULE_SETUP -#line 242 "ael.flex" +#line 244 "ael.flex" { STORE_POS; yylval->str = strdup(yytext); @@ -1511,7 +1512,7 @@ YY_RULE_SETUP case 52: /* rule 52 can match eol */ YY_RULE_SETUP -#line 258 "ael.flex" +#line 260 "ael.flex" { if ( pbcpop(')') ) { /* error */ STORE_LOC; @@ -1537,7 +1538,7 @@ YY_RULE_SETUP case 53: /* rule 53 can match eol */ YY_RULE_SETUP -#line 280 "ael.flex" +#line 282 "ael.flex" { char c = yytext[yyleng-1]; if (c == '(') @@ -1549,7 +1550,7 @@ YY_RULE_SETUP case 54: /* rule 54 can match eol */ YY_RULE_SETUP -#line 288 "ael.flex" +#line 290 "ael.flex" { char c = yytext[yyleng-1]; if ( pbcpop(c)) { /* error */ @@ -1574,7 +1575,7 @@ YY_RULE_SETUP case 55: /* rule 55 can match eol */ YY_RULE_SETUP -#line 310 "ael.flex" +#line 312 "ael.flex" { char c = yytext[yyleng-1]; if (c == '(') @@ -1586,7 +1587,7 @@ YY_RULE_SETUP case 56: /* rule 56 can match eol */ YY_RULE_SETUP -#line 318 "ael.flex" +#line 320 "ael.flex" { if ( pbcpop(')') ) { /* error */ STORE_LOC; @@ -1614,7 +1615,7 @@ YY_RULE_SETUP case 57: /* rule 57 can match eol */ YY_RULE_SETUP -#line 342 "ael.flex" +#line 344 "ael.flex" { if( parencount != 0) { /* printf("Folding in a comma!\n"); */ yymore(); @@ -1632,7 +1633,7 @@ YY_RULE_SETUP case 58: /* rule 58 can match eol */ YY_RULE_SETUP -#line 356 "ael.flex" +#line 358 "ael.flex" { char c = yytext[yyleng-1]; if ( pbcpop(c) ) { /* error */ @@ -1653,7 +1654,7 @@ YY_RULE_SETUP case 59: /* rule 59 can match eol */ YY_RULE_SETUP -#line 373 "ael.flex" +#line 375 "ael.flex" { char c = yytext[yyleng-1]; yymore(); @@ -1663,7 +1664,7 @@ YY_RULE_SETUP case 60: /* rule 60 can match eol */ YY_RULE_SETUP -#line 379 "ael.flex" +#line 381 "ael.flex" { char c = yytext[yyleng-1]; if ( pbcpop(c) ) { /* error */ @@ -1679,7 +1680,7 @@ YY_RULE_SETUP case 61: /* rule 61 can match eol */ YY_RULE_SETUP -#line 391 "ael.flex" +#line 393 "ael.flex" { STORE_LOC; yylval->str = strdup(yytext); @@ -1692,7 +1693,7 @@ YY_RULE_SETUP case 62: /* rule 62 can match eol */ YY_RULE_SETUP -#line 400 "ael.flex" +#line 402 "ael.flex" { char fnamebuf[1024],*p1,*p2; int glob_ret; @@ -1742,7 +1743,7 @@ case YY_STATE_EOF(paren): case YY_STATE_EOF(semic): case YY_STATE_EOF(argg): case YY_STATE_EOF(comment): -#line 445 "ael.flex" +#line 447 "ael.flex" { char fnamebuf[2048]; if (include_stack_index > 0 && include_stack[include_stack_index-1].globbuf_pos < include_stack[include_stack_index-1].globbuf.gl_pathc-1) { @@ -1777,10 +1778,10 @@ case YY_STATE_EOF(comment): YY_BREAK case 63: YY_RULE_SETUP -#line 479 "ael.flex" +#line 481 "ael.flex" ECHO; YY_BREAK -#line 1784 "ael_lex.c" +#line 1786 "ael_lex.c" case YY_END_OF_BUFFER: { @@ -2905,7 +2906,7 @@ void *ael_yyrealloc (void * ptr, yy_size_t size , yyscan_t yyscanner) #define YYTABLES_NAME "yytables" -#line 479 "ael.flex" +#line 481 "ael.flex" diff --git a/res/ael/pval.c b/res/ael/pval.c index 44bc0a12d3..fa9625a3d2 100644 --- a/res/ael/pval.c +++ b/res/ael/pval.c @@ -4006,7 +4006,7 @@ static void fix_gotos_in_extensions(struct ael_extension *exten) } -void ast_compile_ael2(struct ast_context **local_contexts, struct pval *root) +void ast_compile_ael2(struct ast_context **local_contexts, struct ast_hashtab *local_table, struct pval *root) { pval *p,*p2; struct ast_context *context; @@ -4037,7 +4037,7 @@ void ast_compile_ael2(struct ast_context **local_contexts, struct pval *root) switch (p->type) { case PV_MACRO: - context = ast_context_create(local_contexts, p->u1.str, registrar); + context = ast_context_find_or_create(local_contexts, local_table, p->u1.str, registrar); exten = new_exten(); exten->context = context; @@ -4075,7 +4075,7 @@ void ast_compile_ael2(struct ast_context **local_contexts, struct pval *root) break; case PV_CONTEXT: - context = ast_context_find_or_create(local_contexts, p->u1.str, registrar); + context = ast_context_find_or_create(local_contexts, local_table, p->u1.str, registrar); /* contexts contain: ignorepat, includes, switches, eswitches, extensions, */ for (p2=p->u2.statements; p2; p2=p2->next) { diff --git a/utils/Makefile b/utils/Makefile index fe4a2dbfef..87b79302cf 100644 --- a/utils/Makefile +++ b/utils/Makefile @@ -130,7 +130,7 @@ aelparse.c: $(ASTTOPDIR)/res/ael/ael_lex.c aelparse.o: ASTCFLAGS+=-I$(ASTTOPDIR)/res -DSTANDALONE_AEL -Wno-unused -aelparse: aelparse.o aelbison.o pbx_ael.o ael_main.o ast_expr2f.o ast_expr2.o strcompat.o pval.o extconf.o +aelparse: aelparse.o aelbison.o pbx_ael.o hashtab.o ael_main.o ast_expr2f.o ast_expr2.o strcompat.o pval.o extconf.o astobj2.c: $(ASTTOPDIR)/main/astobj2.c @cp $< $@ @@ -154,7 +154,7 @@ hashtest.o: ASTCFLAGS+=-O0 extconf.o: extconf.c -conf2ael: conf2ael.o ast_expr2f.o ast_expr2.o aelbison.o aelparse.o pbx_ael.o pval.o extconf.o strcompat.o +conf2ael: conf2ael.o ast_expr2f.o ast_expr2.o hashtab.o aelbison.o aelparse.o pbx_ael.o pval.o extconf.o strcompat.o testexpr2s: $(ASTTOPDIR)/main/ast_expr2f.c $(ASTTOPDIR)/main/ast_expr2.c $(ASTTOPDIR)/main/ast_expr2.h $(CC) -g -c -I$(ASTTOPDIR)/include -DSTANDALONE_AEL $(ASTTOPDIR)/main/ast_expr2f.c -o ast_expr2f.o diff --git a/utils/ael_main.c b/utils/ael_main.c index 7174cf9425..ae5dbd10b8 100644 --- a/utils/ael_main.c +++ b/utils/ael_main.c @@ -19,6 +19,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/ast_expr.h" #include "asterisk/module.h" #include "asterisk/app.h" +#include "asterisk/lock.h" +#include "asterisk/hashtab.h" #include "asterisk/ael_structs.h" #include "asterisk/extconf.h" @@ -557,3 +559,33 @@ int main(int argc, char **argv) return 0; } +int ast_hashtab_compare_contexts(const void *ah_a, const void *ah_b); + +int ast_hashtab_compare_contexts(const void *ah_a, const void *ah_b) +{ + return 0; +} + +unsigned int ast_hashtab_hash_contexts(const void *obj); + +unsigned int ast_hashtab_hash_contexts(const void *obj) +{ + return 0; +} + +#ifdef DEBUG_THREADS + +void ast_mark_lock_acquired(void *lock_addr) +{ +} + +void ast_remove_lock_info(void *lock_addr) +{ +} + +void ast_store_lock_info(enum ast_lock_type type, const char *filename, + int line_num, const char *func, const char *lock_name, void *lock_addr) +{ +} + +#endif diff --git a/utils/conf2ael.c b/utils/conf2ael.c index 02d28af0df..7ece70e23e 100644 --- a/utils/conf2ael.c +++ b/utils/conf2ael.c @@ -47,6 +47,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/config.h" #include "asterisk/options.h" #include "asterisk/callerid.h" +#include "asterisk/lock.h" +#include "asterisk/hashtab.h" #include "asterisk/ael_structs.h" #include "asterisk/devicestate.h" #include "asterisk/stringfields.h" @@ -607,14 +609,7 @@ int ast_context_add_include2(struct ast_context *con, const char *value, return localized_context_add_include2(con, value,registrar); } -struct ast_context *ast_context_create(struct ast_context **extcontexts, const char *name, const char *registrar) -{ - printf("Creating context %s, registrar=%s\n", name, registrar); - - return localized_context_create(extcontexts, name, registrar); -} - -struct ast_context *ast_context_find_or_create(struct ast_context **extcontexts, const char *name, const char *registrar) +struct ast_context *ast_context_find_or_create(struct ast_context **extcontexts, struct ast_hashtab *exttable, const char *name, const char *registrar) { printf("find/Creating context %s, registrar=%s\n", name, registrar); @@ -661,9 +656,9 @@ int ast_context_verify_includes(struct ast_context *con) return localized_context_verify_includes(con); } -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); -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) { localized_merge_contexts_and_delete(extcontexts, registrar); } @@ -691,3 +686,33 @@ struct ast_exten *pbx_find_extension(struct ast_channel *chan, return localized_find_extension(bypass, q, context, exten, priority, label, callerid, action); } +int ast_hashtab_compare_contexts(const void *ah_a, const void *ah_b); + +int ast_hashtab_compare_contexts(const void *ah_a, const void *ah_b) +{ + return 0; +} + +unsigned int ast_hashtab_hash_contexts(const void *obj); + +unsigned int ast_hashtab_hash_contexts(const void *obj) +{ + return 0; +} + +#ifdef DEBUG_THREADS + +void ast_mark_lock_acquired(void *lock_addr) +{ +} + +void ast_remove_lock_info(void *lock_addr) +{ +} + +void ast_store_lock_info(enum ast_lock_type type, const char *filename, + int line_num, const char *func, const char *lock_name, void *lock_addr) +{ +} + +#endif diff --git a/utils/extconf.c b/utils/extconf.c index 0afcc02229..d388586e03 100644 --- a/utils/extconf.c +++ b/utils/extconf.c @@ -23,6 +23,7 @@ * 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 */ @@ -65,10 +66,9 @@ struct ast_channel #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" @@ -147,1714 +147,1718 @@ void ast_console_toggle_mute(int fd); #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( -__attribute__((format (printf, 5, 6))) -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( -__attribute__((format (printf, 5, 0))) -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; +#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 (!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; + } return res; } -) -#else +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 astmm is in use, let it handle these. Otherwise, it will report that - all allocations are coming from this header file */ +#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 */ -#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) + 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); + } -#endif /* AST_DEBUG_MALLOC */ + return res; +} -#if !defined(ast_strdupa) && defined(__GNUC__) -/*! - \brief duplicate a string in memory from the stack - \param s The string to duplicate +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"); - 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; \ - })) +#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; + } -/* from config.c */ - -#define MAX_NESTED_COMMENTS 128 -#define COMMENT_START ";--" -#define COMMENT_END "--;" -#define COMMENT_META ';' -#define COMMENT_TAG '-' + 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 *extconfig_conf = "extconfig.conf"; + 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; + } -/*! 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 ((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 char *lline_buffer; /*!< A buffer for stuff behind the ; */ -static int lline_buffer_size; + return res; +} -#define CB_INCR 250 +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) +{ + return pthread_cond_init(cond, cond_attr); +} -struct ast_comment { - struct ast_comment *next; - char cmt[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); +} -static void CB_INIT(void) +static inline int __ast_cond_broadcast(const char *filename, int lineno, const char *func, + const char *cond_name, ast_cond_t *cond) { - 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_broadcast(cond); } -static void CB_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 = 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_destroy(cond); } -static void CB_ADD_LEN(char *str, int len) +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 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; + 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); } - strncat(comment_buffer,str,len); /* safe */ - comment_buffer[cbl+len-1] = 0; -} +#endif -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; + 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; } - strcat(lline_buffer,str); -} -static void CB_RESET(void ) -{ - comment_buffer[0] = 0; - lline_buffer[0] = 0; + 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_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; } - -/*! \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_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 -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_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; } -void ast_unreplace_sigchld(void); - -void ast_unreplace_sigchld(void) -{ - unsigned int level; +#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) - level = --safe_system_level; +#else /* !DEBUG_THREADS */ - /* only restore the handler if we are the last one */ - if (level == 0) - signal(SIGCHLD, safe_system_prev_handler); -} +typedef pthread_mutex_t ast_mutex_t; -int ast_safe_system(const char *s); +#define AST_MUTEX_INIT_VALUE ((ast_mutex_t) PTHREAD_MUTEX_INIT_VALUE) -int ast_safe_system(const char *s) +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 DAHDI 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( +__attribute__((format (printf, 5, 6))) +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( +__attribute__((format (printf, 5, 0))) +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 DAHDI 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 */