Sat Apr 26 2014 22:01:35

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