Sat Jul 12 2014 17:18:31

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