Wed Aug 18 22:33:50 2010

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

Generated on Wed Aug 18 22:33:51 2010 for Asterisk - the Open Source PBX by  doxygen 1.4.7