diff options
author | Tilghman Lesher <tilghman@meg.abyt.es> | 2007-08-16 21:09:46 +0000 |
---|---|---|
committer | Tilghman Lesher <tilghman@meg.abyt.es> | 2007-08-16 21:09:46 +0000 |
commit | 56b9568164b694a42e88f1f8a31012078b833203 (patch) | |
tree | 8fda811f62cb6ffb99847befb7b74b1519ea95ba /main/config.c | |
parent | c0060cd99a0a318b504da4c13eee85f3b9043fdc (diff) |
Don't reload a configuration file if nothing has changed.
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@79747 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'main/config.c')
-rw-r--r-- | main/config.c | 219 |
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; |