Thu Oct 11 06:47:14 2012

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

Generated on Thu Oct 11 06:47:14 2012 for Asterisk - the Open Source PBX by  doxygen 1.5.6