config.c: fix #tryinclude being converted to #include on rewrite

Correct an issue in ast_config_text_file_save2() when updating configuration
files with "#tryinclude" statements. The API currently replaces "#tryinclude"
with "#include". The API also creates empty template files if the referenced
files do not exist. This change resolves these problems.

Resolves: https://github.com/asterisk/asterisk/issues/920
(cherry picked from commit 5945703267)
releases/21
Allan Nathanson 8 months ago committed by Asterisk Development Team
parent c3a5f1d3f3
commit ad49fffd54

@ -914,7 +914,7 @@ void ast_category_rename(struct ast_category *cat, const char *name);
struct ast_variable *_ast_variable_new(const char *name, const char *value, const char *filename, const char *file, const char *function, int lineno); struct ast_variable *_ast_variable_new(const char *name, const char *value, const char *filename, const char *file, const char *function, int lineno);
#define ast_variable_new(name, value, filename) _ast_variable_new(name, value, filename, __FILE__, __PRETTY_FUNCTION__, __LINE__) #define ast_variable_new(name, value, filename) _ast_variable_new(name, value, filename, __FILE__, __PRETTY_FUNCTION__, __LINE__)
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); struct ast_config_include *ast_include_new(struct ast_config *conf, const char *from_file, const char *included_file, int include_type, const char *exec_file, int from_lineno, char *real_included_file_name, int real_included_file_name_size);
struct ast_config_include *ast_include_find(struct ast_config *conf, const char *included_file); struct ast_config_include *ast_include_find(struct ast_config *conf, const char *included_file);
void ast_include_rename(struct ast_config *conf, const char *from_file, const char *to_file); void ast_include_rename(struct ast_config *conf, const char *from_file, const char *to_file);
void ast_variable_append(struct ast_category *category, struct ast_variable *variable); void ast_variable_append(struct ast_category *category, struct ast_variable *variable);

