Thu Apr 3 08:20:20 2014

Asterisk developer's documentation


config.c

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

Generated on 3 Apr 2014 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1