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

Generated on Thu Apr 16 06:27:31 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6