Newer
Older
int ast_realtime_is_mapping_defined(const char *family)
{
struct ast_config_map *map;
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);
return 0;
}
/*! \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)
Mark Spencer
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 */
for (eng = config_engine_list; !ret && eng; eng = eng->next) {
if (!strcasecmp(eng->name, map->driver))
/* 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);
static struct ast_config_engine text_file_engine = {
.name = "text",
.load_func = config_text_file_load,
};
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
3092
3093
3094
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;
}
Tilghman Lesher
committed
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)
Olle Johansson
committed
char db[256];
char table[256];
struct ast_config_engine *loader = &text_file_engine;
/* 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;
Tilghman Lesher
committed
if (!ast_test_flag(&flags, CONFIG_FLAG_NOREALTIME) && config_engine_list) {
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;
}
}
Tilghman Lesher
committed
result = loader->load_func(db, table, filename, cfg, flags, suggested_include_file, who_asked);
if (result && result != CONFIG_STATUS_FILEINVALID && result != CONFIG_STATUS_FILEUNCHANGED) {
config_hook_exec(filename, who_asked, result);
} else if (result != CONFIG_STATUS_FILEINVALID) {
Tilghman Lesher
committed
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;
Tilghman Lesher
committed
result = ast_config_internal_load(filename, cfg, flags, "", who_asked);
Tilghman Lesher
committed
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;
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
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 *);
if (!newparam) {
*result = NULL;
return 0;
}
newval = va_arg(ap, const char *);
if (!(first = ast_variable_new(newparam, newval, ""))) {
*result = NULL;
return -1;
}
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);
*result = NULL;
return -1;
}
field->next = fields;
fields = field;
}
first->next = fields;
fields = first;
*result = fields;
return 0;
}
struct ast_variable *ast_load_realtime_all_fields(const char *family, const struct ast_variable *fields)
char db[256];
char table[256];
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);
}
}
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) {
if (ast_strlen_zero(cur->value)) {
/* Eliminate empty entries */
struct ast_variable *next;
next = cur->next;
*prev = next;
ast_variable_destroy(cur);
cur = next;
} else {
/* 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;
}
}
return res;
struct ast_variable *ast_load_realtime(const char *family, ...)
{
RAII_VAR(struct ast_variable *, fields, NULL, ast_variables_destroy);
int field_res = 0;
va_list ap;
va_start(ap, family);
if (realtime_arguments_to_fields(ap, &fields)) {
field_res = -1;
}
if (field_res) {
return NULL;
}
if (!fields) {
return NULL;
}
return ast_load_realtime_fields(family, fields);
}
/*! \brief Check if realtime engine is configured for family */
int ast_check_realtime(const char *family)
{
struct ast_config_engine *eng;
Olle Johansson
committed
if (!ast_realtime_enabled()) {
return 0; /* There are no engines at all so fail early */
}
eng = find_engine(family, 1, NULL, 0, NULL, 0);
if (eng)
return 1;
return 0;
}
/*! \brief Check if there's any realtime engines loaded */
int ast_realtime_enabled(void)
{
return config_maps ? 1 : 0;
}
Tilghman Lesher
committed
int ast_realtime_require_field(const char *family, ...)
{
struct ast_config_engine *eng;
char db[256];
char table[256];
Tilghman Lesher
committed
va_list ap;
Tilghman Lesher
committed
va_start(ap, family);
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;
}
Tilghman Lesher
committed
}
va_end(ap);
return res;
}
int ast_unload_realtime(const char *family)
{
struct ast_config_engine *eng;
char db[256];
char table[256];
Tilghman Lesher
committed
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;
}
Tilghman Lesher
committed
}
return res;
}
struct ast_config *ast_load_realtime_multientry_fields(const char *family, const struct ast_variable *fields)
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;
}
break;
}
} else {
break;
}
}
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);
if (!fields) {
return NULL;
}
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)
char db[256];
char table[256];
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)) {
break;
}
} else {
break;
}
}
Steve Murphy
committed
return res;
}
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);
if (!fields) {
return -1;
}
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)
{
struct ast_config_engine *eng;
char db[256];
char table[256];
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))) {
break;
}
} else {
break;
}
}
return res;
}
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);
va_start(ap, family);
Matthew Jordan
committed
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)
Tilghman Lesher
committed
{
Steve Murphy
committed
struct ast_config_engine *eng;
char db[256];
char table[256];
Steve Murphy
committed
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)) {
break;
}
} else {
break;
}
}
Steve Murphy
committed
return res;
}
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);
if (!fields) {
return -1;
}
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)
Tilghman Lesher
committed
{
Steve Murphy
committed
struct ast_config_engine *eng;
char db[256];
char table[256];
Steve Murphy
committed
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))) {
break;
}
} else {
break;
}
}
Steve Murphy
committed
int ast_destroy_realtime(const char *family, const char *keyfield, const char *lookup, ...)
{
RAII_VAR(struct ast_variable *, fields, NULL, ast_variables_destroy);
int res = 0;
va_list ap;
va_start(ap, lookup);
if (realtime_arguments_to_fields(ap, &fields)) {
res = -1;
}
if (res) {
return -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);
3614
3615
3616
3617
3618
3619
3620
3621
3622
3623
3624
3625
3626
3627
3628
3629
3630
3631
3632
3633
3634
3635
3636
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,
{
va_list ap;
int error = 0;
va_start(ap, p_result);
switch (flags & PARSE_TYPE) {
case PARSE_INT32:
int32_t *result = p_result;
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) {
def = va_arg(ap, int32_t);
}
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;
}
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;
}
}
error = !error;
*result = error ? def : x;
}
ast_debug(3, "extract int from [%s] in [%d, %d] gives [%ld](%d)\n",
arg, low, high, result ? *result : x, error);
case PARSE_UINT32:
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 */
def = va_arg(ap, uint32_t);
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;
}
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;
}
}
error = !error;
*result = error ? def : x;
}
ast_debug(3, "extract uint from [%s] in [%u, %u] gives [%lu](%d)\n",
arg, low, high, result ? *result : x, error);
3748
3749
3750
3751
3752
3753
3754
3755
3756
3757
3758
3759
3760
3761
3762
3763
3764
3765
3766
3767
3768
3769
3770
3771
3772
3773
3774
3775
3776
3777
3778
3779
3780
3781
3782
3783
3784
3785
3786
3787
3788
3789
3790
3791
3792
3793
3794
3795
3796
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;
}
case PARSE_DOUBLE:
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 */
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);
}
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);
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;
memset(&_sa_buf, '\0', sizeof(_sa_buf)); /* clear buffer */
3860
3861
3862
3863
3864
3865
3866
3867
3868
3869
3870
3871
3872
3873
3874
3875
3876
3877
3878
3879
3880
3881
3882
3883
3884
3885
3886
3887
3888
3889
3890
3891
3892
3893
/* 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);
}
}
va_end(ap);
return error;
static char *handle_cli_core_show_config_mappings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
struct ast_config_map *map;
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
committed
}
Jason Parker
committed
}
Tilghman Lesher
committed
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;
Tilghman Lesher
committed
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;
Tilghman Lesher
committed
}
/* Core configs cannot be reloaded */
if (ast_strlen_zero(cfmtime->who_asked)) {
Tilghman Lesher
committed
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 */
prev = cfmtime->filename;
Tilghman Lesher
committed
}
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);
Tilghman Lesher
committed
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)