Fri Jul 24 00:40:54 2009

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

Generated on Fri Jul 24 00:40:54 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7