aboutsummaryrefslogtreecommitdiffstats
path: root/main/config.c
diff options
context:
space:
mode:
authortilghman <tilghman@f38db490-d61c-443f-a65b-d21fe96a405b>2007-08-16 21:09:46 +0000
committertilghman <tilghman@f38db490-d61c-443f-a65b-d21fe96a405b>2007-08-16 21:09:46 +0000
commitdbec3d56c146801fad339a1d46a388865b18ffb4 (patch)
tree8fda811f62cb6ffb99847befb7b74b1519ea95ba /main/config.c
parent0fb9c73a989207650aa3ba603824e4593809611b (diff)
Don't reload a configuration file if nothing has changed.
git-svn-id: http://svn.digium.com/svn/asterisk/trunk@79747 f38db490-d61c-443f-a65b-d21fe96a405b
Diffstat (limited to 'main/config.c')
-rw-r--r--main/config.c219
1 files changed, 174 insertions, 45 deletions
diff --git a/main/config.c b/main/config.c
index 01c9c9a0c..1405423c4 100644
--- a/main/config.c
+++ b/main/config.c
@@ -65,12 +65,27 @@ static char *extconfig_conf = "extconfig.conf";
/*! \brief Structure to keep comments for rewriting configuration files */
-/*! \brief Structure to keep comments for rewriting configuration files */
struct ast_comment {
struct ast_comment *next;
char cmt[0];
};
+/*! \brief Hold the mtime for config files, so if we don't need to reread our config, don't. */
+struct cache_file_include {
+ AST_LIST_ENTRY(cache_file_include) list;
+ char include[0];
+};
+
+struct cache_file_mtime {
+ AST_LIST_ENTRY(cache_file_mtime) list;
+ AST_LIST_HEAD(includes, cache_file_include) includes;
+ unsigned int has_exec:1;
+ time_t mtime;
+ char filename[0];
+};
+
+static AST_LIST_HEAD_STATIC(cfmtime_head, cache_file_mtime);
+
#define CB_INCR 250
static void CB_INIT(char **comment_buffer, int *comment_buffer_size, char **lline_buffer, int *lline_buffer_size)
@@ -590,7 +605,58 @@ void ast_config_set_current_category(struct ast_config *cfg, const struct ast_ca
cfg->current = (struct ast_category *) cat;
}
-static int process_text_line(struct ast_config *cfg, struct ast_category **cat, char *buf, int lineno, const char *configfile, int withcomments,
+enum config_cache_attribute_enum {
+ ATTRIBUTE_INCLUDE = 0,
+ ATTRIBUTE_EXEC = 1,
+};
+
+static void config_cache_attribute(const char *configfile, enum config_cache_attribute_enum attrtype, const char *filename)
+{
+ struct cache_file_mtime *cfmtime;
+ struct cache_file_include *cfinclude;
+ struct stat statbuf = { 0, };
+
+ /* Find our cached entry for this configuration file */
+ AST_LIST_LOCK(&cfmtime_head);
+ AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
+ if (!strcmp(cfmtime->filename, configfile))
+ break;
+ }
+ if (!cfmtime) {
+ cfmtime = ast_calloc(1, sizeof(*cfmtime) + strlen(configfile) + 1);
+ if (!cfmtime) {
+ AST_LIST_UNLOCK(&cfmtime_head);
+ return;
+ }
+ AST_LIST_HEAD_INIT(&cfmtime->includes);
+ strcpy(cfmtime->filename, configfile);
+ /* Note that the file mtime is initialized to 0, i.e. 1970 */
+ AST_LIST_INSERT_TAIL(&cfmtime_head, cfmtime, list);
+ }
+
+ if (!stat(configfile, &statbuf))
+ cfmtime->mtime = 0;
+ else
+ cfmtime->mtime = statbuf.st_mtime;
+
+ switch (attrtype) {
+ case ATTRIBUTE_INCLUDE:
+ cfinclude = ast_calloc(1, sizeof(*cfinclude) + strlen(filename) + 1);
+ if (!cfinclude) {
+ AST_LIST_UNLOCK(&cfmtime_head);
+ return;
+ }
+ strcpy(cfinclude->include, filename);
+ AST_LIST_INSERT_TAIL(&cfmtime->includes, cfinclude, list);
+ break;
+ case ATTRIBUTE_EXEC:
+ cfmtime->has_exec = 1;
+ break;
+ }
+ AST_LIST_UNLOCK(&cfmtime_head);
+}
+
+static int process_text_line(struct ast_config *cfg, struct ast_category **cat, char *buf, int lineno, const char *configfile, struct ast_flags flags,
char **comment_buffer, int *comment_buffer_size, char **lline_buffer, int *lline_buffer_size)
{
char *c;
@@ -619,13 +685,13 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
return -1;
}
/* add comments */
- if (withcomments && *comment_buffer && (*comment_buffer)[0] ) {
+ if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && *comment_buffer && (*comment_buffer)[0] ) {
newcat->precomments = ALLOC_COMMENT(*comment_buffer);
}
- if (withcomments && *lline_buffer && (*lline_buffer)[0] ) {
+ if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && *lline_buffer && (*lline_buffer)[0] ) {
newcat->sameline = ALLOC_COMMENT(*lline_buffer);
}
- if ( withcomments )
+ if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
CB_RESET(comment_buffer, lline_buffer);
/* If there are options or categories to inherit from, process them now */
@@ -702,15 +768,20 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
}
/* #exec </path/to/executable>
We create a tmp file, then we #include it, then we delete it. */
- if (do_exec) {
+ if (do_exec) {
+ if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
+ config_cache_attribute(configfile, ATTRIBUTE_EXEC, NULL);
snprintf(exec_file, sizeof(exec_file), "/var/tmp/exec.%d.%ld", (int)time(NULL), (long)pthread_self());
snprintf(cmd, sizeof(cmd), "%s > %s 2>&1", cur, exec_file);
ast_safe_system(cmd);
cur = exec_file;
- } else
+ } else {
+ if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
+ config_cache_attribute(configfile, ATTRIBUTE_INCLUDE, cur);
exec_file[0] = '\0';
+ }
/* A #include */
- do_include = ast_config_internal_load(cur, cfg, withcomments) ? 1 : 0;
+ do_include = ast_config_internal_load(cur, cfg, flags) ? 1 : 0;
if (!ast_strlen_zero(exec_file))
unlink(exec_file);
if (!do_include)
@@ -750,13 +821,13 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
v->blanklines = 0;
ast_variable_append(*cat, v);
/* add comments */
- if (withcomments && *comment_buffer && (*comment_buffer)[0] ) {
+ if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && *comment_buffer && (*comment_buffer)[0] ) {
v->precomments = ALLOC_COMMENT(*comment_buffer);
}
- if (withcomments && *lline_buffer && (*lline_buffer)[0] ) {
+ if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && *lline_buffer && (*lline_buffer)[0] ) {
v->sameline = ALLOC_COMMENT(*lline_buffer);
}
- if ( withcomments )
+ if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
CB_RESET(comment_buffer, lline_buffer);
} else {
@@ -769,7 +840,7 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
return 0;
}
-static struct ast_config *config_text_file_load(const char *database, const char *table, const char *filename, struct ast_config *cfg, int withcomments)
+static struct ast_config *config_text_file_load(const char *database, const char *table, const char *filename, struct ast_config *cfg, struct ast_flags flags)
{
char fn[256];
char buf[8192];
@@ -780,6 +851,8 @@ static struct ast_config *config_text_file_load(const char *database, const char
struct ast_category *cat = NULL;
int count = 0;
struct stat statbuf;
+ struct cache_file_mtime *cfmtime = NULL;
+ struct cache_file_include *cfinclude;
/*! Growable string buffer */
char *comment_buffer=0; /*!< this will be a comment collector.*/
int comment_buffer_size=0; /*!< the amount of storage so far alloc'd for the comment_buffer */
@@ -787,8 +860,8 @@ static struct ast_config *config_text_file_load(const char *database, const char
char *lline_buffer=0; /*!< A buffer for stuff behind the ; */
int lline_buffer_size=0;
-
- cat = ast_config_get_current_category(cfg);
+ if (cfg)
+ cat = ast_config_get_current_category(cfg);
if (filename[0] == '/') {
ast_copy_string(fn, filename, sizeof(fn));
@@ -796,7 +869,7 @@ static struct ast_config *config_text_file_load(const char *database, const char
snprintf(fn, sizeof(fn), "%s/%s", (char *)ast_config_AST_CONFIG_DIR, filename);
}
- if (withcomments) {
+ if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
CB_INIT(&comment_buffer, &comment_buffer_size, &lline_buffer, &lline_buffer_size);
if (!lline_buffer || !comment_buffer) {
ast_log(LOG_ERROR, "Failed to initialize the comment buffer!\n");
@@ -833,6 +906,77 @@ static struct ast_config *config_text_file_load(const char *database, const char
ast_log(LOG_WARNING, "'%s' is not a regular file, ignoring\n", fn);
continue;
}
+
+ if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE)) {
+ /* Find our cached entry for this configuration file */
+ AST_LIST_LOCK(&cfmtime_head);
+ AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
+ if (!strcmp(cfmtime->filename, fn))
+ break;
+ }
+ if (!cfmtime) {
+ cfmtime = ast_calloc(1, sizeof(*cfmtime) + strlen(fn) + 1);
+ if (!cfmtime)
+ continue;
+ AST_LIST_HEAD_INIT(&cfmtime->includes);
+ strcpy(cfmtime->filename, fn);
+ /* Note that the file mtime is initialized to 0, i.e. 1970 */
+ AST_LIST_INSERT_TAIL(&cfmtime_head, cfmtime, list);
+ }
+ }
+
+ if (cfmtime && (!cfmtime->has_exec) && (cfmtime->mtime == statbuf.st_mtime) && ast_test_flag(&flags, CONFIG_FLAG_FILEUNCHANGED)) {
+ /* File is unchanged, what about the (cached) includes (if any)? */
+ int unchanged = 1;
+ AST_LIST_TRAVERSE(&cfmtime->includes, cfinclude, list) {
+ /* We must glob here, because if we did not, then adding a file to globbed directory would
+ * incorrectly cause no reload to be necessary. */
+ char fn2[256];
+#ifdef AST_INCLUDE_GLOB
+ int glob_ret;
+ glob_t globbuf = { .gl_offs = 0 };
+#ifdef SOLARIS
+ glob_ret = glob(cfinclude->include, GLOB_NOCHECK, NULL, &globbuf);
+#else
+ glob_ret = glob(cfinclude->include, GLOB_NOMAGIC|GLOB_BRACE, NULL, &globbuf);
+#endif
+ /* On error, we reparse */
+ if (glob_ret == GLOB_NOSPACE || glob_ret == GLOB_ABORTED)
+ unchanged = 0;
+ else {
+ /* loop over expanded files */
+ int j;
+ for (j = 0; j < globbuf.gl_pathc; j++) {
+ ast_copy_string(fn2, globbuf.gl_pathv[j], sizeof(fn2));
+#else
+ ast_copy_string(fn2, cfinclude->include);
+#endif
+ if (config_text_file_load(NULL, NULL, fn2, NULL, flags) == NULL) {
+ unchanged = 0;
+ /* One change is enough to short-circuit and reload the whole shebang */
+ break;
+ }
+#ifdef AST_INCLUDE_GLOB
+ }
+ }
+#endif
+ }
+
+ if (unchanged) {
+ AST_LIST_UNLOCK(&cfmtime_head);
+ return CONFIG_STATUS_FILEUNCHANGED;
+ }
+ }
+ if (cfmtime)
+ AST_LIST_UNLOCK(&cfmtime_head);
+
+ /* If cfg is NULL, then we just want an answer */
+ if (cfg == NULL)
+ return NULL;
+
+ if (cfmtime)
+ cfmtime->mtime = statbuf.st_mtime;
+
ast_verb(2, "Parsing '%s': ", fn);
fflush(stdout);
if (!(f = fopen(fn, "r"))) {
@@ -846,7 +990,7 @@ static struct ast_config *config_text_file_load(const char *database, const char
while (!feof(f)) {
lineno++;
if (fgets(buf, sizeof(buf), f)) {
- if ( withcomments ) {
+ if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
CB_ADD(&comment_buffer, &comment_buffer_size, lline_buffer); /* add the current lline buffer to the comment buffer */
lline_buffer[0] = 0; /* erase the lline buffer */
}
@@ -883,7 +1027,7 @@ static struct ast_config *config_text_file_load(const char *database, const char
/* Actually have to move what's left over the top, then continue */
char *oldptr;
oldptr = process_buf + strlen(process_buf);
- if ( withcomments ) {
+ if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
CB_ADD(&comment_buffer, &comment_buffer_size, ";");
CB_ADD_LEN(&comment_buffer, &comment_buffer_size, oldptr+1, new_buf-oldptr-1);
}
@@ -897,7 +1041,7 @@ static struct ast_config *config_text_file_load(const char *database, const char
if (!comment) {
/* If ; is found, and we are not nested in a comment,
we immediately stop all comment processing */
- if ( withcomments ) {
+ if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
LLB_ADD(&lline_buffer, &lline_buffer_size, comment_p);
}
*comment_p = '\0';
@@ -906,7 +1050,7 @@ static struct ast_config *config_text_file_load(const char *database, const char
new_buf = comment_p + 1;
}
}
- if ( withcomments && comment && !process_buf )
+ if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment && !process_buf )
{
CB_ADD(&comment_buffer, &comment_buffer_size, buf); /* the whole line is a comment, store it */
}
@@ -914,7 +1058,7 @@ static struct ast_config *config_text_file_load(const char *database, const char
if (process_buf) {
char *buf = ast_strip(process_buf);
if (!ast_strlen_zero(buf)) {
- if (process_text_line(cfg, &cat, buf, lineno, fn, withcomments, &comment_buffer, &comment_buffer_size, &lline_buffer, &lline_buffer_size)) {
+ if (process_text_line(cfg, &cat, buf, lineno, fn, flags, &comment_buffer, &comment_buffer_size, &lline_buffer, &lline_buffer_size)) {
cfg = NULL;
break;
}
@@ -928,7 +1072,7 @@ static struct ast_config *config_text_file_load(const char *database, const char
ast_log(LOG_WARNING,"Unterminated comment detected beginning on line %d\n", nest[comment - 1]);
}
#ifdef AST_INCLUDE_GLOB
- if (!cfg)
+ if (cfg == NULL || cfg == CONFIG_STATUS_FILEUNCHANGED)
break;
}
globfree(&globbuf);
@@ -936,7 +1080,7 @@ static struct ast_config *config_text_file_load(const char *database, const char
}
#endif
- if (cfg && cfg->include_level == 1 && withcomments && comment_buffer) {
+ if (cfg && cfg != CONFIG_STATUS_FILEUNCHANGED && cfg->include_level == 1 && ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer) {
ast_free(comment_buffer);
ast_free(lline_buffer);
comment_buffer = NULL;
@@ -1090,12 +1234,13 @@ int read_config_maps(void)
struct ast_config *config, *configtmp;
struct ast_variable *v;
char *driver, *table, *database, *stringp, *tmp;
+ struct ast_flags flags = { 0 };
clear_config_maps();
configtmp = ast_config_new();
configtmp->max_include_level = 1;
- config = ast_config_internal_load(extconfig_conf, configtmp, 0);
+ config = ast_config_internal_load(extconfig_conf, configtmp, flags);
if (!config) {
ast_config_destroy(configtmp);
return 0;
@@ -1234,7 +1379,7 @@ static struct ast_config_engine text_file_engine = {
.load_func = config_text_file_load,
};
-struct ast_config *ast_config_internal_load(const char *filename, struct ast_config *cfg, int withcomments)
+struct ast_config *ast_config_internal_load(const char *filename, struct ast_config *cfg, struct ast_flags flags)
{
char db[256];
char table[256];
@@ -1263,9 +1408,9 @@ struct ast_config *ast_config_internal_load(const char *filename, struct ast_con
}
}
- result = loader->load_func(db, table, filename, cfg, withcomments);
+ result = loader->load_func(db, table, filename, cfg, flags);
- if (result)
+ if (result && result != CONFIG_STATUS_FILEUNCHANGED)
result->include_level--;
else
cfg->include_level--;
@@ -1273,23 +1418,7 @@ struct ast_config *ast_config_internal_load(const char *filename, struct ast_con
return result;
}
-struct ast_config *ast_config_load(const char *filename)
-{
- struct ast_config *cfg;
- struct ast_config *result;
-
- cfg = ast_config_new();
- if (!cfg)
- return NULL;
-
- result = ast_config_internal_load(filename, cfg, 0);
- if (!result)
- ast_config_destroy(cfg);
-
- return result;
-}
-
-struct ast_config *ast_config_load_with_comments(const char *filename)
+struct ast_config *ast_config_load(const char *filename, struct ast_flags flags)
{
struct ast_config *cfg;
struct ast_config *result;
@@ -1298,8 +1427,8 @@ struct ast_config *ast_config_load_with_comments(const char *filename)
if (!cfg)
return NULL;
- result = ast_config_internal_load(filename, cfg, 1);
- if (!result)
+ result = ast_config_internal_load(filename, cfg, flags);
+ if (!result || result == CONFIG_STATUS_FILEUNCHANGED)
ast_config_destroy(cfg);
return result;