Fri Aug 17 00:17:15 2018

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

Generated on 17 Aug 2018 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1