Skip to content
Snippets Groups Projects
config.c 107 KiB
Newer Older
  • Learn to ignore specific revisions
  • 	return 0;
    
    int ast_realtime_is_mapping_defined(const char *family)
    {
    	struct ast_config_map *map;
    
    	SCOPED_MUTEX(lock, &config_lock);
    
    
    	for (map = config_maps; map; map = map->next) {
    		if (!strcasecmp(family, map->name)) {
    			return 1;
    		}
    	}
    
    	ast_debug(5, "Failed to find a realtime mapping for %s\n", family);
    
    Olle Johansson's avatar
    Olle Johansson committed
    /*! \brief Find realtime engine for realtime family */
    
    static struct ast_config_engine *find_engine(const char *family, int priority, char *database, int dbsiz, char *table, int tabsiz)
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    	struct ast_config_engine *eng, *ret = NULL;
    
    	struct ast_config_map *map;
    
    	SCOPED_MUTEX(lock, &config_lock);
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    	for (map = config_maps; map; map = map->next) {
    
    		if (!strcasecmp(family, map->name) && (priority == map->priority)) {
    
    			if (database)
    				ast_copy_string(database, map->database, dbsiz);
    			if (table)
    				ast_copy_string(table, map->table ? map->table : family, tabsiz);
    
    
    	/* Check if the required driver (engine) exist */
    
    	if (map) {
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    		for (eng = config_engine_list; !ret && eng; eng = eng->next) {
    			if (!strcasecmp(eng->name, map->driver))
    
    				ret = eng;
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    
    	/* if we found a mapping, but the engine is not available, then issue a warning */
    	if (map && !ret)
    
    		ast_log(LOG_WARNING, "Realtime mapping for '%s' found to engine '%s', but the engine is not available\n", map->name, map->driver);
    
    Mark Spencer's avatar
    Mark Spencer committed
    }
    
    static struct ast_config_engine text_file_engine = {
    	.name = "text",
    	.load_func = config_text_file_load,
    };
    
    struct ast_config *ast_config_copy(const struct ast_config *old)
    {
    	struct ast_config *new_config = ast_config_new();
    	struct ast_category *cat_iter;
    
    	if (!new_config) {
    		return NULL;
    	}
    
    	for (cat_iter = old->root; cat_iter; cat_iter = cat_iter->next) {
    		struct ast_category *new_cat =
    			ast_category_new(cat_iter->name, cat_iter->file, cat_iter->lineno);
    		if (!new_cat) {
    			goto fail;
    		}
    		ast_category_append(new_config, new_cat);
    		if (cat_iter->root) {
    			new_cat->root = ast_variables_dup(cat_iter->root);
    			if (!new_cat->root) {
    				goto fail;
    			}
    			new_cat->last = cat_iter->last;
    		}
    	}
    
    	return new_config;
    
    fail:
    	ast_config_destroy(new_config);
    	return NULL;
    }
    
    
    
    struct ast_config *ast_config_internal_load(const char *filename, struct ast_config *cfg, struct ast_flags flags, const char *suggested_include_file, const char *who_asked)
    
    	struct ast_config_engine *loader = &text_file_engine;
    
    	struct ast_config *result;
    
    	/* The config file itself bumps include_level by 1 */
    	if (cfg->max_include_level > 0 && cfg->include_level == cfg->max_include_level + 1) {
    
    		ast_log(LOG_WARNING, "Maximum Include level (%d) exceeded\n", cfg->max_include_level);
    		return NULL;
    
    
    	cfg->include_level++;
    
    
    	if (!ast_test_flag(&flags, CONFIG_FLAG_NOREALTIME) && config_engine_list) {
    
    		struct ast_config_engine *eng;
    
    
    		eng = find_engine(filename, 1, db, sizeof(db), table, sizeof(table));
    
    		if (eng && eng->load_func) {
    			loader = eng;
    		} else {
    
    			eng = find_engine("global", 1, db, sizeof(db), table, sizeof(table));
    
    			if (eng && eng->load_func)
    				loader = eng;
    		}
    	}
    
    
    	result = loader->load_func(db, table, filename, cfg, flags, suggested_include_file, who_asked);
    
    	if (result && result != CONFIG_STATUS_FILEINVALID && result != CONFIG_STATUS_FILEUNCHANGED) {
    
    		result->include_level--;
    
    		config_hook_exec(filename, who_asked, result);
    	} else if (result != CONFIG_STATUS_FILEINVALID) {
    
    		cfg->include_level--;
    
    
    	return result;
    
    struct ast_config *ast_config_load2(const char *filename, const char *who_asked, struct ast_flags flags)
    
    {
    	struct ast_config *cfg;
    	struct ast_config *result;
    
    	cfg = ast_config_new();
    	if (!cfg)
    		return NULL;
    
    
    	result = ast_config_internal_load(filename, cfg, flags, "", who_asked);
    
    	if (!result || result == CONFIG_STATUS_FILEUNCHANGED || result == CONFIG_STATUS_FILEINVALID)
    
    		ast_config_destroy(cfg);
    
    	return result;
    
    #define realtime_arguments_to_fields(ap, result) realtime_arguments_to_fields2(ap, 0, result)
    
    /*!
     * \internal
     * \brief
     *
     * \param ap list of variable arguments
     * \param skip Skip argument pairs for this number of variables
     * \param result Address of a variables pointer to store the results
     *               May be NULL if no arguments are parsed
     *               Will be NULL on failure.
     *
     * \retval 0 on success or empty ap list
     * \retval -1 on failure
     */
    static int realtime_arguments_to_fields2(va_list ap, int skip, struct ast_variable **result)
    
    {
    	struct ast_variable *first, *fields = NULL;
    
    	const char *newparam;
    	const char *newval;
    
    	/*
    	 * Previously we would do:
    	 *
    	 *     va_start(ap, last);
    	 *     x = realtime_arguments_to_fields(ap);
    	 *     y = realtime_arguments_to_fields(ap);
    	 *     va_end(ap);
    	 *
    	 * While this works on generic amd64 machines (2014), it doesn't on the
    	 * raspberry PI. The va_arg() manpage says:
    	 *
    	 *     If ap is passed to a function that uses va_arg(ap,type) then
    	 *     the value of ap is undefined after the return of that function.
    	 *
    	 * On the raspberry, ap seems to get reset after the call: the contents
    	 * of y would be equal to the contents of x.
    	 *
    	 * So, instead we allow the caller to skip past earlier argument sets
    	 * using the skip parameter:
    	 *
    	 *     va_start(ap, last);
    
    	 *     if (realtime_arguments_to_fields(ap, &x)) {
    	 *         // FAILURE CONDITIONS
    	 *     }
    
    	 *     va_end(ap);
    	 *     va_start(ap, last);
    
    	 *     if (realtime_arguments_to_fields2(ap, 1, &y)) {
    	 *         // FAILURE CONDITIONS
    	 *     }
    
    	 *     va_end(ap);
    	 */
    	while (skip--) {
    		/* There must be at least one argument. */
    		newparam = va_arg(ap, const char *);
    		newval = va_arg(ap, const char *);
    		while ((newparam = va_arg(ap, const char *))) {
    			newval = va_arg(ap, const char *);
    		}
    	}
    
    	/* Load up the first vars. */
    	newparam = va_arg(ap, const char *);
    
    	newval = va_arg(ap, const char *);
    
    
    	if (!(first = ast_variable_new(newparam, newval, ""))) {
    
    	}
    
    	while ((newparam = va_arg(ap, const char *))) {
    		struct ast_variable *field;
    
    		newval = va_arg(ap, const char *);
    		if (!(field = ast_variable_new(newparam, newval, ""))) {
    			ast_variables_destroy(fields);
    			ast_variables_destroy(first);
    
    		}
    
    		field->next = fields;
    		fields = field;
    	}
    
    	first->next = fields;
    	fields = first;
    
    
    }
    
    struct ast_variable *ast_load_realtime_all_fields(const char *family, const struct ast_variable *fields)
    
    	struct ast_config_engine *eng;
    
    	struct ast_variable *res=NULL;
    
    	for (i = 1; ; i++) {
    		if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
    
    			if (eng->realtime_func && (res = eng->realtime_func(db, table, fields))) {
    
    				return res;
    			}
    		} else {
    			return NULL;
    		}
    	}
    
    
    	return res;
    }
    
    struct ast_variable *ast_load_realtime_all(const char *family, ...)
    {
    
    	RAII_VAR(struct ast_variable *, fields, NULL, ast_variables_destroy);
    	struct ast_variable *res = NULL;
    
    	va_list ap;
    
    	va_start(ap, family);
    
    	realtime_arguments_to_fields(ap, &fields);
    
    	if (fields) {
    		res = ast_load_realtime_all_fields(family, fields);
    	}
    
    
    	return res;
    
    struct ast_variable *ast_load_realtime_fields(const char *family, const struct ast_variable *fields)
    
    	struct ast_variable *res;
    	struct ast_variable *cur;
    	struct ast_variable **prev;
    
    	res = ast_load_realtime_all_fields(family, fields);
    
    	/* Filter the list. */
    	prev = &res;
    	cur = res;
    	while (cur) {
    
    			/* Eliminate empty entries */
    			struct ast_variable *next;
    
    			next = cur->next;
    			*prev = next;
    			ast_variable_destroy(cur);
    			cur = next;
    
    			/* Make blank entries empty and keep them. */
    			if (cur->value[0] == ' ' && cur->value[1] == '\0') {
    				char *vptr = (char *) cur->value;
    
    				vptr[0] = '\0';
    			}
    
    			prev = &cur->next;
    			cur = cur->next;
    
    struct ast_variable *ast_load_realtime(const char *family, ...)
    {
    	RAII_VAR(struct ast_variable *, fields, NULL, ast_variables_destroy);
    
    	va_list ap;
    
    	va_start(ap, family);
    
    	if (realtime_arguments_to_fields(ap, &fields)) {
    		field_res = -1;
    	}
    
    	return ast_load_realtime_fields(family, fields);
    }
    
    
    Olle Johansson's avatar
    Olle Johansson committed
    /*! \brief Check if realtime engine is configured for family */
    
    int ast_check_realtime(const char *family)
    {
    	struct ast_config_engine *eng;
    
    	if (!ast_realtime_enabled()) {
    		return 0;	/* There are no engines at all so fail early */
    	}
    
    	eng = find_engine(family, 1, NULL, 0, NULL, 0);
    
    /*! \brief Check if there's any realtime engines loaded */
    
    int ast_realtime_enabled(void)
    
    int ast_realtime_require_field(const char *family, ...)
    {
    	struct ast_config_engine *eng;
    
    	int res = -1, i;
    
    	for (i = 1; ; i++) {
    		if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
    			/* If the require succeeds, it returns 0. */
    			if (eng->require_func && !(res = eng->require_func(db, table, ap))) {
    				break;
    			}
    		} else {
    			break;
    		}
    
    	}
    	va_end(ap);
    
    	return res;
    }
    
    int ast_unload_realtime(const char *family)
    {
    	struct ast_config_engine *eng;
    
    	int res = -1, i;
    
    	for (i = 1; ; i++) {
    		if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
    			if (eng->unload_func) {
    				/* Do this for ALL engines */
    				res = eng->unload_func(db, table);
    			}
    		} else {
    			break;
    		}
    
    struct ast_config *ast_load_realtime_multientry_fields(const char *family, const struct ast_variable *fields)
    
    	struct ast_config_engine *eng;
    
    	char db[256];
    	char table[256];
    	struct ast_config *res = NULL;
    
    	for (i = 1; ; i++) {
    		if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
    
    			if (eng->realtime_multi_func && (res = eng->realtime_multi_func(db, table, fields))) {
    
    				/* If we were returned an empty cfg, destroy it and return NULL */
    				if (!res->root) {
    					ast_config_destroy(res);
    					res = NULL;
    				}
    
    struct ast_config *ast_load_realtime_multientry(const char *family, ...)
    {
    	RAII_VAR(struct ast_variable *, fields, NULL, ast_variables_destroy);
    	va_list ap;
    
    	va_start(ap, family);
    
    	realtime_arguments_to_fields(ap, &fields);
    
    	return ast_load_realtime_multientry_fields(family, fields);
    }
    
    int ast_update_realtime_fields(const char *family, const char *keyfield, const char *lookup, const struct ast_variable *fields)
    
    	struct ast_config_engine *eng;
    
    	int res = -1, i;
    
    	for (i = 1; ; i++) {
    		if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
    
    			/* If the update succeeds, it returns >= 0. */
    			if (eng->update_func && ((res = eng->update_func(db, table, keyfield, lookup, fields)) >= 0)) {
    
    int ast_update_realtime(const char *family, const char *keyfield, const char *lookup, ...)
    {
    	RAII_VAR(struct ast_variable *, fields, NULL, ast_variables_destroy);
    	va_list ap;
    
    	va_start(ap, lookup);
    
    	realtime_arguments_to_fields(ap, &fields);
    
    	return ast_update_realtime_fields(family, keyfield, lookup, fields);
    }
    
    int ast_update2_realtime_fields(const char *family, const struct ast_variable *lookup_fields, const struct ast_variable *update_fields)
    
    	int res = -1, i;
    
    	for (i = 1; ; i++) {
    		if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
    
    			if (eng->update2_func && !(res = eng->update2_func(db, table, lookup_fields, update_fields))) {
    
    int ast_update2_realtime(const char *family, ...)
    {
    	RAII_VAR(struct ast_variable *, lookup_fields, NULL, ast_variables_destroy);
    	RAII_VAR(struct ast_variable *, update_fields, NULL, ast_variables_destroy);
    	va_list ap;
    
    	va_start(ap, family);
    
    	/* XXX: If we wanted to pass no lookup fields (select all), we'd be
    	 * out of luck. realtime_arguments_to_fields expects at least one key
    	 * value pair. */
    
    	realtime_arguments_to_fields(ap, &lookup_fields);
    
    	realtime_arguments_to_fields2(ap, 1, &update_fields);
    
    	va_end(ap);
    
    	if (!lookup_fields || !update_fields) {
    		return -1;
    	}
    
    
    	return ast_update2_realtime_fields(family, lookup_fields, update_fields);
    }
    
    int ast_store_realtime_fields(const char *family, const struct ast_variable *fields)
    
    	int res = -1, i;
    
    	for (i = 1; ; i++) {
    		if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
    
    			/* If the store succeeds, it returns >= 0*/
    			if (eng->store_func && ((res = eng->store_func(db, table, fields)) >= 0)) {
    
    int ast_store_realtime(const char *family, ...)
    {
    	RAII_VAR(struct ast_variable *, fields, NULL, ast_variables_destroy);
    	va_list ap;
    
    	va_start(ap, family);
    
    	realtime_arguments_to_fields(ap, &fields);
    
    	return ast_store_realtime_fields(family, fields);
    }
    
    int ast_destroy_realtime_fields(const char *family, const char *keyfield, const char *lookup, const struct ast_variable *fields)
    
    	int res = -1, i;
    
    	for (i = 1; ; i++) {
    		if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
    
    			if (eng->destroy_func && !(res = eng->destroy_func(db, table, keyfield, lookup, fields))) {
    
    	return res;
    
    int ast_destroy_realtime(const char *family, const char *keyfield, const char *lookup, ...)
    {
    	RAII_VAR(struct ast_variable *, fields, NULL, ast_variables_destroy);
    
    	va_list ap;
    
    	va_start(ap, lookup);
    
    	if (realtime_arguments_to_fields(ap, &fields)) {
    		res = -1;
    	}
    
    	return ast_destroy_realtime_fields(family, keyfield, lookup, fields);
    }
    
    
    char *ast_realtime_decode_chunk(char *chunk)
    {
    	char *orig = chunk;
    	for (; *chunk; chunk++) {
    		if (*chunk == '^' && strchr("0123456789ABCDEFabcdef", chunk[1]) && strchr("0123456789ABCDEFabcdef", chunk[2])) {
    
    			sscanf(chunk + 1, "%02hhX", (unsigned char *)chunk);
    
    			memmove(chunk + 1, chunk + 3, strlen(chunk + 3) + 1);
    		}
    	}
    	return orig;
    }
    
    char *ast_realtime_encode_chunk(struct ast_str **dest, ssize_t maxlen, const char *chunk)
    {
    	if (!strchr(chunk, ';') && !strchr(chunk, '^')) {
    		ast_str_set(dest, maxlen, "%s", chunk);
    	} else {
    		ast_str_reset(*dest);
    		for (; *chunk; chunk++) {
    			if (strchr(";^", *chunk)) {
    				ast_str_append(dest, maxlen, "^%02hhX", *chunk);
    			} else {
    				ast_str_append(dest, maxlen, "%c", *chunk);
    			}
    		}
    	}
    	return ast_str_buffer(*dest);
    }
    
    
    /*! \brief Helper function to parse arguments
     * See documentation in config.h
     */
    int ast_parse_arg(const char *arg, enum ast_parse_flags flags,
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    	void *p_result, ...)
    
    {
    	va_list ap;
    	int error = 0;
    
    	va_start(ap, p_result);
    	switch (flags & PARSE_TYPE) {
    	case PARSE_INT32:
    
    	{
    		long int x = 0;
    
    		int32_t def = result ? *result : 0, high = INT32_MAX, low = INT32_MIN;
    		char *endptr = NULL;
    
    		/* optional arguments: default value and/or (low, high) */
    		if (flags & PARSE_DEFAULT) {
    
    		}
    		if (flags & (PARSE_IN_RANGE | PARSE_OUT_RANGE)) {
    
    			low = va_arg(ap, int32_t);
    			high = va_arg(ap, int32_t);
    		}
    
    		if (ast_strlen_zero(arg)) {
    			error = 1;
    			goto int32_done;
    		}
    
    		x = strtol(arg, &endptr, 0);
    
    		if (*endptr || errno || x < INT32_MIN || x > INT32_MAX) {
    
    			/* Parse error, or type out of int32_t bounds */
    			error = 1;
    			goto int32_done;
    		}
    
    		error = (x < low) || (x > high);
    
    		if (flags & PARSE_RANGE_DEFAULTS) {
    			if (x < low) {
    				def = low;
    			} else if (x > high) {
    				def = high;
    			}
    		}
    
    		if (flags & PARSE_OUT_RANGE) {
    
    		}
    int32_done:
    		if (result) {
    
    		}
    
    		ast_debug(3, "extract int from [%s] in [%d, %d] gives [%ld](%d)\n",
    				arg, low, high, result ? *result : x, error);
    
    	{
    		unsigned long int x = 0;
    
    		uint32_t *result = p_result;
    
    		uint32_t def = result ? *result : 0, low = 0, high = UINT32_MAX;
    		char *endptr = NULL;
    
    
    		/* optional argument: first default value, then range */
    
    		if (flags & PARSE_DEFAULT) {
    
    		if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) {
    			/* range requested, update bounds */
    			low = va_arg(ap, uint32_t);
    			high = va_arg(ap, uint32_t);
    		}
    
    
    		if (ast_strlen_zero(arg)) {
    			error = 1;
    			goto uint32_done;
    		}
    		/* strtoul will happilly and silently negate negative numbers */
    		arg = ast_skip_blanks(arg);
    		if (*arg == '-') {
    			error = 1;
    			goto uint32_done;
    		}
    
    		x = strtoul(arg, &endptr, 0);
    
    		if (*endptr || errno || x > UINT32_MAX) {
    
    			error = 1;
    			goto uint32_done;
    		}
    
    		error = (x < low) || (x > high);
    
    		if (flags & PARSE_RANGE_DEFAULTS) {
    			if (x < low) {
    				def = low;
    			} else if (x > high) {
    				def = high;
    			}
    		}
    
    		if (flags & PARSE_OUT_RANGE) {
    
    		}
    uint32_done:
    		if (result) {
    
    		}
    		ast_debug(3, "extract uint from [%s] in [%u, %u] gives [%lu](%d)\n",
    				arg, low, high, result ? *result : x, error);
    
    	case PARSE_TIMELEN:
    	{
    		int x = 0;
    		int *result = p_result;
    		int def = result ? *result : 0;
    		int high = INT_MAX;
    		int low = INT_MIN;
    		enum ast_timelen defunit;
    
    		defunit = va_arg(ap, enum ast_timelen);
    		/* optional arguments: default value and/or (low, high) */
    		if (flags & PARSE_DEFAULT) {
    			def = va_arg(ap, int);
    		}
    		if (flags & (PARSE_IN_RANGE | PARSE_OUT_RANGE)) {
    			low = va_arg(ap, int);
    			high = va_arg(ap, int);
    		}
    		if (ast_strlen_zero(arg)) {
    			error = 1;
    			goto timelen_done;
    		}
    		error = ast_app_parse_timelen(arg, &x, defunit);
    		if (error || x < INT_MIN || x > INT_MAX) {
    			/* Parse error, or type out of int bounds */
    			error = 1;
    			goto timelen_done;
    		}
    		error = (x < low) || (x > high);
    		if (flags & PARSE_RANGE_DEFAULTS) {
    			if (x < low) {
    				def = low;
    			} else if (x > high) {
    				def = high;
    			}
    		}
    		if (flags & PARSE_OUT_RANGE) {
    			error = !error;
    		}
    timelen_done:
    		if (result) {
    			*result  = error ? def : x;
    		}
    
    		ast_debug(3, "extract timelen from [%s] in [%d, %d] gives [%d](%d)\n",
    				arg, low, high, result ? *result : x, error);
    		break;
    	}
    
    
    		double *result = p_result;
    
    		double x = 0, def = result ? *result : 0, low = -HUGE_VAL, high = HUGE_VAL;
    		char *endptr = NULL;
    
    
    		/* optional argument: first default value, then range */
    
    		if (flags & PARSE_DEFAULT) {
    
    			def = va_arg(ap, double);
    
    		}
    		if (flags & (PARSE_IN_RANGE | PARSE_OUT_RANGE)) {
    
    			/* range requested, update bounds */
    			low = va_arg(ap, double);
    			high = va_arg(ap, double);
    		}
    
    		if (ast_strlen_zero(arg)) {
    			error = 1;
    			goto double_done;
    		}
    		errno = 0;
    		x = strtod(arg, &endptr);
    		if (*endptr || errno == ERANGE) {
    			error = 1;
    			goto double_done;
    		}
    
    		error = (x < low) || (x > high);
    
    		if (flags & PARSE_OUT_RANGE) {
    
    		}
    double_done:
    		if (result) {
    			*result = error ? def : x;
    		}
    		ast_debug(3, "extract double from [%s] in [%f, %f] gives [%f](%d)\n",
    				arg, low, high, result ? *result : x, error);
    
    Mark Michelson's avatar
    Mark Michelson committed
    	case PARSE_ADDR:
    	    {
    		struct ast_sockaddr *addr = (struct ast_sockaddr *)p_result;
    
    		if (!ast_sockaddr_parse(addr, arg, flags & PARSE_PORT_MASK)) {
    			error = 1;
    		}
    
    		ast_debug(3, "extract addr from %s gives %s(%d)\n",
    			  arg, ast_sockaddr_stringify(addr), error);
    
    		break;
    	    }
    	case PARSE_INADDR:	/* TODO Remove this (use PARSE_ADDR instead). */
    
    	    {
    		char *port, *buf;
    		struct sockaddr_in _sa_buf;	/* buffer for the result */
    		struct sockaddr_in *sa = p_result ?
    			(struct sockaddr_in *)p_result : &_sa_buf;
    		/* default is either the supplied value or the result itself */
    		struct sockaddr_in *def = (flags & PARSE_DEFAULT) ?
    			va_arg(ap, struct sockaddr_in *) : sa;
    		struct hostent *hp;
    		struct ast_hostent ahp;
    
    
    Steve Murphy's avatar
    Steve Murphy committed
    		memset(&_sa_buf, '\0', sizeof(_sa_buf)); /* clear buffer */
    
    		/* duplicate the string to strip away the :port */
    		port = ast_strdupa(arg);
    		buf = strsep(&port, ":");
    		sa->sin_family = AF_INET;	/* assign family */
    		/*
    		 * honor the ports flag setting, assign default value
    		 * in case of errors or field unset.
    		 */
    		flags &= PARSE_PORT_MASK; /* the only flags left to process */
    		if (port) {
    			if (flags == PARSE_PORT_FORBID) {
    				error = 1;	/* port was forbidden */
    				sa->sin_port = def->sin_port;
    			} else if (flags == PARSE_PORT_IGNORE)
    				sa->sin_port = def->sin_port;
    			else /* accept or require */
    				sa->sin_port = htons(strtol(port, NULL, 0));
    		} else {
    			sa->sin_port = def->sin_port;
    			if (flags == PARSE_PORT_REQUIRE)
    				error = 1;
    		}
    		/* Now deal with host part, even if we have errors before. */
    		hp = ast_gethostbyname(buf, &ahp);
    		if (hp)	/* resolved successfully */
    			memcpy(&sa->sin_addr, hp->h_addr, sizeof(sa->sin_addr));
    		else {
    			error = 1;
    			sa->sin_addr = def->sin_addr;
    		}
    		ast_debug(3,
    			"extract inaddr from [%s] gives [%s:%d](%d)\n",
    			arg, ast_inet_ntoa(sa->sin_addr),
    			ntohs(sa->sin_port), error);
    
    Jason Parker's avatar
    Jason Parker committed
    static char *handle_cli_core_show_config_mappings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    
    	struct ast_config_engine *eng;
    
    Jason Parker's avatar
    Jason Parker committed
    
    	switch (cmd) {
    	case CLI_INIT:
    		e->command = "core show config mappings";
    		e->usage =
    			"Usage: core show config mappings\n"
    			"	Shows the filenames to config engines.\n";
    		return NULL;
    	case CLI_GENERATE:
    		return NULL;
    	}
    
    	{
    		SCOPED_MUTEX(lock, &config_lock);
    
    		if (!config_engine_list) {
    			ast_cli(a->fd, "No config mappings found.\n");
    		} else {
    			for (eng = config_engine_list; eng; eng = eng->next) {
    				ast_cli(a->fd, "Config Engine: %s\n", eng->name);
    				for (map = config_maps; map; map = map->next) {
    					if (!strcasecmp(map->driver, eng->name)) {
    						ast_cli(a->fd, "===> %s (db=%s, table=%s)\n", map->name, map->database,
    								map->table ? map->table : map->name);
    					}
    
    Jason Parker's avatar
    Jason Parker committed
    	return CLI_SUCCESS;
    
    static char *handle_cli_config_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    {
    	struct cache_file_mtime *cfmtime;
    
    	char *prev = "", *completion_value = NULL;
    
    	int wordlen, which = 0;
    
    	switch (cmd) {
    	case CLI_INIT:
    		e->command = "config reload";
    		e->usage =
    			"Usage: config reload <filename.conf>\n"
    			"   Reloads all modules that reference <filename.conf>\n";
    		return NULL;
    	case CLI_GENERATE:
    		if (a->pos > 2) {
    			return NULL;
    		}
    
    		wordlen = strlen(a->word);
    
    		AST_LIST_LOCK(&cfmtime_head);
    		AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
    
    			/* Skip duplicates - this only works because the list is sorted by filename */
    			if (strcmp(cfmtime->filename, prev) == 0) {
    				continue;
    
    			/* Core configs cannot be reloaded */
    			if (ast_strlen_zero(cfmtime->who_asked)) {
    
    				continue;
    			}
    
    			if (++which > a->n && strncmp(cfmtime->filename, a->word, wordlen) == 0) {
    				completion_value = ast_strdup(cfmtime->filename);
    				break;
    			}
    
    			/* Otherwise save that we've seen this filename */
    
    		}
    		AST_LIST_UNLOCK(&cfmtime_head);
    
    		return completion_value;
    	}
    
    	if (a->argc != 3) {
    		return CLI_SHOWUSAGE;
    	}
    
    	AST_LIST_LOCK(&cfmtime_head);
    	AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
    		if (!strcmp(cfmtime->filename, a->argv[2])) {
    
    			char *buf = ast_alloca(strlen("module reload ") + strlen(cfmtime->who_asked) + 1);
    
    			sprintf(buf, "module reload %s", cfmtime->who_asked);
    			ast_cli_command(a->fd, buf);
    		}
    	}
    	AST_LIST_UNLOCK(&cfmtime_head);
    
    	return CLI_SUCCESS;
    }
    
    static char *handle_cli_config_list(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)