@ -256,7 +256,28 @@ struct ast_config {
struct ast_category *last_browse; /*!< used to cache the last category supplied via category_browse */ struct ast_category *last_browse; /*!< used to cache the last category supplied via category_browse */
int include_level; int include_level;
int max_include_level; int max_include_level;
struct ast_config_include *includes; /*!< a list of inclusions, which should describe the entire tree */ /*! First inclusion in the list. */
struct ast_config_include *includes_root;
/*! Last inclusion in the list. */
struct ast_config_include *includes_last;
};
struct config_included_file {
AST_LIST_ENTRY(config_included_file) next;
/*! #include path or matching #tryinclude paths */
char path[];
};
/*!
* \brief Types used for ast_include_new() include_type
*/
enum include_statement_type {
/*! #include statements */
CONFIG_STATEMENT_INCLUDE = 0,
/*! #exec statements */
CONFIG_STATEMENT_EXEC = 1,
/*! #tryinclude statements */
CONFIG_STATEMENT_TRYINCLUDE = 2,
}; };
struct ast_config_include { struct ast_config_include {
@ -266,7 +287,7 @@ struct ast_config_include {
*/ */
char *include_location_file; char *include_location_file;
int include_location_lineno; /*!< lineno where include occurred */ int include_location_lineno; /*!< lineno where include occurred */
int exec; /*!< set to non-zero if its a #exec statement */ enum include_statement_type include_type; /*!< #include, #exec, #tryinclude */
/*! /*!
* \brief if it's an exec, you'll have both the /var/tmp to read, and the original script * \brief if it's an exec, you'll have both the /var/tmp to read, and the original script
* \note Will never be NULL if exec is non-zero * \note Will never be NULL if exec is non-zero
@ -277,9 +298,13 @@ struct ast_config_include {
* \note Will never be NULL * \note Will never be NULL
*/ */
char *included_file; char *included_file;
AST_LIST_HEAD_NOLOCK(included_files_list, config_included_file) included_files; /*!< list of #include/#tryinclude files */
int inclusion_count; /*!< if the file is included more than once, a running count thereof -- but, worry not, 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 */ 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 */ int output; /*!< a flag to indicate if the inclusion has been output */
struct ast_comment *precomments;
struct ast_comment *sameline;
struct ast_comment *trailing; /*!< the last object in the list will get assigned any trailing comments when EOF is hit */
struct ast_config_include *next; /*!< ptr to next inclusion in the list */ struct ast_config_include *next; /*!< ptr to next inclusion in the list */
}; };
@ -333,14 +358,18 @@ static void ast_variable_move(struct ast_variable *dst_var, struct ast_variable
src_var->trailing = NULL; src_var->trailing = NULL;
} }
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 void make_fn(char *fn, size_t fn_size, const char *file, const char *configfile);
struct ast_config_include *ast_include_new(struct ast_config *conf, const char *from_file, const char *included_file, int include_type, const char *exec_file, int from_lineno, char *real_included_file_name, int real_included_file_name_size)
{ {
/* a file should be included ONCE. Otherwise, if one of the instances is changed, /* 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 * then all be changed. -- how do we know to include it? -- Handling modified
* instances is possible, I'd have * instances is possible, I'd have
* to create a new master for each instance. */ * to create a new master for each instance. */
struct ast_config_include *inc; struct ast_config_include *inc;
struct config_included_file *inc_file;
struct stat statbuf; struct stat statbuf;
char fn[PATH_MAX];
inc = ast_include_find(conf, included_file); inc = ast_include_find(conf, included_file);
if (inc) { if (inc) {
@ -348,7 +377,7 @@ struct ast_config_include *ast_include_new(struct ast_config *conf, const char *
inc->inclusion_count++; inc->inclusion_count++;
snprintf(real_included_file_name, real_included_file_name_size, "%s~~%d", included_file, inc->inclusion_count); snprintf(real_included_file_name, real_included_file_name_size, "%s~~%d", included_file, inc->inclusion_count);
} while (stat(real_included_file_name, &statbuf) == 0); } while (stat(real_included_file_name, &statbuf) == 0);
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); 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 } else
*real_included_file_name = 0; *real_included_file_name = 0;
@ -357,26 +386,82 @@ struct ast_config_include *ast_include_new(struct ast_config *conf, const char *
return NULL; return NULL;
} }
inc->include_location_file = ast_strdup(from_file); inc->include_location_file = ast_strdup(from_file);
if (!inc->include_location_file) {
ast_includes_destroy(inc);
return NULL;
}
inc->include_location_lineno = from_lineno; inc->include_location_lineno = from_lineno;
if (!ast_strlen_zero(real_included_file_name)) if (!ast_strlen_zero(real_included_file_name)) {
inc->included_file = ast_strdup(real_included_file_name); inc->included_file = ast_strdup(real_included_file_name);
else } else {
inc->included_file = ast_strdup(included_file); inc->included_file = ast_strdup(included_file);
}
inc->exec = is_exec; if (!inc->included_file) {
if (is_exec)
inc->exec_file = ast_strdup(exec_file);
if (!inc->include_location_file
|| !inc->included_file
|| (is_exec && !inc->exec_file)) {
ast_includes_destroy(inc); ast_includes_destroy(inc);
return NULL; return NULL;
} }
inc->include_type = include_type;
make_fn(fn, sizeof(fn), inc->included_file, from_file);
switch (inc->include_type) {
case CONFIG_STATEMENT_INCLUDE:
inc_file = ast_calloc(1, sizeof(*inc_file) + strlen(fn) + 1);
if (!inc_file) {
ast_includes_destroy(inc);
return NULL;
}
strcpy(inc_file->path, fn); /* safe */
AST_LIST_INSERT_TAIL(&inc->included_files, inc_file, next);
break;
case CONFIG_STATEMENT_TRYINCLUDE:
{
int glob_ret;
glob_t globbuf;
/* add files that match the pattern */
globbuf.gl_offs = 0;
glob_ret = glob(fn, MY_GLOB_FLAGS, NULL, &globbuf);
if (glob_ret == 0) {
int i;
for (i = 0; i < globbuf.gl_pathc; i++) {
char *matched_path = globbuf.gl_pathv[i];
if (access(matched_path, F_OK) != 0) {
/* skip #tryinclude paths that do not already exist. */
continue;
}
inc_file = ast_calloc(1, sizeof(*inc_file) + strlen(matched_path) + 1);
if (!inc_file) {
ast_includes_destroy(inc);
globfree(&globbuf);
return NULL;
}
strcpy(inc_file->path, matched_path); /* safe */
AST_LIST_INSERT_TAIL(&inc->included_files, inc_file, next);
}
}
globfree(&globbuf);
break;
}
case CONFIG_STATEMENT_EXEC:
inc->exec_file = ast_strdup(exec_file);
if (!inc->exec_file) {
ast_includes_destroy(inc);
return NULL;
}
break;
}
/* attach this new struct to the conf struct */ /* attach this new struct to the conf struct */
inc->next = conf->includes; if (conf->includes_root && conf->includes_last) {
conf->includes = inc; conf->includes_last->next = inc;
} else {
conf->includes_root = inc;
}
conf->includes_last = inc;
return inc; return inc;
} }
@ -402,8 +487,8 @@ void ast_include_rename(struct ast_config *conf, const char *from_file, const ch
/* file names are on categories, includes (of course), and on variables. So, /* file names are on categories, includes (of course), and on variables. So,
* traverse all this and swap names */ * traverse all this and swap names */
for (incl = conf->includes; incl; incl=incl->next) { for (incl = conf->includes_root; incl; incl = incl->next) {
if (strcmp(incl->include_location_file,from_file) == 0) { if (strcmp(incl->include_location_file, from_file) == 0) {
if (from_len >= to_len) if (from_len >= to_len)
strcpy(incl->include_location_file, to_file); strcpy(incl->include_location_file, to_file);
else { else {
@ -421,7 +506,7 @@ void ast_include_rename(struct ast_config *conf, const char *from_file, const ch
struct ast_variable *v; struct ast_variable *v;
struct ast_variable *new_var; struct ast_variable *new_var;
if (strcmp(cat->file,from_file) == 0) { if (strcmp(cat->file, from_file) == 0) {
if (from_len >= to_len) if (from_len >= to_len)
strcpy(cat->file, to_file); strcpy(cat->file, to_file);
else { else {
@ -476,8 +561,8 @@ void ast_include_rename(struct ast_config *conf, const char *from_file, const ch
struct ast_config_include *ast_include_find(struct ast_config *conf, const char *included_file) struct ast_config_include *ast_include_find(struct ast_config *conf, const char *included_file)
{ {
struct ast_config_include *x; struct ast_config_include *x;
for (x=conf->includes;x;x=x->next) { for (x = conf->includes_root; x; x = x->next) {
if (strcmp(x->included_file,included_file) == 0) if (strcmp(x->included_file, included_file) == 0)
return x; return x;
} }
return 0; return 0;
@ -1222,16 +1307,34 @@ void ast_category_destroy(struct ast_category *cat)
ast_free(cat); ast_free(cat);
} }
static void config_included_files_destroy(struct ast_config_include *incl)
{
struct config_included_file *x;
while ((x = AST_LIST_REMOVE_HEAD(&incl->included_files, next))) {
ast_free(x);
}
}
static void ast_include_destroy(struct ast_config_include *incl)
{
ast_free(incl->include_location_file);
ast_free(incl->exec_file);
ast_free(incl->included_file);
config_included_files_destroy(incl);
ast_comment_destroy(&incl->precomments);
ast_comment_destroy(&incl->sameline);
ast_comment_destroy(&incl->trailing);
ast_free(incl);
}
static void ast_includes_destroy(struct ast_config_include *incls) static void ast_includes_destroy(struct ast_config_include *incls)
{ {
struct ast_config_include *incl,*inclnext; struct ast_config_include *incl, *inclnext;
for (incl=incls; incl; incl = inclnext) { for (incl = incls; incl; incl = inclnext) {
inclnext = incl->next; inclnext = incl->next;
ast_free(incl->include_location_file); ast_include_destroy(incl);
ast_free(incl->exec_file);
ast_free(incl->included_file);
ast_free(incl);
} }
} }
@ -1338,7 +1441,7 @@ void ast_config_sort_categories(struct ast_config *config, int descending,
} else if (qsize == 0 || !q) { } else if (qsize == 0 || !q) {
/* q is empty; e must come from p. */ /* q is empty; e must come from p. */
e = p; p = p->next; psize--; e = p; p = p->next; psize--;
} else if ((comparator(p,q) * descending) <= 0) { } else if ((comparator(p, q) * descending) <= 0) {
/* First element of p is lower (or same) e must come from p. */ /* First element of p is lower (or same) e must come from p. */
e = p; e = p;
p = p->next; p = p->next;
@ -1617,7 +1720,7 @@ void ast_config_destroy(struct ast_config *cfg)
if (!cfg) if (!cfg)
return; return;
ast_includes_destroy(cfg->includes); ast_includes_destroy(cfg->includes_root);
cat = cfg->root; cat = cfg->root;
while (cat) { while (cat) {
@ -1909,7 +2012,7 @@ static int handle_include_exec(const char *command, const char *output_file)
/*! \brief parse one line in the configuration. /*! \brief parse one line in the configuration.
* \verbatim * \verbatim
* We can have a category header [foo](...) * We can have a category header [foo](...)
* a directive #include / #exec * a directive #include, #tryinclude, #exec
* or a regular line name = value * or a regular line name = value
* \endverbatim * \endverbatim
*/ */
@ -1918,7 +2021,10 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
struct ast_str *comment_buffer, struct ast_str *comment_buffer,
struct ast_str *lline_buffer, struct ast_str *lline_buffer,
const char *suggested_include_file, const char *suggested_include_file,
struct ast_category **last_cat, struct ast_variable **last_var, const char *who_asked) struct ast_category **last_cat,
struct ast_variable **last_var,
struct ast_config_include **last_inc,
const char *who_asked)
{ {
char *c; char *c;
char *cur = buf; char *cur = buf;
@ -1957,12 +2063,11 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
(*cat)->lineno = lineno; (*cat)->lineno = lineno;
/* add comments */ /* add comments */
if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
newcat->precomments = ALLOC_COMMENT(comment_buffer); newcat->precomments = ALLOC_COMMENT(comment_buffer);
if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
newcat->sameline = ALLOC_COMMENT(lline_buffer); newcat->sameline = ALLOC_COMMENT(lline_buffer);
if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
CB_RESET(comment_buffer, lline_buffer); CB_RESET(comment_buffer, lline_buffer);
}
/* If there are options or categories to inherit from, process them now */ /* If there are options or categories to inherit from, process them now */
if (c) { if (c) {
@ -2025,16 +2130,18 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
* may be in another file or it already has trailing comments * may be in another file or it already has trailing comments
* that we would then leak. * that we would then leak.
*/ */
*last_var = NULL;
*last_cat = newcat; *last_cat = newcat;
*last_var = NULL;
*last_inc = NULL;
if (newcat) { if (newcat) {
ast_category_append(cfg, newcat); ast_category_append(cfg, newcat);
} }
} else if (cur[0] == '#') { /* A directive - #include or #exec */ } else if (cur[0] == '#') { /* A directive - #include, #tryinclude, or #exec */
char *cur2; char *cur2;
char real_inclusion_name[256]; char real_inclusion_name[256];
int do_include = 0; /* otherwise, it is exec */ int include_type;
int try_include = 0; struct ast_config_include *newinclude;
struct ast_config *result;
cur++; cur++;
c = cur; c = cur;
@ -2053,15 +2160,15 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
c = NULL; c = NULL;
} }
if (!strcasecmp(cur, "include")) { if (!strcasecmp(cur, "include")) {
do_include = 1; include_type = CONFIG_STATEMENT_INCLUDE;
} else if (!strcasecmp(cur, "tryinclude")) { } else if (!strcasecmp(cur, "tryinclude")) {
do_include = 1; include_type = CONFIG_STATEMENT_TRYINCLUDE;
try_include = 1;
} else if (!strcasecmp(cur, "exec")) { } else if (!strcasecmp(cur, "exec")) {
if (!ast_opt_exec_includes) { if (!ast_opt_exec_includes) {
ast_log(LOG_WARNING, "Cannot perform #exec unless execincludes option is enabled in asterisk.conf (options section)!\n"); ast_log(LOG_WARNING, "Cannot perform #exec unless execincludes option is enabled in asterisk.conf (options section)!\n");
return 0; /* XXX is this correct ? or we should return -1 ? */ return 0; /* XXX is this correct ? or we should return -1 ? */
} }
include_type = CONFIG_STATEMENT_EXEC;
} else { } else {
ast_log(LOG_WARNING, "Unknown directive '#%s' at line %d of %s\n", cur, lineno, configfile); ast_log(LOG_WARNING, "Unknown directive '#%s' at line %d of %s\n", cur, lineno, configfile);
return 0; /* XXX is this correct ? or we should return -1 ? */ return 0; /* XXX is this correct ? or we should return -1 ? */
@ -2069,8 +2176,8 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
if (c == NULL) { if (c == NULL) {
ast_log(LOG_WARNING, "Directive '#%s' needs an argument (%s) at line %d of %s\n", ast_log(LOG_WARNING, "Directive '#%s' needs an argument (%s) at line %d of %s\n",
do_include ? "include / tryinclude" : "exec", cur,
do_include ? "filename" : "/path/to/executable", (include_type != CONFIG_STATEMENT_EXEC) ? "filename" : "/path/to/executable",
lineno, lineno,
configfile); configfile);
return 0; /* XXX is this correct ? or we should return -1 ? */ return 0; /* XXX is this correct ? or we should return -1 ? */
@ -2092,9 +2199,9 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
} }
cur2 = cur; cur2 = cur;
/* #exec </path/to/executable> if (include_type == CONFIG_STATEMENT_EXEC) {
We create a tmp file, then we #include it, then we delete it. */ /* #exec </path/to/executable>
if (!do_include) { We create a tmp file, then we #include it, then we delete it. */
struct timeval now = ast_tvnow(); struct timeval now = ast_tvnow();
if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE)) if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
@ -2111,17 +2218,35 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
} }
/* A #include */ /* A #include */
/* record this inclusion */ /* record this inclusion */
ast_include_new(cfg, cfg->include_level == 1 ? "" : configfile, cur, !do_include, cur2, lineno, real_inclusion_name, sizeof(real_inclusion_name)); newinclude = ast_include_new(cfg, cfg->include_level == 1 ? "" : configfile, cur, include_type, cur2, lineno, real_inclusion_name, sizeof(real_inclusion_name));
if (!newinclude) {
if (!ast_strlen_zero(exec_file)) {
unlink(exec_file);
}
return -1;
}
do_include = ast_config_internal_load(cur, cfg, flags, real_inclusion_name, who_asked) ? 1 : 0; result = ast_config_internal_load(cur, cfg, flags, real_inclusion_name, who_asked);
if (!ast_strlen_zero(exec_file)) if (!ast_strlen_zero(exec_file)) {
unlink(exec_file); unlink(exec_file);
if (!do_include && !try_include) { }
if (!result && (include_type != CONFIG_STATEMENT_TRYINCLUDE)) {
ast_log(LOG_ERROR, "The file '%s' was listed as a #include but it does not exist.\n", cur); ast_log(LOG_ERROR, "The file '%s' was listed as a #include but it does not exist.\n", cur);
ast_include_destroy(newinclude);
return -1; return -1;
} }
/* XXX otherwise what ? the default return is 0 anyways */
/* add comments */
if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
newinclude->precomments = ALLOC_COMMENT(comment_buffer);
newinclude->sameline = ALLOC_COMMENT(lline_buffer);
CB_RESET(comment_buffer, lline_buffer);
}
/* for now, any end-of-file (trailing) comments are after this #include */
*last_cat = NULL;
*last_var = NULL;
*last_inc = newinclude;
} else { } else {
/* Just a line (variable = value) */ /* Just a line (variable = value) */
int object = 0; int object = 0;
@ -2189,17 +2314,16 @@ set_new_variable:
v->object = object; v->object = object;
*last_cat = NULL; *last_cat = NULL;
*last_var = v; *last_var = v;
*last_inc = NULL;
/* Put and reset comments */ /* Put and reset comments */
v->blanklines = 0; v->blanklines = 0;
ast_variable_append(*cat, v); ast_variable_append(*cat, v);
/* add comments */ /* add comments */
if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
v->precomments = ALLOC_COMMENT(comment_buffer); v->precomments = ALLOC_COMMENT(comment_buffer);
if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
v->sameline = ALLOC_COMMENT(lline_buffer); v->sameline = ALLOC_COMMENT(lline_buffer);
if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
CB_RESET(comment_buffer, lline_buffer); CB_RESET(comment_buffer, lline_buffer);
}
} else { } else {
return -1; return -1;
} }
@ -2227,8 +2351,9 @@ static struct ast_config *config_text_file_load(const char *database, const char
struct stat statbuf; struct stat statbuf;
struct cache_file_mtime *cfmtime = NULL; struct cache_file_mtime *cfmtime = NULL;
struct cache_file_include *cfinclude; struct cache_file_include *cfinclude;
struct ast_variable *last_var = NULL;
struct ast_category *last_cat = NULL; struct ast_category *last_cat = NULL;
struct ast_variable *last_var = NULL;
struct ast_config_include *last_inc = NULL;
/*! Growable string buffer */ /*! Growable string buffer */
struct ast_str *comment_buffer = NULL; /*!< this will be a comment collector.*/ struct ast_str *comment_buffer = NULL; /*!< this will be a comment collector.*/
struct ast_str *lline_buffer = NULL; /*!< A buffer for stuff behind the ; */ struct ast_str *lline_buffer = NULL; /*!< A buffer for stuff behind the ; */
@ -2282,7 +2407,7 @@ static struct ast_config *config_text_file_load(const char *database, const char
ast_free(lline_buffer); ast_free(lline_buffer);
return NULL; return NULL;
} }
for (i=0; i<globbuf.gl_pathc; i++) { for (i = 0; i < globbuf.gl_pathc; i++) {
ast_copy_string(fn, globbuf.gl_pathv[i], sizeof(fn)); ast_copy_string(fn, globbuf.gl_pathv[i], sizeof(fn));
/* /*
@ -2417,11 +2542,9 @@ static struct ast_config *config_text_file_load(const char *database, const char
} }
if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)
&& comment_buffer && (ast_strlen_zero(buf) || strlen(buf) == strspn(buf, " \t\n\r"))) {
&& ast_str_strlen(comment_buffer) /* keep blank lines as comments */
&& (ast_strlen_zero(buf) || strlen(buf) == strspn(buf," \t\n\r"))) { CB_ADD(&comment_buffer, "\n"); /* add newline to the comment buffer */
/* blank line? really? Can we add it to an existing comment and maybe preserve inter- and post- comment spacing? */
CB_ADD(&comment_buffer, "\n"); /* add a newline to the comment buffer */
continue; /* go get a new line, then */ continue; /* go get a new line, then */
} }
@ -2501,7 +2624,7 @@ static struct ast_config *config_text_file_load(const char *database, const char
if (!ast_strlen_zero(buffer)) { if (!ast_strlen_zero(buffer)) {
if (process_text_line(cfg, &cat, buffer, lineno, fn, if (process_text_line(cfg, &cat, buffer, lineno, fn,
flags, comment_buffer, lline_buffer, flags, comment_buffer, lline_buffer,
suggested_include_file, &last_cat, &last_var, suggested_include_file, &last_cat, &last_var, &last_inc,
who_asked)) { who_asked)) {
cfg = CONFIG_STATUS_FILEINVALID; cfg = CONFIG_STATUS_FILEINVALID;
break; break;
@ -2527,6 +2650,14 @@ static struct ast_config *config_text_file_load(const char *database, const char
} }
last_var->trailing = ALLOC_COMMENT(comment_buffer); last_var->trailing = ALLOC_COMMENT(comment_buffer);
} }
} else if (last_inc) {
if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && ast_str_strlen(comment_buffer)) {
if (lline_buffer && ast_str_strlen(lline_buffer)) {
CB_ADD(&comment_buffer, ast_str_buffer(lline_buffer)); /* add the current lline buffer to the comment buffer */
ast_str_reset(lline_buffer); /* erase the lline buffer */
}
last_inc->trailing = ALLOC_COMMENT(comment_buffer);
}
} else { } else {
if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && ast_str_strlen(comment_buffer)) { if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && ast_str_strlen(comment_buffer)) {
ast_debug(1, "Nothing to attach comments to, discarded: %s\n", ast_str_buffer(comment_buffer)); ast_debug(1, "Nothing to attach comments to, discarded: %s\n", ast_str_buffer(comment_buffer));
@ -2539,7 +2670,7 @@ static struct ast_config *config_text_file_load(const char *database, const char
fclose(f); fclose(f);
} while (0); } while (0);
if (comment) { if (comment) {
ast_log(LOG_WARNING,"Unterminated comment detected beginning on line %d\n", nest[comment - 1]); ast_log(LOG_WARNING, "Unterminated comment detected beginning on line %d\n", nest[comment - 1]);
} }
if (cfg == NULL || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) { if (cfg == NULL || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) {
break; break;
@ -2735,6 +2866,41 @@ static int is_writable(const char *fn)
return 1; return 1;
} }
static void print_comment(FILE *f, struct ast_comment *comment)
{
struct ast_comment *cmt;
for (cmt = comment; cmt; cmt = cmt->next) {
const char *line = cmt->cmt;
char *nl;
while ((nl = strchr(line, '\n')) != NULL) {
*nl = '\0';
if (line[0] != ';' || line[1] != '!') {
/* if not one of our generated header comments */
fprintf(f, "%s\n", line);
}
line = nl + 1;
}
}
}
static void print_include(FILE *f, struct ast_config_include *incl)
{
switch (incl->include_type) {
case CONFIG_STATEMENT_INCLUDE:
fprintf(f, "#include \"%s\"\n", incl->included_file);
break;
case CONFIG_STATEMENT_TRYINCLUDE:
fprintf(f, "#tryinclude \"%s\"\n", incl->included_file);
break;
case CONFIG_STATEMENT_EXEC:
fprintf(f, "#exec \"%s\"\n", incl->exec_file);
break;
}
}
int ast_config_text_file_save2(const char *configfile, const struct ast_config *cfg, const char *generator, uint32_t flags) int ast_config_text_file_save2(const char *configfile, const struct ast_config *cfg, const char *generator, uint32_t flags)
{ {
FILE *f; FILE *f;
@ -2748,15 +2914,30 @@ int ast_config_text_file_save2(const char *configfile, const struct ast_config *
struct inclfile *fi; struct inclfile *fi;
/* Check all the files for write access before attempting to modify any of them */ /* Check all the files for write access before attempting to modify any of them */
for (incl = cfg->includes; incl; incl = incl->next) { for (incl = cfg->includes_root; incl; incl = incl->next) {
/* reset all the output flags in case this isn't our first time saving this data */ /* reset all the output flags in case this isn't our first time saving this data */
incl->output = 0; incl->output = 0;
if (!incl->exec) { if (incl->include_type != CONFIG_STATEMENT_EXEC) {
/* now make sure we have write access to the include file or its parent directory */ struct config_included_file *included_file;
make_fn(fn, sizeof(fn), incl->included_file, configfile); int ok = 1;
/* If the file itself doesn't exist, make sure we have write access to the directory */
if (!is_writable(fn)) { /* Not a #exec. */
/* For #include, make sure we have write access to the include file
file or its parent directory. If the file itself doesn't exist,
make sure we have write access to the directory. */
/* For #tryinclude, we check the file(s) that match the pattern. */
AST_LIST_TRAVERSE(&incl->included_files, included_file, next) {
if (!is_writable(included_file->path)) {
/* the #tryinclude path exists but is not writable */
ok = 0;
break;
}
}
if (!ok) {
/* at least one include file is not writable */
return -1; return -1;
} }
} }
@ -2779,19 +2960,24 @@ int ast_config_text_file_save2(const char *configfile, const struct ast_config *
/* go thru all the inclusions and make sure all the files involved (configfile plus all its inclusions) /* go thru all the inclusions and make sure all the files involved (configfile plus all its inclusions)
are all truncated to zero bytes and have that nice header*/ are all truncated to zero bytes and have that nice header*/
for (incl = cfg->includes; incl; incl = incl->next) { /* Note: for #exec, we write out the directives but don't zero out the exec files */
if (!incl->exec) { /* leave the execs alone -- we'll write out the #exec directives, but won't zero out the include files or exec files*/ for (incl = cfg->includes_root; incl; incl = incl->next) {
/* normally, fn is just set to incl->included_file, prepended with config dir if relative */ if (incl->include_type != CONFIG_STATEMENT_EXEC) {
fi = set_fn(fn, sizeof(fn), incl->included_file, configfile, fileset); struct config_included_file *included_file;
f = fopen(fn, "w");
if (f) { AST_LIST_TRAVERSE(&incl->included_files, included_file, next) {
gen_header(f, configfile, fn, generator); fi = set_fn(fn, sizeof(fn), included_file->path, configfile, fileset);
fclose(f); /* this should zero out the file */ f = fopen(fn, "w");
} else { if (f) {
ast_log(LOG_ERROR, "Unable to write %s (%s)\n", fn, strerror(errno)); ast_verb(2, "Saving tryinclude '%s'\n", fn);
} gen_header(f, configfile, fn, generator);
if (fi) { fclose(f);
ao2_ref(fi, -1); } else {
ast_log(LOG_ERROR, "Unable to write %s (%s)\n", fn, strerror(errno));
}
if (fi) {
ao2_ref(fi, -1);
}
} }
} }
} }
@ -2830,31 +3016,24 @@ int ast_config_text_file_save2(const char *configfile, const struct ast_config *
} }
/* dump any includes that happen before this category header */ /* dump any includes that happen before this category header */
for (incl=cfg->includes; incl; incl = incl->next) { for (incl = cfg->includes_root; incl; incl = incl->next) {
if (strcmp(incl->include_location_file, cat->file) == 0){ if (strcmp(incl->include_location_file, cat->file) == 0) {
if (cat->lineno > incl->include_location_lineno && !incl->output) { if (cat->lineno > incl->include_location_lineno && !incl->output) {
if (incl->exec) /* add the precomments */
fprintf(f,"#exec \"%s\"\n", incl->exec_file); insert_leading_blank_lines(f, fi, incl->precomments, incl->include_location_lineno);
else print_comment(f, incl->precomments);
fprintf(f,"#include \"%s\"\n", incl->included_file); /* and the include */
print_include(f, incl);
incl->output = 1; incl->output = 1;
} }
} }
} }
/* dump any comments that happen before this category header */
insert_leading_blank_lines(f, fi, cat->precomments, cat->lineno); insert_leading_blank_lines(f, fi, cat->precomments, cat->lineno);
/* Dump section with any appropriate comment */ print_comment(f, cat->precomments);
for (cmt = cat->precomments; cmt; cmt=cmt->next) {
char *cmtp = cmt->cmt; /* dump the category header */
while (cmtp && *cmtp == ';' && *(cmtp+1) == '!') {
char *cmtp2 = strchr(cmtp+1, '\n');
if (cmtp2)
cmtp = cmtp2+1;
else cmtp = 0;
}
if (cmtp)
fprintf(f,"%s", cmtp);
}
fprintf(f, "[%s]", cat->name); fprintf(f, "[%s]", cat->name);
if (cat->ignored || !AST_LIST_EMPTY(&cat->template_instances)) { if (cat->ignored || !AST_LIST_EMPTY(&cat->template_instances)) {
fprintf(f, "("); fprintf(f, "(");
@ -2867,23 +3046,24 @@ int ast_config_text_file_save2(const char *configfile, const struct ast_config *
if (!AST_LIST_EMPTY(&cat->template_instances)) { if (!AST_LIST_EMPTY(&cat->template_instances)) {
struct ast_category_template_instance *x; struct ast_category_template_instance *x;
AST_LIST_TRAVERSE(&cat->template_instances, x, next) { AST_LIST_TRAVERSE(&cat->template_instances, x, next) {
fprintf(f,"%s",x->name); fprintf(f, "%s",x->name);
if (x != AST_LIST_LAST(&cat->template_instances)) if (x != AST_LIST_LAST(&cat->template_instances))
fprintf(f,","); fprintf(f, ",");
} }
} }
fprintf(f, ")"); fprintf(f, ")");
} }
for(cmt = cat->sameline; cmt; cmt=cmt->next) for(cmt = cat->sameline; cmt; cmt = cmt->next)
{ {
fprintf(f,"%s", cmt->cmt); fprintf(f, "%s", cmt->cmt);
} }
if (!cat->sameline) if (!cat->sameline)
fprintf(f,"\n"); fprintf(f, "\n");
for (cmt = cat->trailing; cmt; cmt=cmt->next) {
if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!') /* dump any trailing comments */
fprintf(f,"%s", cmt->cmt); print_comment(f, cat->trailing);
}
/* and we're all done */
fclose(f); fclose(f);
if (fi) { if (fi) {
ao2_ref(fi, -1); ao2_ref(fi, -1);
@ -2935,24 +3115,23 @@ int ast_config_text_file_save2(const char *configfile, const struct ast_config *
return -1; return -1;
} }
/* dump any includes that happen before this category header */ /* dump any includes that happen before this variable */
for (incl=cfg->includes; incl; incl = incl->next) { for (incl = cfg->includes_root; incl; incl = incl->next) {
if (strcmp(incl->include_location_file, var->file) == 0){ if (strcmp(incl->include_location_file, var->file) == 0) {
if (var->lineno > incl->include_location_lineno && !incl->output) { if (var->lineno > incl->include_location_lineno && !incl->output) {
if (incl->exec) /* add the precomments */
fprintf(f,"#exec \"%s\"\n", incl->exec_file); insert_leading_blank_lines(f, fi, incl->precomments, incl->include_location_lineno);
else print_comment(f, incl->precomments);
fprintf(f,"#include \"%s\"\n", incl->included_file); /* and the include */
print_include(f, incl);
incl->output = 1; incl->output = 1;
} }
} }
} }
/* dump any comments that happen before this variable */
insert_leading_blank_lines(f, fi, var->precomments, var->lineno); insert_leading_blank_lines(f, fi, var->precomments, var->lineno);
for (cmt = var->precomments; cmt; cmt=cmt->next) { print_comment(f, var->precomments);
if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
fprintf(f,"%s", cmt->cmt);
}
{ /* Block for 'escaped' scope */ { /* Block for 'escaped' scope */
int escaped_len = 2 * strlen(var->value) + 1; int escaped_len = 2 * strlen(var->value) + 1;
@ -2969,10 +3148,8 @@ int ast_config_text_file_save2(const char *configfile, const struct ast_config *
} }
} }
for (cmt = var->trailing; cmt; cmt=cmt->next) { /* dump any trailing comments */
if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!') print_comment(f, var->trailing);
fprintf(f,"%s", cmt->cmt);
}
if (var->blanklines) { if (var->blanklines) {
blanklines = var->blanklines; blanklines = var->blanklines;
while (blanklines--) while (blanklines--)
@ -2988,6 +3165,42 @@ int ast_config_text_file_save2(const char *configfile, const struct ast_config *
} }
cat = cat->next; cat = cat->next;
} }
/* Now, for files with trailing #include/#exec statements,
we have to make sure every entry is output */
for (incl = cfg->includes_root; incl; incl = incl->next) {
if (!incl->output) {
/* open the respective file */
fi = set_fn(fn, sizeof(fn), incl->include_location_file, configfile, fileset);
f = fopen(fn, "a");
if (!f) {
ast_debug(1, "Unable to open for writing: %s\n", fn);
ast_verb(2, "Unable to write %s (%s)\n", fn, strerror(errno));
if (fi) {
ao2_ref(fi, -1);
}
ao2_ref(fileset, -1);
return -1;
}
/* add the precomments */
insert_leading_blank_lines(f, fi, incl->precomments, incl->include_location_lineno);
print_comment(f, incl->precomments);
/* and the include */
print_include(f, incl);
/* and any trailing comments */
print_comment(f, incl->trailing);
incl->output = 1;
fclose(f);
if (fi) {
ao2_ref(fi, -1);
}
}
}
ast_verb(2, "Saving '%s': saved\n", fn); ast_verb(2, "Saving '%s': saved\n", fn);
} else { } else {
ast_debug(1, "Unable to open for writing: %s\n", fn); ast_debug(1, "Unable to open for writing: %s\n", fn);
@ -2999,35 +3212,6 @@ int ast_config_text_file_save2(const char *configfile, const struct ast_config *
return -1; return -1;
} }
/* Now, for files with trailing #include/#exec statements,
we have to make sure every entry is output */
for (incl=cfg->includes; incl; incl = incl->next) {
if (!incl->output) {
/* open the respective file */
fi = set_fn(fn, sizeof(fn), incl->include_location_file, configfile, fileset);
f = fopen(fn, "a");
if (!f) {
ast_debug(1, "Unable to open for writing: %s\n", fn);
ast_verb(2, "Unable to write %s (%s)\n", fn, strerror(errno));
if (fi) {
ao2_ref(fi, -1);
}
ao2_ref(fileset, -1);
return -1;
}
/* output the respective include */
if (incl->exec)
fprintf(f,"#exec \"%s\"\n", incl->exec_file);
else
fprintf(f,"#include \"%s\"\n", incl->included_file);
fclose(f);
incl->output = 1;
if (fi) {
ao2_ref(fi, -1);
}
}
}
ao2_ref(fileset, -1); /* this should destroy the hash container */ ao2_ref(fileset, -1); /* this should destroy the hash container */
/* pass new configuration to any config hooks */ /* pass new configuration to any config hooks */
@ -3379,7 +3563,7 @@ static int realtime_arguments_to_fields2(va_list ap, int skip, struct ast_variab
* While this works on generic amd64 machines (2014), it doesn't on the * While this works on generic amd64 machines (2014), it doesn't on the
* raspberry PI. The va_arg() manpage says: * raspberry PI. The va_arg() manpage says:
* *
* If ap is passed to a function that uses va_arg(ap,type) then * 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. * 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 * On the raspberry, ap seems to get reset after the call: the contents

Loading…
Cancel
Save