Wed Apr 6 11:29:44 2011

Asterisk developer's documentation


config.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2010, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief Configuration File Parser
00022  *
00023  * \author Mark Spencer <markster@digium.com>
00024  *
00025  * Includes the Asterisk Realtime API - ARA
00026  * See doc/realtime.txt and doc/extconfig.txt
00027  */
00028 
00029 #include "asterisk.h"
00030 
00031 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 283882 $")
00032 
00033 #include "asterisk/paths.h"   /* use ast_config_AST_CONFIG_DIR */
00034 #include "asterisk/network.h" /* we do some sockaddr manipulation here */
00035 #include <time.h>
00036 #include <sys/stat.h>
00037 
00038 #include <math.h> /* HUGE_VAL */
00039 
00040 #define AST_INCLUDE_GLOB 1
00041 
00042 #include "asterisk/config.h"
00043 #include "asterisk/cli.h"
00044 #include "asterisk/lock.h"
00045 #include "asterisk/utils.h"
00046 #include "asterisk/channel.h"
00047 #include "asterisk/app.h"
00048 #include "asterisk/astobj2.h"
00049 #include "asterisk/strings.h" /* for the ast_str_*() API */
00050 #include "asterisk/netsock2.h"
00051 
00052 #define MAX_NESTED_COMMENTS 128
00053 #define COMMENT_START ";--"
00054 #define COMMENT_END "--;"
00055 #define COMMENT_META ';'
00056 #define COMMENT_TAG '-'
00057 
00058 static char *extconfig_conf = "extconfig.conf";
00059 
00060 
00061 /*! \brief Structure to keep comments for rewriting configuration files */
00062 struct ast_comment {
00063    struct ast_comment *next;
00064    char cmt[0];
00065 };
00066 
00067 /*! \brief Hold the mtime for config files, so if we don't need to reread our config, don't. */
00068 struct cache_file_include {
00069    AST_LIST_ENTRY(cache_file_include) list;
00070    char include[0];
00071 };
00072 
00073 struct cache_file_mtime {
00074    AST_LIST_ENTRY(cache_file_mtime) list;
00075    AST_LIST_HEAD(includes, cache_file_include) includes;
00076    unsigned int has_exec:1;
00077    time_t mtime;
00078    char *who_asked;
00079    char filename[0];
00080 };
00081 
00082 static AST_LIST_HEAD_STATIC(cfmtime_head, cache_file_mtime);
00083 
00084 static int init_appendbuf(void *data)
00085 {
00086    struct ast_str **str = data;
00087    *str = ast_str_create(16);
00088    return *str ? 0 : -1;
00089 }
00090 
00091 AST_THREADSTORAGE_CUSTOM(appendbuf, init_appendbuf, ast_free_ptr);
00092 
00093 /* comment buffers are better implemented using the ast_str_*() API */
00094 #define CB_SIZE 250  /* initial size of comment buffers */
00095 
00096 static void  CB_ADD(struct ast_str **cb, const char *str)
00097 {
00098    ast_str_append(cb, 0, "%s", str);
00099 }
00100 
00101 static void  CB_ADD_LEN(struct ast_str **cb, const char *str, int len)
00102 {
00103    char *s = alloca(len + 1);
00104    ast_copy_string(s, str, len);
00105    ast_str_append(cb, 0, "%s", str);
00106 }
00107 
00108 static void CB_RESET(struct ast_str *cb, struct ast_str *llb)  
00109 { 
00110    if (cb) {
00111       ast_str_reset(cb);
00112    }
00113    if (llb) {
00114       ast_str_reset(llb);
00115    }
00116 }
00117 
00118 static struct ast_comment *ALLOC_COMMENT(struct ast_str *buffer)
00119 { 
00120    struct ast_comment *x = NULL;
00121    if (!buffer || !ast_str_strlen(buffer)) {
00122       return NULL;
00123    }
00124    if ((x = ast_calloc(1, sizeof(*x) + ast_str_strlen(buffer) + 1))) {
00125       strcpy(x->cmt, ast_str_buffer(buffer)); /* SAFE */
00126    }
00127    return x;
00128 }
00129 
00130 /* I need to keep track of each config file, and all its inclusions,
00131    so that we can track blank lines in each */
00132 
00133 struct inclfile {
00134    char *fname;
00135    int lineno;
00136 };
00137 
00138 static int hash_string(const void *obj, const int flags)
00139 {
00140    char *str = ((struct inclfile *) obj)->fname;
00141    int total;
00142 
00143    for (total = 0; *str; str++) {
00144       unsigned int tmp = total;
00145       total <<= 1; /* multiply by 2 */
00146       total += tmp; /* multiply by 3 */
00147       total <<= 2; /* multiply by 12 */
00148       total += tmp; /* multiply by 13 */
00149 
00150       total += ((unsigned int) (*str));
00151    }
00152    if (total < 0) {
00153       total = -total;
00154    }
00155    return total;
00156 }
00157 
00158 static int hashtab_compare_strings(void *a, void *b, int flags)
00159 {
00160    const struct inclfile *ae = a, *be = b;
00161    return !strcmp(ae->fname, be->fname) ? CMP_MATCH | CMP_STOP : 0;
00162 }
00163 
00164 static struct ast_config_map {
00165    struct ast_config_map *next;
00166    int priority;
00167    char *name;
00168    char *driver;
00169    char *database;
00170    char *table;
00171    char stuff[0];
00172 } *config_maps = NULL;
00173 
00174 AST_MUTEX_DEFINE_STATIC(config_lock);
00175 static struct ast_config_engine *config_engine_list;
00176 
00177 #define MAX_INCLUDE_LEVEL 10
00178 
00179 struct ast_category_template_instance {
00180    char name[80]; /* redundant? */
00181    const struct ast_category *inst;
00182    AST_LIST_ENTRY(ast_category_template_instance) next;
00183 };
00184 
00185 struct ast_category {
00186    char name[80];
00187    int ignored;         /*!< do not let user of the config see this category -- set by (!) after the category decl; a template */
00188    int include_level;
00189    char *file;            /*!< the file name from whence this declaration was read */
00190    int lineno;
00191    AST_LIST_HEAD_NOLOCK(template_instance_list, ast_category_template_instance) template_instances;
00192    struct ast_comment *precomments;
00193    struct ast_comment *sameline;
00194    struct ast_comment *trailing; /*!< the last object in the list will get assigned any trailing comments when EOF is hit */
00195    struct ast_variable *root;
00196    struct ast_variable *last;
00197    struct ast_category *next;
00198 };
00199 
00200 struct ast_config {
00201    struct ast_category *root;
00202    struct ast_category *last;
00203    struct ast_category *current;
00204    struct ast_category *last_browse;     /*!< used to cache the last category supplied via category_browse */
00205    int include_level;
00206    int max_include_level;
00207    struct ast_config_include *includes;  /*!< a list of inclusions, which should describe the entire tree */
00208 };
00209 
00210 struct ast_config_include {
00211    char *include_location_file;     /*!< file name in which the include occurs */
00212    int  include_location_lineno;    /*!< lineno where include occurred */
00213    int  exec;                       /*!< set to non-zero if itsa #exec statement */
00214    char *exec_file;                 /*!< if it's an exec, you'll have both the /var/tmp to read, and the original script */
00215    char *included_file;             /*!< file name included */
00216    int inclusion_count;             /*!< if the file is included more than once, a running count thereof -- but, worry not,
00217                                          we explode the instances and will include those-- so all entries will be unique */
00218    int output;                      /*!< a flag to indicate if the inclusion has been output */
00219    struct ast_config_include *next; /*!< ptr to next inclusion in the list */
00220 };
00221 
00222 #ifdef MALLOC_DEBUG
00223 struct ast_variable *_ast_variable_new(const char *name, const char *value, const char *filename, const char *file, const char *func, int lineno) 
00224 #else
00225 struct ast_variable *ast_variable_new(const char *name, const char *value, const char *filename) 
00226 #endif
00227 {
00228    struct ast_variable *variable;
00229    int name_len = strlen(name) + 1; 
00230    int val_len = strlen(value) + 1; 
00231    int fn_len = strlen(filename) + 1;  
00232 
00233 #ifdef MALLOC_DEBUG
00234    if ((variable = __ast_calloc(1, name_len + val_len + fn_len + sizeof(*variable), file, lineno, func))) {
00235 #else
00236    if ((variable = ast_calloc(1, name_len + val_len + fn_len + sizeof(*variable)))) {
00237 #endif
00238       char *dst = variable->stuff;  /* writable space starts here */
00239       variable->name = strcpy(dst, name);
00240       dst += name_len;
00241       variable->value = strcpy(dst, value);
00242       dst += val_len;
00243       variable->file = strcpy(dst, filename);
00244    }
00245    return variable;
00246 }
00247 
00248 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)
00249 {
00250    /* a file should be included ONCE. Otherwise, if one of the instances is changed,
00251     * then all be changed. -- how do we know to include it? -- Handling modified 
00252     * instances is possible, I'd have
00253     * to create a new master for each instance. */
00254    struct ast_config_include *inc;
00255    struct stat statbuf;
00256    
00257    inc = ast_include_find(conf, included_file);
00258    if (inc) {
00259       do {
00260          inc->inclusion_count++;
00261          snprintf(real_included_file_name, real_included_file_name_size, "%s~~%d", included_file, inc->inclusion_count);
00262       } while (stat(real_included_file_name, &statbuf) == 0);
00263       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);
00264    } else
00265       *real_included_file_name = 0;
00266    
00267    inc = ast_calloc(1,sizeof(struct ast_config_include));
00268    inc->include_location_file = ast_strdup(from_file);
00269    inc->include_location_lineno = from_lineno;
00270    if (!ast_strlen_zero(real_included_file_name))
00271       inc->included_file = ast_strdup(real_included_file_name);
00272    else
00273       inc->included_file = ast_strdup(included_file);
00274    
00275    inc->exec = is_exec;
00276    if (is_exec)
00277       inc->exec_file = ast_strdup(exec_file);
00278    
00279    /* attach this new struct to the conf struct */
00280    inc->next = conf->includes;
00281    conf->includes = inc;
00282    
00283    return inc;
00284 }
00285 
00286 void ast_include_rename(struct ast_config *conf, const char *from_file, const char *to_file)
00287 {
00288    struct ast_config_include *incl;
00289    struct ast_category *cat;
00290    struct ast_variable *v;
00291    
00292    int from_len = strlen(from_file);
00293    int to_len = strlen(to_file);
00294    
00295    if (strcmp(from_file, to_file) == 0) /* no use wasting time if the name is the same */
00296       return;
00297    
00298    /* the manager code allows you to read in one config file, then
00299     * write it back out under a different name. But, the new arrangement
00300     * ties output lines to the file name. So, before you try to write
00301     * the config file to disk, better riffle thru the data and make sure
00302     * the file names are changed.
00303     */
00304    /* file names are on categories, includes (of course), and on variables. So,
00305     * traverse all this and swap names */
00306 
00307    for (incl = conf->includes; incl; incl=incl->next) {
00308       if (strcmp(incl->include_location_file,from_file) == 0) {
00309          if (from_len >= to_len)
00310             strcpy(incl->include_location_file, to_file);
00311          else {
00312             free(incl->include_location_file);
00313             incl->include_location_file = strdup(to_file);
00314          }
00315       }
00316    }
00317    for (cat = conf->root; cat; cat = cat->next) {
00318       if (strcmp(cat->file,from_file) == 0) {
00319          if (from_len >= to_len)
00320             strcpy(cat->file, to_file);
00321          else {
00322             free(cat->file);
00323             cat->file = strdup(to_file);
00324          }
00325       }
00326       for (v = cat->root; v; v = v->next) {
00327          if (strcmp(v->file,from_file) == 0) {
00328             if (from_len >= to_len)
00329                strcpy(v->file, to_file);
00330             else {
00331                free(v->file);
00332                v->file = strdup(to_file);
00333             }
00334          }
00335       }
00336    }
00337 }
00338 
00339 struct ast_config_include *ast_include_find(struct ast_config *conf, const char *included_file)
00340 {
00341    struct ast_config_include *x;
00342    for (x=conf->includes;x;x=x->next) {
00343       if (strcmp(x->included_file,included_file) == 0)
00344          return x;
00345    }
00346    return 0;
00347 }
00348 
00349 
00350 void ast_variable_append(struct ast_category *category, struct ast_variable *variable)
00351 {
00352    if (!variable)
00353       return;
00354    if (category->last)
00355       category->last->next = variable;
00356    else
00357       category->root = variable;
00358    category->last = variable;
00359    while (category->last->next)
00360       category->last = category->last->next;
00361 }
00362 
00363 void ast_variable_insert(struct ast_category *category, struct ast_variable *variable, const char *line)
00364 {
00365    struct ast_variable *cur = category->root;
00366    int lineno;
00367    int insertline;
00368 
00369    if (!variable || sscanf(line, "%30d", &insertline) != 1) {
00370       return;
00371    }
00372    if (!insertline) {
00373       variable->next = category->root;
00374       category->root = variable;
00375    } else {
00376       for (lineno = 1; lineno < insertline; lineno++) {
00377          cur = cur->next;
00378          if (!cur->next) {
00379             break;
00380          }
00381       }
00382       variable->next = cur->next;
00383       cur->next = variable;
00384    }
00385 }
00386 
00387 static void ast_comment_destroy(struct ast_comment **comment)
00388 {
00389    struct ast_comment *n, *p;
00390 
00391    for (p = *comment; p; p = n) {
00392       n = p->next;
00393       ast_free(p);
00394    }
00395 
00396    *comment = NULL;
00397 }
00398 
00399 void ast_variables_destroy(struct ast_variable *v)
00400 {
00401    struct ast_variable *vn;
00402 
00403    while (v) {
00404       vn = v;
00405       v = v->next;
00406       ast_comment_destroy(&vn->precomments);
00407       ast_comment_destroy(&vn->sameline);
00408       ast_comment_destroy(&vn->trailing);
00409       ast_free(vn);
00410    }
00411 }
00412 
00413 struct ast_variable *ast_variable_browse(const struct ast_config *config, const char *category)
00414 {
00415    struct ast_category *cat = NULL;
00416 
00417    if (category && config->last_browse && (config->last_browse->name == category)) {
00418       cat = config->last_browse;
00419    } else {
00420       cat = ast_category_get(config, category);
00421    }
00422 
00423    return (cat) ? cat->root : NULL;
00424 }
00425 
00426 const char *ast_config_option(struct ast_config *cfg, const char *cat, const char *var)
00427 {
00428    const char *tmp;
00429    tmp = ast_variable_retrieve(cfg, cat, var);
00430    if (!tmp) {
00431       tmp = ast_variable_retrieve(cfg, "general", var);
00432    }
00433    return tmp;
00434 }
00435 
00436 
00437 const char *ast_variable_retrieve(const struct ast_config *config, const char *category, const char *variable)
00438 {
00439    struct ast_variable *v;
00440 
00441    if (category) {
00442       for (v = ast_variable_browse(config, category); v; v = v->next) {
00443          if (!strcasecmp(variable, v->name)) {
00444             return v->value;
00445          }
00446       }
00447    } else {
00448       struct ast_category *cat;
00449 
00450       for (cat = config->root; cat; cat = cat->next) {
00451          for (v = cat->root; v; v = v->next) {
00452             if (!strcasecmp(variable, v->name)) {
00453                return v->value;
00454             }
00455          }
00456       }
00457    }
00458 
00459    return NULL;
00460 }
00461 
00462 static struct ast_variable *variable_clone(const struct ast_variable *old)
00463 {
00464    struct ast_variable *new = ast_variable_new(old->name, old->value, old->file);
00465 
00466    if (new) {
00467       new->lineno = old->lineno;
00468       new->object = old->object;
00469       new->blanklines = old->blanklines;
00470       /* TODO: clone comments? */
00471    }
00472 
00473    return new;
00474 }
00475  
00476 static void move_variables(struct ast_category *old, struct ast_category *new)
00477 {
00478    struct ast_variable *var = old->root;
00479 
00480    old->root = NULL;
00481    /* we can just move the entire list in a single op */
00482    ast_variable_append(new, var);
00483 }
00484 
00485 struct ast_category *ast_category_new(const char *name, const char *in_file, int lineno) 
00486 {
00487    struct ast_category *category;
00488 
00489    if ((category = ast_calloc(1, sizeof(*category))))
00490       ast_copy_string(category->name, name, sizeof(category->name));
00491    category->file = strdup(in_file);
00492    category->lineno = lineno; /* if you don't know the lineno, set it to 999999 or something real big */
00493    return category;
00494 }
00495 
00496 static struct ast_category *category_get(const struct ast_config *config, const char *category_name, int ignored)
00497 {
00498    struct ast_category *cat;
00499 
00500    /* try exact match first, then case-insensitive match */
00501    for (cat = config->root; cat; cat = cat->next) {
00502       if (cat->name == category_name && (ignored || !cat->ignored))
00503          return cat;
00504    }
00505 
00506    for (cat = config->root; cat; cat = cat->next) {
00507       if (!strcasecmp(cat->name, category_name) && (ignored || !cat->ignored))
00508          return cat;
00509    }
00510 
00511    return NULL;
00512 }
00513 
00514 struct ast_category *ast_category_get(const struct ast_config *config, const char *category_name)
00515 {
00516    return category_get(config, category_name, 0);
00517 }
00518 
00519 int ast_category_exist(const struct ast_config *config, const char *category_name)
00520 {
00521    return !!ast_category_get(config, category_name);
00522 }
00523 
00524 void ast_category_append(struct ast_config *config, struct ast_category *category)
00525 {
00526    if (config->last)
00527       config->last->next = category;
00528    else
00529       config->root = category;
00530    category->include_level = config->include_level;
00531    config->last = category;
00532    config->current = category;
00533 }
00534 
00535 void ast_category_insert(struct ast_config *config, struct ast_category *cat, const char *match)
00536 {
00537    struct ast_category *cur_category;
00538 
00539    if (!cat || !match)
00540       return;
00541    if (!strcasecmp(config->root->name, match)) {
00542       cat->next = config->root;
00543       config->root = cat;
00544       return;
00545    } 
00546    for (cur_category = config->root; cur_category; cur_category = cur_category->next) {
00547       if (!strcasecmp(cur_category->next->name, match)) {
00548          cat->next = cur_category->next;
00549          cur_category->next = cat;
00550          break;
00551       }
00552    }
00553 }
00554 
00555 static void ast_destroy_template_list(struct ast_category *cat)
00556 {
00557    struct ast_category_template_instance *x;
00558 
00559    while ((x = AST_LIST_REMOVE_HEAD(&cat->template_instances, next)))
00560       free(x);
00561 }
00562 
00563 void ast_category_destroy(struct ast_category *cat)
00564 {
00565    ast_variables_destroy(cat->root);
00566    if (cat->file) {
00567       free(cat->file);
00568       cat->file = 0;
00569    }
00570    ast_comment_destroy(&cat->precomments);
00571    ast_comment_destroy(&cat->sameline);
00572    ast_comment_destroy(&cat->trailing);
00573    ast_destroy_template_list(cat);
00574    ast_free(cat);
00575 }
00576 
00577 static void ast_includes_destroy(struct ast_config_include *incls)
00578 {
00579    struct ast_config_include *incl,*inclnext;
00580    
00581    for (incl=incls; incl; incl = inclnext) {
00582       inclnext = incl->next;
00583       if (incl->include_location_file)
00584          free(incl->include_location_file);
00585       if (incl->exec_file)
00586          free(incl->exec_file);
00587       if (incl->included_file)
00588          free(incl->included_file);
00589       free(incl);
00590    }
00591 }
00592 
00593 static struct ast_category *next_available_category(struct ast_category *cat)
00594 {
00595    for (; cat && cat->ignored; cat = cat->next);
00596 
00597    return cat;
00598 }
00599 
00600 /*! return the first var of a category */
00601 struct ast_variable *ast_category_first(struct ast_category *cat)
00602 {
00603    return (cat) ? cat->root : NULL;
00604 }
00605 
00606 struct ast_variable *ast_category_root(struct ast_config *config, char *cat)
00607 {
00608    struct ast_category *category = ast_category_get(config, cat);
00609 
00610    if (category)
00611       return category->root;
00612    return NULL;
00613 }
00614 
00615 char *ast_category_browse(struct ast_config *config, const char *prev)
00616 {  
00617    struct ast_category *cat = NULL;
00618 
00619    if (prev && config->last_browse && (config->last_browse->name == prev))
00620       cat = config->last_browse->next;
00621    else if (!prev && config->root)
00622       cat = config->root;
00623    else if (prev) {
00624       for (cat = config->root; cat; cat = cat->next) {
00625          if (cat->name == prev) {
00626             cat = cat->next;
00627             break;
00628          }
00629       }
00630       if (!cat) {
00631          for (cat = config->root; cat; cat = cat->next) {
00632             if (!strcasecmp(cat->name, prev)) {
00633                cat = cat->next;
00634                break;
00635             }
00636          }
00637       }
00638    }
00639    
00640    if (cat)
00641       cat = next_available_category(cat);
00642 
00643    config->last_browse = cat;
00644    return (cat) ? cat->name : NULL;
00645 }
00646 
00647 struct ast_variable *ast_category_detach_variables(struct ast_category *cat)
00648 {
00649    struct ast_variable *v;
00650 
00651    v = cat->root;
00652    cat->root = NULL;
00653    cat->last = NULL;
00654 
00655    return v;
00656 }
00657 
00658 void ast_category_rename(struct ast_category *cat, const char *name)
00659 {
00660    ast_copy_string(cat->name, name, sizeof(cat->name));
00661 }
00662 
00663 static void inherit_category(struct ast_category *new, const struct ast_category *base)
00664 {
00665    struct ast_variable *var;
00666    struct ast_category_template_instance *x = ast_calloc(1,sizeof(struct ast_category_template_instance));
00667 
00668    strcpy(x->name, base->name);
00669    x->inst = base;
00670    AST_LIST_INSERT_TAIL(&new->template_instances, x, next);
00671    for (var = base->root; var; var = var->next)
00672       ast_variable_append(new, variable_clone(var));
00673 }
00674 
00675 struct ast_config *ast_config_new(void) 
00676 {
00677    struct ast_config *config;
00678 
00679    if ((config = ast_calloc(1, sizeof(*config))))
00680       config->max_include_level = MAX_INCLUDE_LEVEL;
00681    return config;
00682 }
00683 
00684 int ast_variable_delete(struct ast_category *category, const char *variable, const char *match, const char *line)
00685 {
00686    struct ast_variable *cur, *prev=NULL, *curn;
00687    int res = -1;
00688    int lineno = 0;
00689 
00690    cur = category->root;
00691    while (cur) {
00692       if (cur->name == variable) {
00693          if (prev) {
00694             prev->next = cur->next;
00695             if (cur == category->last)
00696                category->last = prev;
00697          } else {
00698             category->root = cur->next;
00699             if (cur == category->last)
00700                category->last = NULL;
00701          }
00702          cur->next = NULL;
00703          ast_variables_destroy(cur);
00704          return 0;
00705       }
00706       prev = cur;
00707       cur = cur->next;
00708    }
00709 
00710    prev = NULL;
00711    cur = category->root;
00712    while (cur) {
00713       curn = cur->next;
00714       if ((!ast_strlen_zero(line) && lineno == atoi(line)) || (ast_strlen_zero(line) && !strcasecmp(cur->name, variable) && (ast_strlen_zero(match) || !strcasecmp(cur->value, match)))) {
00715          if (prev) {
00716             prev->next = cur->next;
00717             if (cur == category->last)
00718                category->last = prev;
00719          } else {
00720             category->root = cur->next;
00721             if (cur == category->last)
00722                category->last = NULL;
00723          }
00724          cur->next = NULL;
00725          ast_variables_destroy(cur);
00726          res = 0;
00727       } else
00728          prev = cur;
00729 
00730       cur = curn;
00731       lineno++;
00732    }
00733    return res;
00734 }
00735 
00736 int ast_variable_update(struct ast_category *category, const char *variable, 
00737                   const char *value, const char *match, unsigned int object)
00738 {
00739    struct ast_variable *cur, *prev=NULL, *newer=NULL;
00740 
00741    for (cur = category->root; cur; prev = cur, cur = cur->next) {
00742       if (strcasecmp(cur->name, variable) ||
00743          (!ast_strlen_zero(match) && strcasecmp(cur->value, match)))
00744          continue;
00745 
00746       if (!(newer = ast_variable_new(variable, value, cur->file)))
00747          return -1;
00748    
00749       newer->next = cur->next;
00750       newer->object = cur->object || object;
00751 
00752       /* Preserve everything */
00753       newer->lineno = cur->lineno;
00754       newer->blanklines = cur->blanklines;
00755       newer->precomments = cur->precomments; cur->precomments = NULL;
00756       newer->sameline = cur->sameline; cur->sameline = NULL;
00757       newer->trailing = cur->trailing; cur->trailing = NULL;
00758 
00759       if (prev)
00760          prev->next = newer;
00761       else
00762          category->root = newer;
00763       if (category->last == cur)
00764          category->last = newer;
00765 
00766       cur->next = NULL;
00767       ast_variables_destroy(cur);
00768 
00769       return 0;
00770    }
00771 
00772    /* Could not find variable to update */
00773    return -1;
00774 }
00775 
00776 int ast_category_delete(struct ast_config *cfg, const char *category)
00777 {
00778    struct ast_category *prev=NULL, *cat;
00779 
00780    cat = cfg->root;
00781    while (cat) {
00782       if (cat->name == category) {
00783          if (prev) {
00784             prev->next = cat->next;
00785             if (cat == cfg->last)
00786                cfg->last = prev;
00787          } else {
00788             cfg->root = cat->next;
00789             if (cat == cfg->last)
00790                cfg->last = NULL;
00791          }
00792          ast_category_destroy(cat);
00793          return 0;
00794       }
00795       prev = cat;
00796       cat = cat->next;
00797    }
00798 
00799    prev = NULL;
00800    cat = cfg->root;
00801    while (cat) {
00802       if (!strcasecmp(cat->name, category)) {
00803          if (prev) {
00804             prev->next = cat->next;
00805             if (cat == cfg->last)
00806                cfg->last = prev;
00807          } else {
00808             cfg->root = cat->next;
00809             if (cat == cfg->last)
00810                cfg->last = NULL;
00811          }
00812          ast_category_destroy(cat);
00813          return 0;
00814       }
00815       prev = cat;
00816       cat = cat->next;
00817    }
00818    return -1;
00819 }
00820 
00821 int ast_category_empty(struct ast_config *cfg, const char *category)
00822 {
00823    struct ast_category *cat;
00824 
00825    for (cat = cfg->root; cat; cat = cat->next) {
00826       if (!strcasecmp(cat->name, category))
00827          continue;
00828       ast_variables_destroy(cat->root);
00829       cat->root = NULL;
00830       cat->last = NULL;
00831       return 0;
00832    }
00833 
00834    return -1;
00835 }
00836 
00837 void ast_config_destroy(struct ast_config *cfg)
00838 {
00839    struct ast_category *cat, *catn;
00840 
00841    if (!cfg)
00842       return;
00843 
00844    ast_includes_destroy(cfg->includes);
00845 
00846    cat = cfg->root;
00847    while (cat) {
00848       catn = cat;
00849       cat = cat->next;
00850       ast_category_destroy(catn);
00851    }
00852    ast_free(cfg);
00853 }
00854 
00855 struct ast_category *ast_config_get_current_category(const struct ast_config *cfg)
00856 {
00857    return cfg->current;
00858 }
00859 
00860 void ast_config_set_current_category(struct ast_config *cfg, const struct ast_category *cat)
00861 {
00862    /* cast below is just to silence compiler warning about dropping "const" */
00863    cfg->current = (struct ast_category *) cat;
00864 }
00865 
00866 enum config_cache_attribute_enum {
00867    ATTRIBUTE_INCLUDE = 0,
00868    ATTRIBUTE_EXEC = 1,
00869 };
00870 
00871 static void config_cache_attribute(const char *configfile, enum config_cache_attribute_enum attrtype, const char *filename, const char *who_asked)
00872 {
00873    struct cache_file_mtime *cfmtime;
00874    struct cache_file_include *cfinclude;
00875    struct stat statbuf = { 0, };
00876 
00877    /* Find our cached entry for this configuration file */
00878    AST_LIST_LOCK(&cfmtime_head);
00879    AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
00880       if (!strcmp(cfmtime->filename, configfile) && !strcmp(cfmtime->who_asked, who_asked))
00881          break;
00882    }
00883    if (!cfmtime) {
00884       cfmtime = ast_calloc(1, sizeof(*cfmtime) + strlen(configfile) + 1 + strlen(who_asked) + 1);
00885       if (!cfmtime) {
00886          AST_LIST_UNLOCK(&cfmtime_head);
00887          return;
00888       }
00889       AST_LIST_HEAD_INIT(&cfmtime->includes);
00890       strcpy(cfmtime->filename, configfile);
00891       cfmtime->who_asked = cfmtime->filename + strlen(configfile) + 1;
00892       strcpy(cfmtime->who_asked, who_asked);
00893       /* Note that the file mtime is initialized to 0, i.e. 1970 */
00894       AST_LIST_INSERT_SORTALPHA(&cfmtime_head, cfmtime, list, filename);
00895    }
00896 
00897    if (!stat(configfile, &statbuf))
00898       cfmtime->mtime = 0;
00899    else
00900       cfmtime->mtime = statbuf.st_mtime;
00901 
00902    switch (attrtype) {
00903    case ATTRIBUTE_INCLUDE:
00904       AST_LIST_TRAVERSE(&cfmtime->includes, cfinclude, list) {
00905          if (!strcmp(cfinclude->include, filename)) {
00906             AST_LIST_UNLOCK(&cfmtime_head);
00907             return;
00908          }
00909       }
00910       cfinclude = ast_calloc(1, sizeof(*cfinclude) + strlen(filename) + 1);
00911       if (!cfinclude) {
00912          AST_LIST_UNLOCK(&cfmtime_head);
00913          return;
00914       }
00915       strcpy(cfinclude->include, filename);
00916       AST_LIST_INSERT_TAIL(&cfmtime->includes, cfinclude, list);
00917       break;
00918    case ATTRIBUTE_EXEC:
00919       cfmtime->has_exec = 1;
00920       break;
00921    }
00922    AST_LIST_UNLOCK(&cfmtime_head);
00923 }
00924 
00925 /*! \brief parse one line in the configuration.
00926  * \verbatim
00927  * We can have a category header [foo](...)
00928  * a directive          #include / #exec
00929  * or a regular line       name = value
00930  * \endverbatim
00931  */
00932 static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
00933    char *buf, int lineno, const char *configfile, struct ast_flags flags,
00934    struct ast_str *comment_buffer,
00935    struct ast_str *lline_buffer,
00936    const char *suggested_include_file,
00937    struct ast_category **last_cat, struct ast_variable **last_var, const char *who_asked)
00938 {
00939    char *c;
00940    char *cur = buf;
00941    struct ast_variable *v;
00942    char cmd[512], exec_file[512];
00943 
00944    /* Actually parse the entry */
00945    if (cur[0] == '[') { /* A category header */
00946       /* format is one of the following:
00947        * [foo] define a new category named 'foo'
00948        * [foo](!) define a new template category named 'foo'
00949        * [foo](+) append to category 'foo', error if foo does not exist.
00950        * [foo](a) define a new category and inherit from template a.
00951        *    You can put a comma-separated list of templates and '!' and '+'
00952        *    between parentheses, with obvious meaning.
00953        */
00954       struct ast_category *newcat = NULL;
00955       char *catname;
00956 
00957       c = strchr(cur, ']');
00958       if (!c) {
00959          ast_log(LOG_WARNING, "parse error: no closing ']', line %d of %s\n", lineno, configfile);
00960          return -1;
00961       }
00962       *c++ = '\0';
00963       cur++;
00964       if (*c++ != '(')
00965          c = NULL;
00966       catname = cur;
00967       if (!(*cat = newcat = ast_category_new(catname,
00968             S_OR(suggested_include_file, cfg->include_level == 1 ? "" : configfile),
00969             lineno))) {
00970          return -1;
00971       }
00972       (*cat)->lineno = lineno;
00973       *last_var = 0;
00974       *last_cat = newcat;
00975       
00976       /* add comments */
00977       if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
00978          newcat->precomments = ALLOC_COMMENT(comment_buffer);
00979       if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
00980          newcat->sameline = ALLOC_COMMENT(lline_buffer);
00981       if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
00982          CB_RESET(comment_buffer, lline_buffer);
00983       
00984       /* If there are options or categories to inherit from, process them now */
00985       if (c) {
00986          if (!(cur = strchr(c, ')'))) {
00987             ast_log(LOG_WARNING, "parse error: no closing ')', line %d of %s\n", lineno, configfile);
00988             return -1;
00989          }
00990          *cur = '\0';
00991          while ((cur = strsep(&c, ","))) {
00992             if (!strcasecmp(cur, "!")) {
00993                (*cat)->ignored = 1;
00994             } else if (!strcasecmp(cur, "+")) {
00995                *cat = category_get(cfg, catname, 1);
00996                if (!(*cat)) {
00997                   if (newcat)
00998                      ast_category_destroy(newcat);
00999                   ast_log(LOG_WARNING, "Category addition requested, but category '%s' does not exist, line %d of %s\n", catname, lineno, configfile);
01000                   return -1;
01001                }
01002                if (newcat) {
01003                   move_variables(newcat, *cat);
01004                   ast_category_destroy(newcat);
01005                   newcat = NULL;
01006                }
01007             } else {
01008                struct ast_category *base;
01009             
01010                base = category_get(cfg, cur, 1);
01011                if (!base) {
01012                   ast_log(LOG_WARNING, "Inheritance requested, but category '%s' does not exist, line %d of %s\n", cur, lineno, configfile);
01013                   return -1;
01014                }
01015                inherit_category(*cat, base);
01016             }
01017          }
01018       }
01019       if (newcat)
01020          ast_category_append(cfg, *cat);
01021    } else if (cur[0] == '#') { /* A directive - #include or #exec */
01022       char *cur2;
01023       char real_inclusion_name[256];
01024       struct ast_config_include *inclu;
01025       int do_include = 0;  /* otherwise, it is exec */
01026 
01027       cur++;
01028       c = cur;
01029       while (*c && (*c > 32)) {
01030          c++;
01031       }
01032 
01033       if (*c) {
01034          *c = '\0';
01035          /* Find real argument */
01036          c = ast_strip(c + 1);
01037          if (!(*c)) {
01038             c = NULL;
01039          }
01040       } else {
01041          c = NULL;
01042       }
01043       if (!strcasecmp(cur, "include")) {
01044          do_include = 1;
01045       } else if (!strcasecmp(cur, "exec")) {
01046          if (!ast_opt_exec_includes) {
01047             ast_log(LOG_WARNING, "Cannot perform #exec unless execincludes option is enabled in asterisk.conf (options section)!\n");
01048             return 0;   /* XXX is this correct ? or we should return -1 ? */
01049          }
01050       } else {
01051          ast_log(LOG_WARNING, "Unknown directive '#%s' at line %d of %s\n", cur, lineno, configfile);
01052          return 0;   /* XXX is this correct ? or we should return -1 ? */
01053       }
01054 
01055       if (c == NULL) {
01056          ast_log(LOG_WARNING, "Directive '#%s' needs an argument (%s) at line %d of %s\n", 
01057                do_include ? "include" : "exec",
01058                do_include ? "filename" : "/path/to/executable",
01059                lineno,
01060                configfile);
01061          return 0;   /* XXX is this correct ? or we should return -1 ? */
01062       }
01063 
01064       cur = c;
01065       /* Strip off leading and trailing "'s and <>'s */
01066       /* Dequote */
01067       if ((*c == '"') || (*c == '<')) {
01068          char quote_char = *c;
01069          if (quote_char == '<') {
01070             quote_char = '>';
01071          }
01072 
01073          if (*(c + strlen(c) - 1) == quote_char) {
01074             cur++;
01075             *(c + strlen(c) - 1) = '\0';
01076          }
01077       }
01078       cur2 = cur;
01079 
01080       /* #exec </path/to/executable>
01081          We create a tmp file, then we #include it, then we delete it. */
01082       if (!do_include) {
01083          struct timeval now = ast_tvnow();
01084          if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
01085             config_cache_attribute(configfile, ATTRIBUTE_EXEC, NULL, who_asked);
01086          snprintf(exec_file, sizeof(exec_file), "/var/tmp/exec.%d%d.%ld", (int)now.tv_sec, (int)now.tv_usec, (long)pthread_self());
01087          snprintf(cmd, sizeof(cmd), "%s > %s 2>&1", cur, exec_file);
01088          ast_safe_system(cmd);
01089          cur = exec_file;
01090       } else {
01091          if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
01092             config_cache_attribute(configfile, ATTRIBUTE_INCLUDE, cur, who_asked);
01093          exec_file[0] = '\0';
01094       }
01095       /* A #include */
01096       /* record this inclusion */
01097       inclu = ast_include_new(cfg, cfg->include_level == 1 ? "" : configfile, cur, !do_include, cur2, lineno, real_inclusion_name, sizeof(real_inclusion_name));
01098 
01099       do_include = ast_config_internal_load(cur, cfg, flags, real_inclusion_name, who_asked) ? 1 : 0;
01100       if (!ast_strlen_zero(exec_file))
01101          unlink(exec_file);
01102       if (!do_include) {
01103          ast_log(LOG_ERROR, "The file '%s' was listed as a #include but it does not exist.\n", cur);
01104          return -1;
01105       }
01106       /* XXX otherwise what ? the default return is 0 anyways */
01107 
01108    } else {
01109       /* Just a line (variable = value) */
01110       int object = 0;
01111       if (!(*cat)) {
01112          ast_log(LOG_WARNING,
01113             "parse error: No category context for line %d of %s\n", lineno, configfile);
01114          return -1;
01115       }
01116       c = strchr(cur, '=');
01117 
01118       if (c && c > cur && (*(c - 1) == '+')) {
01119          struct ast_variable *var, *replace = NULL;
01120          struct ast_str **str = ast_threadstorage_get(&appendbuf, sizeof(*str));
01121 
01122          if (!str || !*str) {
01123             return -1;
01124          }
01125 
01126          *(c - 1) = '\0';
01127          c++;
01128          cur = ast_strip(cur);
01129 
01130          /* Must iterate through category until we find last variable of same name (since there could be multiple) */
01131          for (var = ast_category_first(*cat); var; var = var->next) {
01132             if (!strcmp(var->name, cur)) {
01133                replace = var;
01134             }
01135          }
01136 
01137          if (!replace) {
01138             /* Nothing to replace; just set a variable normally. */
01139             goto set_new_variable;
01140          }
01141 
01142          ast_str_set(str, 0, "%s", replace->value);
01143          ast_str_append(str, 0, "%s", c);
01144          ast_str_trim_blanks(*str);
01145          ast_variable_update(*cat, replace->name, ast_skip_blanks(ast_str_buffer(*str)), replace->value, object);
01146       } else if (c) {
01147          *c = 0;
01148          c++;
01149          /* Ignore > in => */
01150          if (*c== '>') {
01151             object = 1;
01152             c++;
01153          }
01154 set_new_variable:
01155          if ((v = ast_variable_new(ast_strip(cur), ast_strip(c), S_OR(suggested_include_file, cfg->include_level == 1 ? "" : configfile)))) {
01156             v->lineno = lineno;
01157             v->object = object;
01158             *last_cat = 0;
01159             *last_var = v;
01160             /* Put and reset comments */
01161             v->blanklines = 0;
01162             ast_variable_append(*cat, v);
01163             /* add comments */
01164             if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
01165                v->precomments = ALLOC_COMMENT(comment_buffer);
01166             if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
01167                v->sameline = ALLOC_COMMENT(lline_buffer);
01168             if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
01169                CB_RESET(comment_buffer, lline_buffer);
01170             
01171          } else {
01172             return -1;
01173          }
01174       } else {
01175          ast_log(LOG_WARNING, "No '=' (equal sign) in line %d of %s\n", lineno, configfile);
01176       }
01177    }
01178    return 0;
01179 }
01180 
01181 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, const char *suggested_include_file, const char *who_asked)
01182 {
01183    char fn[256];
01184 #if defined(LOW_MEMORY)
01185    char buf[512];
01186 #else
01187    char buf[8192];
01188 #endif
01189    char *new_buf, *comment_p, *process_buf;
01190    FILE *f;
01191    int lineno=0;
01192    int comment = 0, nest[MAX_NESTED_COMMENTS];
01193    struct ast_category *cat = NULL;
01194    int count = 0;
01195    struct stat statbuf;
01196    struct cache_file_mtime *cfmtime = NULL;
01197    struct cache_file_include *cfinclude;
01198    struct ast_variable *last_var = 0;
01199    struct ast_category *last_cat = 0;
01200    /*! Growable string buffer */
01201    struct ast_str *comment_buffer = NULL; /*!< this will be a comment collector.*/
01202    struct ast_str *lline_buffer = NULL;   /*!< A buffer for stuff behind the ; */
01203 
01204    if (cfg)
01205       cat = ast_config_get_current_category(cfg);
01206 
01207    if (filename[0] == '/') {
01208       ast_copy_string(fn, filename, sizeof(fn));
01209    } else {
01210       snprintf(fn, sizeof(fn), "%s/%s", ast_config_AST_CONFIG_DIR, filename);
01211    }
01212 
01213    if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
01214       comment_buffer = ast_str_create(CB_SIZE);
01215       if (comment_buffer)
01216          lline_buffer = ast_str_create(CB_SIZE);
01217       if (!lline_buffer) {
01218          if (comment_buffer)
01219             ast_free(comment_buffer);
01220          ast_log(LOG_ERROR, "Failed to initialize the comment buffer!\n");
01221          return NULL;
01222       }
01223    }
01224 #ifdef AST_INCLUDE_GLOB
01225    {
01226       int glob_ret;
01227       glob_t globbuf;
01228       globbuf.gl_offs = 0; /* initialize it to silence gcc */
01229       glob_ret = glob(fn, MY_GLOB_FLAGS, NULL, &globbuf);
01230       if (glob_ret == GLOB_NOSPACE)
01231          ast_log(LOG_WARNING,
01232             "Glob Expansion of pattern '%s' failed: Not enough memory\n", fn);
01233       else if (glob_ret  == GLOB_ABORTED)
01234          ast_log(LOG_WARNING,
01235             "Glob Expansion of pattern '%s' failed: Read error\n", fn);
01236       else  {
01237          /* loop over expanded files */
01238          int i;
01239          for (i=0; i<globbuf.gl_pathc; i++) {
01240             ast_copy_string(fn, globbuf.gl_pathv[i], sizeof(fn));
01241 #endif
01242    /*
01243     * The following is not a loop, but just a convenient way to define a block
01244     * (using do { } while(0) ), and be able to exit from it with 'continue'
01245     * or 'break' in case of errors. Nice trick.
01246     */
01247    do {
01248       if (stat(fn, &statbuf))
01249          continue;
01250 
01251       if (!S_ISREG(statbuf.st_mode)) {
01252          ast_log(LOG_WARNING, "'%s' is not a regular file, ignoring\n", fn);
01253          continue;
01254       }
01255 
01256       if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE)) {
01257          /* Find our cached entry for this configuration file */
01258          AST_LIST_LOCK(&cfmtime_head);
01259          AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
01260             if (!strcmp(cfmtime->filename, fn) && !strcmp(cfmtime->who_asked, who_asked))
01261                break;
01262          }
01263          if (!cfmtime) {
01264             cfmtime = ast_calloc(1, sizeof(*cfmtime) + strlen(fn) + 1 + strlen(who_asked) + 1);
01265             if (!cfmtime)
01266                continue;
01267             AST_LIST_HEAD_INIT(&cfmtime->includes);
01268             strcpy(cfmtime->filename, fn);
01269             cfmtime->who_asked = cfmtime->filename + strlen(fn) + 1;
01270             strcpy(cfmtime->who_asked, who_asked);
01271             /* Note that the file mtime is initialized to 0, i.e. 1970 */
01272             AST_LIST_INSERT_SORTALPHA(&cfmtime_head, cfmtime, list, filename);
01273          }
01274       }
01275 
01276       if (cfmtime && (!cfmtime->has_exec) && (cfmtime->mtime == statbuf.st_mtime) && ast_test_flag(&flags, CONFIG_FLAG_FILEUNCHANGED)) {
01277          /* File is unchanged, what about the (cached) includes (if any)? */
01278          int unchanged = 1;
01279          AST_LIST_TRAVERSE(&cfmtime->includes, cfinclude, list) {
01280             /* We must glob here, because if we did not, then adding a file to globbed directory would
01281              * incorrectly cause no reload to be necessary. */
01282             char fn2[256];
01283 #ifdef AST_INCLUDE_GLOB
01284             int glob_return;
01285             glob_t glob_buf = { .gl_offs = 0 };
01286             glob_return = glob(cfinclude->include, MY_GLOB_FLAGS, NULL, &glob_buf);
01287             /* On error, we reparse */
01288             if (glob_return == GLOB_NOSPACE || glob_return  == GLOB_ABORTED)
01289                unchanged = 0;
01290             else  {
01291                /* loop over expanded files */
01292                int j;
01293                for (j = 0; j < glob_buf.gl_pathc; j++) {
01294                   ast_copy_string(fn2, glob_buf.gl_pathv[j], sizeof(fn2));
01295 #else
01296                   ast_copy_string(fn2, cfinclude->include);
01297 #endif
01298                   if (config_text_file_load(NULL, NULL, fn2, NULL, flags, "", who_asked) == NULL) {
01299                      /* that second-to-last field needs to be looked at in this case... TODO */
01300                      unchanged = 0;
01301                      /* One change is enough to short-circuit and reload the whole shebang */
01302                      break;
01303                   }
01304 #ifdef AST_INCLUDE_GLOB
01305                }
01306             }
01307 #endif
01308          }
01309 
01310          if (unchanged) {
01311             AST_LIST_UNLOCK(&cfmtime_head);
01312             return CONFIG_STATUS_FILEUNCHANGED;
01313          }
01314       }
01315       if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
01316          AST_LIST_UNLOCK(&cfmtime_head);
01317 
01318       /* If cfg is NULL, then we just want an answer */
01319       if (cfg == NULL)
01320          return NULL;
01321 
01322       if (cfmtime)
01323          cfmtime->mtime = statbuf.st_mtime;
01324 
01325       ast_verb(2, "Parsing '%s': ", fn);
01326          fflush(stdout);
01327       if (!(f = fopen(fn, "r"))) {
01328          ast_debug(1, "No file to parse: %s\n", fn);
01329          ast_verb(2, "Not found (%s)\n", strerror(errno));
01330          continue;
01331       }
01332       count++;
01333       /* If we get to this point, then we're loading regardless */
01334       ast_clear_flag(&flags, CONFIG_FLAG_FILEUNCHANGED);
01335       ast_debug(1, "Parsing %s\n", fn);
01336       ast_verb(2, "Found\n");
01337       while (!feof(f)) {
01338          lineno++;
01339          if (fgets(buf, sizeof(buf), f)) {
01340             if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && lline_buffer && ast_str_strlen(lline_buffer)) {
01341                CB_ADD(&comment_buffer, ast_str_buffer(lline_buffer));       /* add the current lline buffer to the comment buffer */
01342                ast_str_reset(lline_buffer);        /* erase the lline buffer */
01343             }
01344             
01345             new_buf = buf;
01346             if (comment) 
01347                process_buf = NULL;
01348             else
01349                process_buf = buf;
01350             
01351             if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && ast_str_strlen(comment_buffer) && (ast_strlen_zero(buf) || strlen(buf) == strspn(buf," \t\n\r"))) {
01352                /* blank line? really? Can we add it to an existing comment and maybe preserve inter- and post- comment spacing? */
01353                CB_ADD(&comment_buffer, "\n");       /* add a newline to the comment buffer */
01354                continue; /* go get a new line, then */
01355             }
01356             
01357             while ((comment_p = strchr(new_buf, COMMENT_META))) {
01358                if ((comment_p > new_buf) && (*(comment_p - 1) == '\\')) {
01359                   /* Escaped semicolons aren't comments. */
01360                   new_buf = comment_p + 1;
01361                } else if (comment_p[1] == COMMENT_TAG && comment_p[2] == COMMENT_TAG && (comment_p[3] != '-')) {
01362                   /* Meta-Comment start detected ";--" */
01363                   if (comment < MAX_NESTED_COMMENTS) {
01364                      *comment_p = '\0';
01365                      new_buf = comment_p + 3;
01366                      comment++;
01367                      nest[comment-1] = lineno;
01368                   } else {
01369                      ast_log(LOG_ERROR, "Maximum nest limit of %d reached.\n", MAX_NESTED_COMMENTS);
01370                   }
01371                } else if ((comment_p >= new_buf + 2) &&
01372                      (*(comment_p - 1) == COMMENT_TAG) &&
01373                      (*(comment_p - 2) == COMMENT_TAG)) {
01374                   /* Meta-Comment end detected */
01375                   comment--;
01376                   new_buf = comment_p + 1;
01377                   if (!comment) {
01378                      /* Back to non-comment now */
01379                      if (process_buf) {
01380                         /* Actually have to move what's left over the top, then continue */
01381                         char *oldptr;
01382                         oldptr = process_buf + strlen(process_buf);
01383                         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
01384                            CB_ADD(&comment_buffer, ";");
01385                            CB_ADD_LEN(&comment_buffer, oldptr+1, new_buf-oldptr-1);
01386                         }
01387                         
01388                         memmove(oldptr, new_buf, strlen(new_buf) + 1);
01389                         new_buf = oldptr;
01390                      } else
01391                         process_buf = new_buf;
01392                   }
01393                } else {
01394                   if (!comment) {
01395                      /* If ; is found, and we are not nested in a comment, 
01396                         we immediately stop all comment processing */
01397                      if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
01398                         CB_ADD(&lline_buffer, comment_p);
01399                      }
01400                      *comment_p = '\0'; 
01401                      new_buf = comment_p;
01402                   } else
01403                      new_buf = comment_p + 1;
01404                }
01405             }
01406             if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment && !process_buf ) {
01407                CB_ADD(&comment_buffer, buf);  /* the whole line is a comment, store it */
01408             }
01409             
01410             if (process_buf) {
01411                char *buffer = ast_strip(process_buf);
01412                if (!ast_strlen_zero(buffer)) {
01413                   if (process_text_line(cfg, &cat, buffer, lineno, fn, flags, comment_buffer, lline_buffer, suggested_include_file, &last_cat, &last_var, who_asked)) {
01414                      cfg = CONFIG_STATUS_FILEINVALID;
01415                      break;
01416                   }
01417                }
01418             }
01419          }
01420       }
01421       /* end of file-- anything in a comment buffer? */
01422       if (last_cat) {
01423          if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && ast_str_strlen(comment_buffer)) {
01424             if (lline_buffer && ast_str_strlen(lline_buffer)) {
01425                CB_ADD(&comment_buffer, ast_str_buffer(lline_buffer));       /* add the current lline buffer to the comment buffer */
01426                ast_str_reset(lline_buffer);        /* erase the lline buffer */
01427             }
01428             last_cat->trailing = ALLOC_COMMENT(comment_buffer);
01429          }
01430       } else if (last_var) {
01431          if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && ast_str_strlen(comment_buffer)) {
01432             if (lline_buffer && ast_str_strlen(lline_buffer)) {
01433                CB_ADD(&comment_buffer, ast_str_buffer(lline_buffer));       /* add the current lline buffer to the comment buffer */
01434                ast_str_reset(lline_buffer);        /* erase the lline buffer */
01435             }
01436             last_var->trailing = ALLOC_COMMENT(comment_buffer);
01437          }
01438       } else {
01439          if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && ast_str_strlen(comment_buffer)) {
01440             ast_debug(1, "Nothing to attach comments to, discarded: %s\n", ast_str_buffer(comment_buffer));
01441          }
01442       }
01443       if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
01444          CB_RESET(comment_buffer, lline_buffer);
01445 
01446       fclose(f);
01447    } while (0);
01448    if (comment) {
01449       ast_log(LOG_WARNING,"Unterminated comment detected beginning on line %d\n", nest[comment - 1]);
01450    }
01451 #ifdef AST_INCLUDE_GLOB
01452                if (cfg == NULL || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) {
01453                   break;
01454                }
01455             }
01456             globfree(&globbuf);
01457          }
01458       }
01459 #endif
01460 
01461    if (cfg && cfg != CONFIG_STATUS_FILEUNCHANGED && cfg != CONFIG_STATUS_FILEINVALID && cfg->include_level == 1 && ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
01462       if (comment_buffer)
01463          ast_free(comment_buffer);
01464       if (lline_buffer)
01465          ast_free(lline_buffer);
01466       comment_buffer = NULL;
01467       lline_buffer = NULL;
01468    }
01469    
01470    if (count == 0)
01471       return NULL;
01472 
01473    return cfg;
01474 }
01475 
01476 
01477 /* NOTE: categories and variables each have a file and lineno attribute. On a save operation, these are used to determine
01478    which file and line number to write out to. Thus, an entire hierarchy of config files (via #include statements) can be
01479    recreated. BUT, care must be taken to make sure that every cat and var has the proper file name stored, or you may
01480    be shocked and mystified as to why things are not showing up in the files! 
01481 
01482    Also, All #include/#exec statements are recorded in the "includes" LL in the ast_config structure. The file name
01483    and line number are stored for each include, plus the name of the file included, so that these statements may be
01484    included in the output files on a file_save operation. 
01485 
01486    The lineno's are really just for relative placement in the file. There is no attempt to make sure that blank lines
01487    are included to keep the lineno's the same between input and output. The lineno fields are used mainly to determine
01488    the position of the #include and #exec directives. So, blank lines tend to disappear from a read/rewrite operation,
01489    and a header gets added.
01490 
01491    vars and category heads are output in the order they are stored in the config file. So, if the software
01492    shuffles these at all, then the placement of #include directives might get a little mixed up, because the
01493    file/lineno data probably won't get changed.
01494 
01495 */
01496 
01497 static void gen_header(FILE *f1, const char *configfile, const char *fn, const char *generator)
01498 {
01499    char date[256]="";
01500    time_t t;
01501 
01502    time(&t);
01503    ast_copy_string(date, ctime(&t), sizeof(date));
01504 
01505    fprintf(f1, ";!\n");
01506    fprintf(f1, ";! Automatically generated configuration file\n");
01507    if (strcmp(configfile, fn))
01508       fprintf(f1, ";! Filename: %s (%s)\n", configfile, fn);
01509    else
01510       fprintf(f1, ";! Filename: %s\n", configfile);
01511    fprintf(f1, ";! Generator: %s\n", generator);
01512    fprintf(f1, ";! Creation Date: %s", date);
01513    fprintf(f1, ";!\n");
01514 }
01515 
01516 static void   inclfile_destroy(void *obj)
01517 {
01518    const struct inclfile *o = obj;
01519 
01520    if (o->fname)
01521       free(o->fname);
01522 }
01523 
01524 
01525 static void set_fn(char *fn, int fn_size, const char *file, const char *configfile, struct ao2_container *fileset, struct inclfile **fi)
01526 {
01527    struct inclfile lookup;
01528    
01529    if (!file || file[0] == 0) {
01530       if (configfile[0] == '/')
01531          ast_copy_string(fn, configfile, fn_size);
01532       else
01533          snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, configfile);
01534    } else if (file[0] == '/') 
01535       ast_copy_string(fn, file, fn_size);
01536    else
01537       snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, file);
01538    lookup.fname = fn;
01539    *fi = ao2_find(fileset, &lookup, OBJ_POINTER);
01540    if (!(*fi)) {
01541       /* set up a file scratch pad */
01542       struct inclfile *fx = ao2_alloc(sizeof(struct inclfile), inclfile_destroy);
01543       fx->fname = ast_strdup(fn);
01544       fx->lineno = 1;
01545       *fi = fx;
01546       ao2_link(fileset, fx);
01547    }
01548 }
01549 
01550 static int count_linefeeds(char *str)
01551 {
01552    int count = 0;
01553 
01554    while (*str) {
01555       if (*str =='\n')
01556          count++;
01557       str++;
01558    }
01559    return count;
01560 }
01561 
01562 static int count_linefeeds_in_comments(struct ast_comment *x)
01563 {
01564    int count = 0;
01565 
01566    while (x) {
01567       count += count_linefeeds(x->cmt);
01568       x = x->next;
01569    }
01570    return count;
01571 }
01572 
01573 static void insert_leading_blank_lines(FILE *fp, struct inclfile *fi, struct ast_comment *precomments, int lineno)
01574 {
01575    int precomment_lines = count_linefeeds_in_comments(precomments);
01576    int i;
01577 
01578    /* I don't have to worry about those ;! comments, they are
01579       stored in the precomments, but not printed back out.
01580       I did have to make sure that comments following
01581       the ;! header comments were not also deleted in the process */
01582    if (lineno - precomment_lines - fi->lineno < 0) { /* insertions can mess up the line numbering and produce negative numbers that mess things up */
01583       return;
01584    } else if (lineno == 0) {
01585       /* Line replacements also mess things up */
01586       return;
01587    } else if (lineno - precomment_lines - fi->lineno < 5) {
01588       /* Only insert less than 5 blank lines; if anything more occurs,
01589        * it's probably due to context deletion. */
01590       for (i = fi->lineno; i < lineno - precomment_lines; i++) {
01591          fprintf(fp, "\n");
01592       }
01593    } else {
01594       /* Deletion occurred - insert a single blank line, for separation of
01595        * contexts. */
01596       fprintf(fp, "\n");
01597    }
01598  
01599    fi->lineno = lineno + 1; /* Advance the file lineno */
01600 }
01601 
01602 int config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator)
01603 {
01604    return ast_config_text_file_save(configfile, cfg, generator);
01605 }
01606 
01607 int ast_config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator)
01608 {
01609    FILE *f;
01610    char fn[256];
01611    struct ast_variable *var;
01612    struct ast_category *cat;
01613    struct ast_comment *cmt;
01614    struct ast_config_include *incl;
01615    int blanklines = 0;
01616    struct ao2_container *fileset = ao2_container_alloc(180000, hash_string, hashtab_compare_strings);
01617    struct inclfile *fi = 0;
01618 
01619    /* reset all the output flags, in case this isn't our first time saving this data */
01620 
01621    for (incl=cfg->includes; incl; incl = incl->next)
01622       incl->output = 0;
01623 
01624    /* go thru all the inclusions and make sure all the files involved (configfile plus all its inclusions)
01625       are all truncated to zero bytes and have that nice header*/
01626 
01627    for (incl=cfg->includes; incl; incl = incl->next)
01628    {
01629       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*/
01630          FILE *f1;
01631 
01632          set_fn(fn, sizeof(fn), incl->included_file, configfile, fileset, &fi); /* normally, fn is just set to incl->included_file, prepended with config dir if relative */
01633          f1 = fopen(fn,"w");
01634          if (f1) {
01635             gen_header(f1, configfile, fn, generator);
01636             fclose(f1); /* this should zero out the file */
01637          } else {
01638             ast_debug(1, "Unable to open for writing: %s\n", fn);
01639             ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
01640          }
01641          ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
01642          fi = 0;
01643       }
01644    }
01645 
01646    set_fn(fn, sizeof(fn), 0, configfile, fileset, &fi); /* just set fn to absolute ver of configfile */
01647 #ifdef __CYGWIN__ 
01648    if ((f = fopen(fn, "w+"))) {
01649 #else
01650    if ((f = fopen(fn, "w"))) {
01651 #endif       
01652       ast_verb(2, "Saving '%s': ", fn);
01653       gen_header(f, configfile, fn, generator);
01654       cat = cfg->root;
01655       fclose(f);
01656       ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
01657       
01658       /* from here out, we open each involved file and concat the stuff we need to add to the end and immediately close... */
01659       /* since each var, cat, and associated comments can come from any file, we have to be 
01660          mobile, and open each file, print, and close it on an entry-by-entry basis */
01661 
01662       while (cat) {
01663          set_fn(fn, sizeof(fn), cat->file, configfile, fileset, &fi);
01664          f = fopen(fn, "a");
01665          if (!f)
01666          {
01667             ast_debug(1, "Unable to open for writing: %s\n", fn);
01668             ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
01669             ao2_ref(fileset, -1);
01670             return -1;
01671          }
01672 
01673          /* dump any includes that happen before this category header */
01674          for (incl=cfg->includes; incl; incl = incl->next) {
01675             if (strcmp(incl->include_location_file, cat->file) == 0){
01676                if (cat->lineno > incl->include_location_lineno && !incl->output) {
01677                   if (incl->exec)
01678                      fprintf(f,"#exec \"%s\"\n", incl->exec_file);
01679                   else
01680                      fprintf(f,"#include \"%s\"\n", incl->included_file);
01681                   incl->output = 1;
01682                }
01683             }
01684          }
01685 
01686          insert_leading_blank_lines(f, fi, cat->precomments, cat->lineno);
01687          /* Dump section with any appropriate comment */
01688          for (cmt = cat->precomments; cmt; cmt=cmt->next) {
01689             char *cmtp = cmt->cmt;
01690             while (*cmtp == ';' && *(cmtp+1) == '!') {
01691                char *cmtp2 = strchr(cmtp+1, '\n');
01692                if (cmtp2)
01693                   cmtp = cmtp2+1;
01694                else cmtp = 0;
01695             }
01696             if (cmtp)
01697                fprintf(f,"%s", cmtp);
01698          }
01699          fprintf(f, "[%s]", cat->name);
01700          if (cat->ignored || !AST_LIST_EMPTY(&cat->template_instances)) {
01701             fprintf(f, "(");
01702             if (cat->ignored) {
01703                fprintf(f, "!");
01704             }
01705             if (cat->ignored && !AST_LIST_EMPTY(&cat->template_instances)) {
01706                fprintf(f, ",");
01707             }
01708             if (!AST_LIST_EMPTY(&cat->template_instances)) {
01709                struct ast_category_template_instance *x;
01710                AST_LIST_TRAVERSE(&cat->template_instances, x, next) {
01711                   fprintf(f,"%s",x->name);
01712                   if (x != AST_LIST_LAST(&cat->template_instances))
01713                      fprintf(f,",");
01714                }
01715             }
01716             fprintf(f, ")");
01717          }
01718          for(cmt = cat->sameline; cmt; cmt=cmt->next)
01719          {
01720             fprintf(f,"%s", cmt->cmt);
01721          }
01722          if (!cat->sameline)
01723             fprintf(f,"\n");
01724          for (cmt = cat->trailing; cmt; cmt=cmt->next) {
01725             if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
01726                fprintf(f,"%s", cmt->cmt);
01727          }
01728          fclose(f);
01729          ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
01730          fi = 0;
01731          
01732          var = cat->root;
01733          while (var) {
01734             struct ast_category_template_instance *x;
01735             int found = 0;
01736             AST_LIST_TRAVERSE(&cat->template_instances, x, next) {
01737                struct ast_variable *v;
01738                for (v = x->inst->root; v; v = v->next) {
01739                   if (!strcasecmp(var->name, v->name) && !strcmp(var->value, v->value)) {
01740                      found = 1;
01741                      break;
01742                   }
01743                }
01744                if (found)
01745                   break;
01746             }
01747             if (found) {
01748                var = var->next;
01749                continue;
01750             }
01751             set_fn(fn, sizeof(fn), var->file, configfile, fileset, &fi);
01752             f = fopen(fn, "a");
01753             if (!f)
01754             {
01755                ast_debug(1, "Unable to open for writing: %s\n", fn);
01756                ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
01757                ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
01758                fi = 0;
01759                ao2_ref(fileset, -1);
01760                return -1;
01761             }
01762             
01763             /* dump any includes that happen before this category header */
01764             for (incl=cfg->includes; incl; incl = incl->next) {
01765                if (strcmp(incl->include_location_file, var->file) == 0){
01766                   if (var->lineno > incl->include_location_lineno && !incl->output) {
01767                      if (incl->exec)
01768                         fprintf(f,"#exec \"%s\"\n", incl->exec_file);
01769                      else
01770                         fprintf(f,"#include \"%s\"\n", incl->included_file);
01771                      incl->output = 1;
01772                   }
01773                }
01774             }
01775             
01776             insert_leading_blank_lines(f, fi, var->precomments, var->lineno);
01777             for (cmt = var->precomments; cmt; cmt=cmt->next) {
01778                if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
01779                   fprintf(f,"%s", cmt->cmt);
01780             }
01781             if (var->sameline) 
01782                fprintf(f, "%s %s %s  %s", var->name, (var->object ? "=>" : "="), var->value, var->sameline->cmt);
01783             else  
01784                fprintf(f, "%s %s %s\n", var->name, (var->object ? "=>" : "="), var->value);
01785             for (cmt = var->trailing; cmt; cmt=cmt->next) {
01786                if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
01787                   fprintf(f,"%s", cmt->cmt);
01788             }
01789             if (var->blanklines) {
01790                blanklines = var->blanklines;
01791                while (blanklines--)
01792                   fprintf(f, "\n");
01793             }
01794             
01795             fclose(f);
01796             ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
01797             fi = 0;
01798             
01799             var = var->next;
01800          }
01801          cat = cat->next;
01802       }
01803       if (!option_debug)
01804          ast_verb(2, "Saved\n");
01805    } else {
01806       ast_debug(1, "Unable to open for writing: %s\n", fn);
01807       ast_verb(2, "Unable to write (%s)", strerror(errno));
01808       ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
01809       ao2_ref(fileset, -1);
01810       return -1;
01811    }
01812 
01813    /* Now, for files with trailing #include/#exec statements,
01814       we have to make sure every entry is output */
01815 
01816    for (incl=cfg->includes; incl; incl = incl->next) {
01817       if (!incl->output) {
01818          /* open the respective file */
01819          set_fn(fn, sizeof(fn), incl->include_location_file, configfile, fileset, &fi);
01820          f = fopen(fn, "a");
01821          if (!f)
01822          {
01823             ast_debug(1, "Unable to open for writing: %s\n", fn);
01824             ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
01825             ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
01826             fi = 0;
01827             ao2_ref(fileset, -1);
01828             return -1;
01829          }
01830          
01831          /* output the respective include */
01832          if (incl->exec)
01833             fprintf(f,"#exec \"%s\"\n", incl->exec_file);
01834          else
01835             fprintf(f,"#include \"%s\"\n", incl->included_file);
01836          fclose(f);
01837          incl->output = 1;
01838          ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
01839          fi = 0;
01840       }
01841    }
01842    ao2_ref(fileset, -1); /* this should destroy the hash container */
01843             
01844    return 0;
01845 }
01846 
01847 static void clear_config_maps(void) 
01848 {
01849    struct ast_config_map *map;
01850 
01851    ast_mutex_lock(&config_lock);
01852 
01853    while (config_maps) {
01854       map = config_maps;
01855       config_maps = config_maps->next;
01856       ast_free(map);
01857    }
01858       
01859    ast_mutex_unlock(&config_lock);
01860 }
01861 
01862 static int append_mapping(const char *name, const char *driver, const char *database, const char *table, int priority)
01863 {
01864    struct ast_config_map *map;
01865    int length;
01866 
01867    length = sizeof(*map);
01868    length += strlen(name) + 1;
01869    length += strlen(driver) + 1;
01870    length += strlen(database) + 1;
01871    if (table)
01872       length += strlen(table) + 1;
01873 
01874    if (!(map = ast_calloc(1, length)))
01875       return -1;
01876 
01877    map->name = map->stuff;
01878    strcpy(map->name, name);
01879    map->driver = map->name + strlen(map->name) + 1;
01880    strcpy(map->driver, driver);
01881    map->database = map->driver + strlen(map->driver) + 1;
01882    strcpy(map->database, database);
01883    if (table) {
01884       map->table = map->database + strlen(map->database) + 1;
01885       strcpy(map->table, table);
01886    }
01887    map->priority = priority;
01888    map->next = config_maps;
01889 
01890    ast_verb(2, "Binding %s to %s/%s/%s\n", map->name, map->driver, map->database, map->table ? map->table : map->name);
01891 
01892    config_maps = map;
01893    return 0;
01894 }
01895 
01896 int read_config_maps(void) 
01897 {
01898    struct ast_config *config, *configtmp;
01899    struct ast_variable *v;
01900    char *driver, *table, *database, *textpri, *stringp, *tmp;
01901    struct ast_flags flags = { CONFIG_FLAG_NOREALTIME };
01902    int pri;
01903 
01904    clear_config_maps();
01905 
01906    configtmp = ast_config_new();
01907    configtmp->max_include_level = 1;
01908    config = ast_config_internal_load(extconfig_conf, configtmp, flags, "", "extconfig");
01909    if (config == CONFIG_STATUS_FILEINVALID) {
01910       return -1;
01911    } else if (!config) {
01912       ast_config_destroy(configtmp);
01913       return 0;
01914    }
01915 
01916    for (v = ast_variable_browse(config, "settings"); v; v = v->next) {
01917       char buf[512];
01918       ast_copy_string(buf, v->value, sizeof(buf));
01919       stringp = buf;
01920       driver = strsep(&stringp, ",");
01921 
01922       if ((tmp = strchr(stringp, '\"')))
01923          stringp = tmp;
01924 
01925       /* check if the database text starts with a double quote */
01926       if (*stringp == '"') {
01927          stringp++;
01928          database = strsep(&stringp, "\"");
01929          strsep(&stringp, ",");
01930       } else {
01931          /* apparently this text has no quotes */
01932          database = strsep(&stringp, ",");
01933       }
01934 
01935       table = strsep(&stringp, ",");
01936       textpri = strsep(&stringp, ",");
01937       if (!textpri || !(pri = atoi(textpri))) {
01938          pri = 1;
01939       }
01940 
01941       if (!strcmp(v->name, extconfig_conf)) {
01942          ast_log(LOG_WARNING, "Cannot bind '%s'!\n", extconfig_conf);
01943          continue;
01944       }
01945 
01946       if (!strcmp(v->name, "asterisk.conf")) {
01947          ast_log(LOG_WARNING, "Cannot bind 'asterisk.conf'!\n");
01948          continue;
01949       }
01950 
01951       if (!strcmp(v->name, "logger.conf")) {
01952          ast_log(LOG_WARNING, "Cannot bind 'logger.conf'!\n");
01953          continue;
01954       }
01955 
01956       if (!driver || !database)
01957          continue;
01958       if (!strcasecmp(v->name, "sipfriends")) {
01959          ast_log(LOG_WARNING, "The 'sipfriends' table is obsolete, update your config to use sipusers and sippeers, though they can point to the same table.\n");
01960          append_mapping("sipusers", driver, database, table ? table : "sipfriends", pri);
01961          append_mapping("sippeers", driver, database, table ? table : "sipfriends", pri);
01962       } else if (!strcasecmp(v->name, "iaxfriends")) {
01963          ast_log(LOG_WARNING, "The 'iaxfriends' table is obsolete, update your config to use iaxusers and iaxpeers, though they can point to the same table.\n");
01964          append_mapping("iaxusers", driver, database, table ? table : "iaxfriends", pri);
01965          append_mapping("iaxpeers", driver, database, table ? table : "iaxfriends", pri);
01966       } else 
01967          append_mapping(v->name, driver, database, table, pri);
01968    }
01969       
01970    ast_config_destroy(config);
01971    return 0;
01972 }
01973 
01974 int ast_config_engine_register(struct ast_config_engine *new) 
01975 {
01976    struct ast_config_engine *ptr;
01977 
01978    ast_mutex_lock(&config_lock);
01979 
01980    if (!config_engine_list) {
01981       config_engine_list = new;
01982    } else {
01983       for (ptr = config_engine_list; ptr->next; ptr=ptr->next);
01984       ptr->next = new;
01985    }
01986 
01987    ast_mutex_unlock(&config_lock);
01988    ast_log(LOG_NOTICE,"Registered Config Engine %s\n", new->name);
01989 
01990    return 1;
01991 }
01992 
01993 int ast_config_engine_deregister(struct ast_config_engine *del) 
01994 {
01995    struct ast_config_engine *ptr, *last=NULL;
01996 
01997    ast_mutex_lock(&config_lock);
01998 
01999    for (ptr = config_engine_list; ptr; ptr=ptr->next) {
02000       if (ptr == del) {
02001          if (last)
02002             last->next = ptr->next;
02003          else
02004             config_engine_list = ptr->next;
02005          break;
02006       }
02007       last = ptr;
02008    }
02009 
02010    ast_mutex_unlock(&config_lock);
02011 
02012    return 0;
02013 }
02014 
02015 /*! \brief Find realtime engine for realtime family */
02016 static struct ast_config_engine *find_engine(const char *family, int priority, char *database, int dbsiz, char *table, int tabsiz) 
02017 {
02018    struct ast_config_engine *eng, *ret = NULL;
02019    struct ast_config_map *map;
02020 
02021    ast_mutex_lock(&config_lock);
02022 
02023    for (map = config_maps; map; map = map->next) {
02024       if (!strcasecmp(family, map->name) && (priority == map->priority)) {
02025          if (database)
02026             ast_copy_string(database, map->database, dbsiz);
02027          if (table)
02028             ast_copy_string(table, map->table ? map->table : family, tabsiz);
02029          break;
02030       }
02031    }
02032 
02033    /* Check if the required driver (engine) exist */
02034    if (map) {
02035       for (eng = config_engine_list; !ret && eng; eng = eng->next) {
02036          if (!strcasecmp(eng->name, map->driver))
02037             ret = eng;
02038       }
02039    }
02040 
02041    ast_mutex_unlock(&config_lock);
02042    
02043    /* if we found a mapping, but the engine is not available, then issue a warning */
02044    if (map && !ret)
02045       ast_log(LOG_WARNING, "Realtime mapping for '%s' found to engine '%s', but the engine is not available\n", map->name, map->driver);
02046 
02047    return ret;
02048 }
02049 
02050 static struct ast_config_engine text_file_engine = {
02051    .name = "text",
02052    .load_func = config_text_file_load,
02053 };
02054 
02055 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)
02056 {
02057    char db[256];
02058    char table[256];
02059    struct ast_config_engine *loader = &text_file_engine;
02060    struct ast_config *result; 
02061 
02062    /* The config file itself bumps include_level by 1 */
02063    if (cfg->max_include_level > 0 && cfg->include_level == cfg->max_include_level + 1) {
02064       ast_log(LOG_WARNING, "Maximum Include level (%d) exceeded\n", cfg->max_include_level);
02065       return NULL;
02066    }
02067 
02068    cfg->include_level++;
02069 
02070    if (!ast_test_flag(&flags, CONFIG_FLAG_NOREALTIME) && config_engine_list) {
02071       struct ast_config_engine *eng;
02072 
02073       eng = find_engine(filename, 1, db, sizeof(db), table, sizeof(table));
02074 
02075 
02076       if (eng && eng->load_func) {
02077          loader = eng;
02078       } else {
02079          eng = find_engine("global", 1, db, sizeof(db), table, sizeof(table));
02080          if (eng && eng->load_func)
02081             loader = eng;
02082       }
02083    }
02084 
02085    result = loader->load_func(db, table, filename, cfg, flags, suggested_include_file, who_asked);
02086 
02087    if (result && result != CONFIG_STATUS_FILEINVALID && result != CONFIG_STATUS_FILEUNCHANGED)
02088       result->include_level--;
02089    else if (result != CONFIG_STATUS_FILEINVALID)
02090       cfg->include_level--;
02091 
02092    return result;
02093 }
02094 
02095 struct ast_config *ast_config_load2(const char *filename, const char *who_asked, struct ast_flags flags)
02096 {
02097    struct ast_config *cfg;
02098    struct ast_config *result;
02099 
02100    cfg = ast_config_new();
02101    if (!cfg)
02102       return NULL;
02103 
02104    result = ast_config_internal_load(filename, cfg, flags, "", who_asked);
02105    if (!result || result == CONFIG_STATUS_FILEUNCHANGED || result == CONFIG_STATUS_FILEINVALID)
02106       ast_config_destroy(cfg);
02107 
02108    return result;
02109 }
02110 
02111 static struct ast_variable *ast_load_realtime_helper(const char *family, va_list ap)
02112 {
02113    struct ast_config_engine *eng;
02114    char db[256];
02115    char table[256];
02116    struct ast_variable *res=NULL;
02117    int i;
02118 
02119    for (i = 1; ; i++) {
02120       if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
02121          if (eng->realtime_func && (res = eng->realtime_func(db, table, ap))) {
02122             return res;
02123          }
02124       } else {
02125          return NULL;
02126       }
02127    }
02128 
02129    return res;
02130 }
02131 
02132 struct ast_variable *ast_load_realtime_all(const char *family, ...)
02133 {
02134    struct ast_variable *res;
02135    va_list ap;
02136 
02137    va_start(ap, family);
02138    res = ast_load_realtime_helper(family, ap);
02139    va_end(ap);
02140 
02141    return res;
02142 }
02143 
02144 struct ast_variable *ast_load_realtime(const char *family, ...)
02145 {
02146    struct ast_variable *res, *cur, *prev = NULL, *freeme = NULL;
02147    va_list ap;
02148 
02149    va_start(ap, family);
02150    res = ast_load_realtime_helper(family, ap);
02151    va_end(ap);
02152 
02153    /* Eliminate blank entries */
02154    for (cur = res; cur; cur = cur->next) {
02155       if (freeme) {
02156          ast_free(freeme);
02157          freeme = NULL;
02158       }
02159 
02160       if (ast_strlen_zero(cur->value)) {
02161          if (prev)
02162             prev->next = cur->next;
02163          else
02164             res = cur->next;
02165          freeme = cur;
02166       } else if (cur->value[0] == ' ' && cur->value[1] == '\0') {
02167          char *vptr = (char *) cur->value;
02168          vptr[0] = '\0';
02169          prev = cur;
02170       } else {
02171          prev = cur;
02172       }
02173    }
02174    return res;
02175 }
02176 
02177 /*! \brief Check if realtime engine is configured for family */
02178 int ast_check_realtime(const char *family)
02179 {
02180    struct ast_config_engine *eng;
02181    if (!ast_realtime_enabled()) {
02182       return 0;   /* There are no engines at all so fail early */
02183    }
02184 
02185    eng = find_engine(family, 1, NULL, 0, NULL, 0);
02186    if (eng)
02187       return 1;
02188    return 0;
02189 }
02190 
02191 /*! \brief Check if there's any realtime engines loaded */
02192 int ast_realtime_enabled()
02193 {
02194    return config_maps ? 1 : 0;
02195 }
02196 
02197 int ast_realtime_require_field(const char *family, ...)
02198 {
02199    struct ast_config_engine *eng;
02200    char db[256];
02201    char table[256];
02202    va_list ap;
02203    int res = -1, i;
02204 
02205    va_start(ap, family);
02206    for (i = 1; ; i++) {
02207       if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
02208          /* If the require succeeds, it returns 0. */
02209          if (eng->require_func && !(res = eng->require_func(db, table, ap))) {
02210             break;
02211          }
02212       } else {
02213          break;
02214       }
02215    }
02216    va_end(ap);
02217 
02218    return res;
02219 }
02220 
02221 int ast_unload_realtime(const char *family)
02222 {
02223    struct ast_config_engine *eng;
02224    char db[256];
02225    char table[256];
02226    int res = -1, i;
02227 
02228    for (i = 1; ; i++) {
02229       if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
02230          if (eng->unload_func) {
02231             /* Do this for ALL engines */
02232             res = eng->unload_func(db, table);
02233          }
02234       } else {
02235          break;
02236       }
02237    }
02238    return res;
02239 }
02240 
02241 struct ast_config *ast_load_realtime_multientry(const char *family, ...)
02242 {
02243    struct ast_config_engine *eng;
02244    char db[256];
02245    char table[256];
02246    struct ast_config *res = NULL;
02247    va_list ap;
02248    int i;
02249 
02250    va_start(ap, family);
02251    for (i = 1; ; i++) {
02252       if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
02253          if (eng->realtime_multi_func && (res = eng->realtime_multi_func(db, table, ap))) {
02254             break;
02255          }
02256       } else {
02257          break;
02258       }
02259    }
02260    va_end(ap);
02261 
02262    return res;
02263 }
02264 
02265 int ast_update_realtime(const char *family, const char *keyfield, const char *lookup, ...)
02266 {
02267    struct ast_config_engine *eng;
02268    int res = -1, i;
02269    char db[256];
02270    char table[256];
02271    va_list ap;
02272 
02273    va_start(ap, lookup);
02274    for (i = 1; ; i++) {
02275       if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
02276          /* If the update succeeds, it returns 0. */
02277          if (eng->update_func && !(res = eng->update_func(db, table, keyfield, lookup, ap))) {
02278             break;
02279          }
02280       } else {
02281          break;
02282       }
02283    }
02284    va_end(ap);
02285 
02286    return res;
02287 }
02288 
02289 int ast_update2_realtime(const char *family, ...)
02290 {
02291    struct ast_config_engine *eng;
02292    int res = -1, i;
02293    char db[256];
02294    char table[256];
02295    va_list ap;
02296 
02297    va_start(ap, family);
02298    for (i = 1; ; i++) {
02299       if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
02300          if (eng->update2_func && !(res = eng->update2_func(db, table, ap))) {
02301             break;
02302          }
02303       } else {
02304          break;
02305       }
02306    }
02307    va_end(ap);
02308 
02309    return res;
02310 }
02311 
02312 int ast_store_realtime(const char *family, ...)
02313 {
02314    struct ast_config_engine *eng;
02315    int res = -1, i;
02316    char db[256];
02317    char table[256];
02318    va_list ap;
02319 
02320    va_start(ap, family);
02321    for (i = 1; ; i++) {
02322       if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
02323          /* If the store succeeds, it returns 0. */
02324          if (eng->store_func && !(res = eng->store_func(db, table, ap))) {
02325             break;
02326          }
02327       } else {
02328          break;
02329       }
02330    }
02331    va_end(ap);
02332 
02333    return res;
02334 }
02335 
02336 int ast_destroy_realtime(const char *family, const char *keyfield, const char *lookup, ...)
02337 {
02338    struct ast_config_engine *eng;
02339    int res = -1, i;
02340    char db[256];
02341    char table[256];
02342    va_list ap;
02343 
02344    va_start(ap, lookup);
02345    for (i = 1; ; i++) {
02346       if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
02347          if (eng->destroy_func && !(res = eng->destroy_func(db, table, keyfield, lookup, ap))) {
02348             break;
02349          }
02350       } else {
02351          break;
02352       }
02353    }
02354    va_end(ap);
02355 
02356    return res;
02357 }
02358 
02359 char *ast_realtime_decode_chunk(char *chunk)
02360 {
02361    char *orig = chunk;
02362    for (; *chunk; chunk++) {
02363       if (*chunk == '^' && strchr("0123456789ABCDEFabcdef", chunk[1]) && strchr("0123456789ABCDEFabcdef", chunk[2])) {
02364          sscanf(chunk + 1, "%02hhX", chunk);
02365          memmove(chunk + 1, chunk + 3, strlen(chunk + 3) + 1);
02366       }
02367    }
02368    return orig;
02369 }
02370 
02371 char *ast_realtime_encode_chunk(struct ast_str **dest, ssize_t maxlen, const char *chunk)
02372 {
02373    if (!strchr(chunk, ';') && !strchr(chunk, '^')) {
02374       ast_str_set(dest, maxlen, "%s", chunk);
02375    } else {
02376       ast_str_reset(*dest);
02377       for (; *chunk; chunk++) {
02378          if (strchr(";^", *chunk)) {
02379             ast_str_append(dest, maxlen, "^%02hhX", *chunk);
02380          } else {
02381             ast_str_append(dest, maxlen, "%c", *chunk);
02382          }
02383       }
02384    }
02385    return ast_str_buffer(*dest);
02386 }
02387 
02388 /*! \brief Helper function to parse arguments
02389  * See documentation in config.h
02390  */
02391 int ast_parse_arg(const char *arg, enum ast_parse_flags flags,
02392    void *p_result, ...)
02393 {
02394    va_list ap;
02395    int error = 0;
02396 
02397    va_start(ap, p_result);
02398    switch (flags & PARSE_TYPE) {
02399    case PARSE_INT32:
02400        {
02401       int32_t *result = p_result;
02402       int32_t x, def = result ? *result : 0,
02403          high = (int32_t)0x7fffffff,
02404          low  = (int32_t)0x80000000;
02405       /* optional argument: first default value, then range */
02406       if (flags & PARSE_DEFAULT)
02407          def = va_arg(ap, int32_t);
02408       if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) {
02409          /* range requested, update bounds */
02410          low = va_arg(ap, int32_t);
02411          high = va_arg(ap, int32_t);
02412       }
02413       x = strtol(arg, NULL, 0);
02414       error = (x < low) || (x > high);
02415       if (flags & PARSE_OUT_RANGE)
02416          error = !error;
02417       if (result)
02418          *result  = error ? def : x;
02419       ast_debug(3,
02420          "extract int from [%s] in [%d, %d] gives [%d](%d)\n",
02421          arg, low, high,
02422          result ? *result : x, error);
02423       break;
02424        }
02425 
02426    case PARSE_UINT32:
02427        {
02428       uint32_t *result = p_result;
02429       uint32_t x, def = result ? *result : 0,
02430          low = 0, high = (uint32_t)~0;
02431       /* optional argument: first default value, then range */
02432       if (flags & PARSE_DEFAULT)
02433          def = va_arg(ap, uint32_t);
02434       if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) {
02435          /* range requested, update bounds */
02436          low = va_arg(ap, uint32_t);
02437          high = va_arg(ap, uint32_t);
02438       }
02439       x = strtoul(arg, NULL, 0);
02440       error = (x < low) || (x > high);
02441       if (flags & PARSE_OUT_RANGE)
02442          error = !error;
02443       if (result)
02444          *result  = error ? def : x;
02445       ast_debug(3,
02446          "extract uint from [%s] in [%u, %u] gives [%u](%d)\n",
02447          arg, low, high,
02448          result ? *result : x, error);
02449       break;
02450        }
02451 
02452    case PARSE_DOUBLE:
02453        {
02454       double *result = p_result;
02455       double x, def = result ? *result : 0,
02456          low = -HUGE_VAL, high = HUGE_VAL;
02457 
02458       /* optional argument: first default value, then range */
02459       if (flags & PARSE_DEFAULT)
02460          def = va_arg(ap, double);
02461       if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) {
02462          /* range requested, update bounds */
02463          low = va_arg(ap, double);
02464          high = va_arg(ap, double);
02465       }
02466       x = strtod(arg, NULL);
02467       error = (x < low) || (x > high);
02468       if (flags & PARSE_OUT_RANGE)
02469          error = !error;
02470       if (result)
02471          *result  = error ? def : x;
02472       ast_debug(3,
02473          "extract double from [%s] in [%f, %f] gives [%f](%d)\n",
02474          arg, low, high,
02475          result ? *result : x, error);
02476       break;
02477        }
02478    case PARSE_ADDR:
02479        {
02480       struct ast_sockaddr *addr = (struct ast_sockaddr *)p_result;
02481 
02482       if (!ast_sockaddr_parse(addr, arg, flags & PARSE_PORT_MASK)) {
02483          error = 1;
02484       }
02485 
02486       ast_debug(3, "extract addr from %s gives %s(%d)\n",
02487            arg, ast_sockaddr_stringify(addr), error);
02488 
02489       break;
02490        }
02491    case PARSE_INADDR:   /* TODO Remove this (use PARSE_ADDR instead). */
02492        {
02493       char *port, *buf;
02494       struct sockaddr_in _sa_buf;   /* buffer for the result */
02495       struct sockaddr_in *sa = p_result ?
02496          (struct sockaddr_in *)p_result : &_sa_buf;
02497       /* default is either the supplied value or the result itself */
02498       struct sockaddr_in *def = (flags & PARSE_DEFAULT) ?
02499          va_arg(ap, struct sockaddr_in *) : sa;
02500       struct hostent *hp;
02501       struct ast_hostent ahp;
02502 
02503       memset(&_sa_buf, '\0', sizeof(_sa_buf)); /* clear buffer */
02504       /* duplicate the string to strip away the :port */
02505       port = ast_strdupa(arg);
02506       buf = strsep(&port, ":");
02507       sa->sin_family = AF_INET;  /* assign family */
02508       /*
02509        * honor the ports flag setting, assign default value
02510        * in case of errors or field unset.
02511        */
02512       flags &= PARSE_PORT_MASK; /* the only flags left to process */
02513       if (port) {
02514          if (flags == PARSE_PORT_FORBID) {
02515             error = 1;  /* port was forbidden */
02516             sa->sin_port = def->sin_port;
02517          } else if (flags == PARSE_PORT_IGNORE)
02518             sa->sin_port = def->sin_port;
02519          else /* accept or require */
02520             sa->sin_port = htons(strtol(port, NULL, 0));
02521       } else {
02522          sa->sin_port = def->sin_port;
02523          if (flags == PARSE_PORT_REQUIRE)
02524             error = 1;
02525       }
02526       /* Now deal with host part, even if we have errors before. */
02527       hp = ast_gethostbyname(buf, &ahp);
02528       if (hp)  /* resolved successfully */
02529          memcpy(&sa->sin_addr, hp->h_addr, sizeof(sa->sin_addr));
02530       else {
02531          error = 1;
02532          sa->sin_addr = def->sin_addr;
02533       }
02534       ast_debug(3,
02535          "extract inaddr from [%s] gives [%s:%d](%d)\n",
02536          arg, ast_inet_ntoa(sa->sin_addr),
02537          ntohs(sa->sin_port), error);
02538          break;
02539        }
02540    }
02541    va_end(ap);
02542    return error;
02543 }
02544 
02545 static char *handle_cli_core_show_config_mappings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02546 {
02547    struct ast_config_engine *eng;
02548    struct ast_config_map *map;
02549 
02550    switch (cmd) {
02551    case CLI_INIT:
02552       e->command = "core show config mappings";
02553       e->usage =
02554          "Usage: core show config mappings\n"
02555          "  Shows the filenames to config engines.\n";
02556       return NULL;
02557    case CLI_GENERATE:
02558       return NULL;
02559    }
02560    
02561    ast_mutex_lock(&config_lock);
02562 
02563    if (!config_engine_list) {
02564       ast_cli(a->fd, "No config mappings found.\n");
02565    } else {
02566       for (eng = config_engine_list; eng; eng = eng->next) {
02567          ast_cli(a->fd, "Config Engine: %s\n", eng->name);
02568          for (map = config_maps; map; map = map->next) {
02569             if (!strcasecmp(map->driver, eng->name)) {
02570                ast_cli(a->fd, "===> %s (db=%s, table=%s)\n", map->name, map->database,
02571                      map->table ? map->table : map->name);
02572             }
02573          }
02574       }
02575    }
02576    
02577    ast_mutex_unlock(&config_lock);
02578 
02579    return CLI_SUCCESS;
02580 }
02581 
02582 static char *handle_cli_config_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02583 {
02584    struct cache_file_mtime *cfmtime;
02585    char *prev = "", *completion_value = NULL;
02586    int wordlen, which = 0;
02587 
02588    switch (cmd) {
02589    case CLI_INIT:
02590       e->command = "config reload";
02591       e->usage =
02592          "Usage: config reload <filename.conf>\n"
02593          "   Reloads all modules that reference <filename.conf>\n";
02594       return NULL;
02595    case CLI_GENERATE:
02596       if (a->pos > 2) {
02597          return NULL;
02598       }
02599 
02600       wordlen = strlen(a->word);
02601 
02602       AST_LIST_LOCK(&cfmtime_head);
02603       AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
02604          /* Skip duplicates - this only works because the list is sorted by filename */
02605          if (strcmp(cfmtime->filename, prev) == 0) {
02606             continue;
02607          }
02608 
02609          /* Core configs cannot be reloaded */
02610          if (ast_strlen_zero(cfmtime->who_asked)) {
02611             continue;
02612          }
02613 
02614          if (++which > a->n && strncmp(cfmtime->filename, a->word, wordlen) == 0) {
02615             completion_value = ast_strdup(cfmtime->filename);
02616             break;
02617          }
02618 
02619          /* Otherwise save that we've seen this filename */
02620          prev = cfmtime->filename;
02621       }
02622       AST_LIST_UNLOCK(&cfmtime_head);
02623 
02624       return completion_value;
02625    }
02626 
02627    if (a->argc != 3) {
02628       return CLI_SHOWUSAGE;
02629    }
02630 
02631    AST_LIST_LOCK(&cfmtime_head);
02632    AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
02633       if (!strcmp(cfmtime->filename, a->argv[2])) {
02634          char *buf = alloca(strlen("module reload ") + strlen(cfmtime->who_asked) + 1);
02635          sprintf(buf, "module reload %s", cfmtime->who_asked);
02636          ast_cli_command(a->fd, buf);
02637       }
02638    }
02639    AST_LIST_UNLOCK(&cfmtime_head);
02640 
02641    return CLI_SUCCESS;
02642 }
02643 
02644 static char *handle_cli_config_list(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02645 {
02646    struct cache_file_mtime *cfmtime;
02647 
02648    switch (cmd) {
02649    case CLI_INIT:
02650       e->command = "config list";
02651       e->usage =
02652          "Usage: config list\n"
02653          "   Show all modules that have loaded a configuration file\n";
02654       return NULL;
02655    case CLI_GENERATE:
02656       return NULL;
02657    }
02658 
02659    AST_LIST_LOCK(&cfmtime_head);
02660    AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
02661       ast_cli(a->fd, "%-20.20s %-50s\n", S_OR(cfmtime->who_asked, "core"), cfmtime->filename);
02662    }
02663    AST_LIST_UNLOCK(&cfmtime_head);
02664 
02665    return CLI_SUCCESS;
02666 }
02667 
02668 static struct ast_cli_entry cli_config[] = {
02669    AST_CLI_DEFINE(handle_cli_core_show_config_mappings, "Display config mappings (file names to config engines)"),
02670    AST_CLI_DEFINE(handle_cli_config_reload, "Force a reload on modules using a particular configuration file"),
02671    AST_CLI_DEFINE(handle_cli_config_list, "Show all files that have loaded a configuration file"),
02672 };
02673 
02674 int register_config_cli() 
02675 {
02676    ast_cli_register_multiple(cli_config, ARRAY_LEN(cli_config));
02677    return 0;
02678 }

Generated on Wed Apr 6 11:29:44 2011 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7