00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029 #include "asterisk.h"
00030
00031 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 161182 $")
00032
00033 #include "asterisk/paths.h"
00034 #include "asterisk/network.h"
00035 #include <time.h>
00036 #include <sys/stat.h>
00037
00038 #include <math.h>
00039
00040 #define AST_INCLUDE_GLOB 1
00041
00042 #ifdef AST_INCLUDE_GLOB
00043
00044
00045
00046 #if defined(__Darwin__) || defined(__CYGWIN__)
00047 #define GLOB_ABORTED GLOB_ABEND
00048 #endif
00049
00050 #include <glob.h>
00051
00052 #ifdef SOLARIS
00053 #define MY_GLOB_FLAGS GLOB_NOCHECK
00054 #else
00055 #define MY_GLOB_FLAGS (GLOB_NOMAGIC|GLOB_BRACE)
00056 #endif
00057
00058 #endif
00059
00060 #include "asterisk/config.h"
00061 #include "asterisk/cli.h"
00062 #include "asterisk/lock.h"
00063 #include "asterisk/utils.h"
00064 #include "asterisk/channel.h"
00065 #include "asterisk/app.h"
00066 #include "asterisk/astobj2.h"
00067 #include "asterisk/strings.h"
00068
00069 #define MAX_NESTED_COMMENTS 128
00070 #define COMMENT_START ";--"
00071 #define COMMENT_END "--;"
00072 #define COMMENT_META ';'
00073 #define COMMENT_TAG '-'
00074
00075 static char *extconfig_conf = "extconfig.conf";
00076
00077
00078
00079 struct ast_comment {
00080 struct ast_comment *next;
00081 char cmt[0];
00082 };
00083
00084
00085 struct cache_file_include {
00086 AST_LIST_ENTRY(cache_file_include) list;
00087 char include[0];
00088 };
00089
00090 struct cache_file_mtime {
00091 AST_LIST_ENTRY(cache_file_mtime) list;
00092 AST_LIST_HEAD(includes, cache_file_include) includes;
00093 unsigned int has_exec:1;
00094 time_t mtime;
00095 char *who_asked;
00096 char filename[0];
00097 };
00098
00099 static AST_LIST_HEAD_STATIC(cfmtime_head, cache_file_mtime);
00100
00101 static int init_appendbuf(void *data)
00102 {
00103 struct ast_str **str = data;
00104 *str = ast_str_create(16);
00105 return *str ? 0 : -1;
00106 }
00107
00108 AST_THREADSTORAGE_CUSTOM(appendbuf, init_appendbuf, ast_free_ptr);
00109
00110
00111 #define CB_SIZE 250
00112
00113 static void CB_ADD(struct ast_str **cb, const char *str)
00114 {
00115 ast_str_append(cb, 0, "%s", str);
00116 }
00117
00118 static void CB_ADD_LEN(struct ast_str **cb, const char *str, int len)
00119 {
00120 char *s = alloca(len + 1);
00121 ast_copy_string(s, str, len);
00122 ast_str_append(cb, 0, "%s", str);
00123 }
00124
00125 static void CB_RESET(struct ast_str *cb, struct ast_str *llb)
00126 {
00127 if (cb)
00128 cb->used = 0;
00129 if (llb)
00130 llb->used = 0;
00131 }
00132
00133 static struct ast_comment *ALLOC_COMMENT(const struct ast_str *buffer)
00134 {
00135 struct ast_comment *x = NULL;
00136 if (buffer && buffer->used)
00137 x = ast_calloc(1, sizeof(*x) + buffer->used + 1);
00138 if (x)
00139 strcpy(x->cmt, buffer->str);
00140 return x;
00141 }
00142
00143
00144
00145
00146 struct inclfile {
00147 char *fname;
00148 int lineno;
00149 };
00150
00151 static int hash_string(const void *obj, const int flags)
00152 {
00153 char *str = ((struct inclfile*)obj)->fname;
00154 int total;
00155
00156 for (total=0; *str; str++) {
00157 unsigned int tmp = total;
00158 total <<= 1;
00159 total += tmp;
00160 total <<= 2;
00161 total += tmp;
00162
00163 total += ((unsigned int)(*str));
00164 }
00165 if (total < 0)
00166 total = -total;
00167 return total;
00168 }
00169
00170 static int hashtab_compare_strings(void *a, void *b, int flags)
00171 {
00172 const struct inclfile *ae = a, *be = b;
00173 return !strcmp(ae->fname, be->fname) ? CMP_MATCH | CMP_STOP : 0;
00174 }
00175
00176 static struct ast_config_map {
00177 struct ast_config_map *next;
00178 char *name;
00179 char *driver;
00180 char *database;
00181 char *table;
00182 char stuff[0];
00183 } *config_maps = NULL;
00184
00185 AST_MUTEX_DEFINE_STATIC(config_lock);
00186 static struct ast_config_engine *config_engine_list;
00187
00188 #define MAX_INCLUDE_LEVEL 10
00189
00190 struct ast_category_template_instance {
00191 char name[80];
00192 const struct ast_category *inst;
00193 AST_LIST_ENTRY(ast_category_template_instance) next;
00194 };
00195
00196 struct ast_category {
00197 char name[80];
00198 int ignored;
00199 int include_level;
00200 char *file;
00201 int lineno;
00202 AST_LIST_HEAD_NOLOCK(template_instance_list, ast_category_template_instance) template_instances;
00203 struct ast_comment *precomments;
00204 struct ast_comment *sameline;
00205 struct ast_comment *trailing;
00206 struct ast_variable *root;
00207 struct ast_variable *last;
00208 struct ast_category *next;
00209 };
00210
00211 struct ast_config {
00212 struct ast_category *root;
00213 struct ast_category *last;
00214 struct ast_category *current;
00215 struct ast_category *last_browse;
00216 int include_level;
00217 int max_include_level;
00218 struct ast_config_include *includes;
00219 };
00220
00221 struct ast_config_include {
00222 char *include_location_file;
00223 int include_location_lineno;
00224 int exec;
00225 char *exec_file;
00226 char *included_file;
00227 int inclusion_count;
00228
00229 int output;
00230 struct ast_config_include *next;
00231 };
00232
00233 #ifdef MALLOC_DEBUG
00234 struct ast_variable *_ast_variable_new(const char *name, const char *value, const char *filename, const char *file, const char *func, int lineno)
00235 #else
00236 struct ast_variable *ast_variable_new(const char *name, const char *value, const char *filename)
00237 #endif
00238 {
00239 struct ast_variable *variable;
00240 int name_len = strlen(name) + 1;
00241 int val_len = strlen(value) + 1;
00242 int fn_len = strlen(filename) + 1;
00243
00244 #ifdef MALLOC_DEBUG
00245 if ((variable = __ast_calloc(1, name_len + val_len + fn_len + sizeof(*variable), file, lineno, func))) {
00246 #else
00247 if ((variable = ast_calloc(1, name_len + val_len + fn_len + sizeof(*variable)))) {
00248 #endif
00249 char *dst = variable->stuff;
00250 variable->name = strcpy(dst, name);
00251 dst += name_len;
00252 variable->value = strcpy(dst, value);
00253 dst += val_len;
00254 variable->file = strcpy(dst, filename);
00255 }
00256 return variable;
00257 }
00258
00259 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)
00260 {
00261
00262
00263
00264
00265 struct ast_config_include *inc;
00266 struct stat statbuf;
00267
00268 inc = ast_include_find(conf, included_file);
00269 if (inc) {
00270 do {
00271 inc->inclusion_count++;
00272 snprintf(real_included_file_name, real_included_file_name_size, "%s~~%d", included_file, inc->inclusion_count);
00273 } while (stat(real_included_file_name, &statbuf) == 0);
00274 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);
00275 } else
00276 *real_included_file_name = 0;
00277
00278 inc = ast_calloc(1,sizeof(struct ast_config_include));
00279 inc->include_location_file = ast_strdup(from_file);
00280 inc->include_location_lineno = from_lineno;
00281 if (!ast_strlen_zero(real_included_file_name))
00282 inc->included_file = ast_strdup(real_included_file_name);
00283 else
00284 inc->included_file = ast_strdup(included_file);
00285
00286 inc->exec = is_exec;
00287 if (is_exec)
00288 inc->exec_file = ast_strdup(exec_file);
00289
00290
00291 inc->next = conf->includes;
00292 conf->includes = inc;
00293
00294 return inc;
00295 }
00296
00297 void ast_include_rename(struct ast_config *conf, const char *from_file, const char *to_file)
00298 {
00299 struct ast_config_include *incl;
00300 struct ast_category *cat;
00301 struct ast_variable *v;
00302
00303 int from_len = strlen(from_file);
00304 int to_len = strlen(to_file);
00305
00306 if (strcmp(from_file, to_file) == 0)
00307 return;
00308
00309
00310
00311
00312
00313
00314
00315
00316
00317
00318 for (incl = conf->includes; incl; incl=incl->next) {
00319 if (strcmp(incl->include_location_file,from_file) == 0) {
00320 if (from_len >= to_len)
00321 strcpy(incl->include_location_file, to_file);
00322 else {
00323 free(incl->include_location_file);
00324 incl->include_location_file = strdup(to_file);
00325 }
00326 }
00327 }
00328 for (cat = conf->root; cat; cat = cat->next) {
00329 if (strcmp(cat->file,from_file) == 0) {
00330 if (from_len >= to_len)
00331 strcpy(cat->file, to_file);
00332 else {
00333 free(cat->file);
00334 cat->file = strdup(to_file);
00335 }
00336 }
00337 for (v = cat->root; v; v = v->next) {
00338 if (strcmp(v->file,from_file) == 0) {
00339 if (from_len >= to_len)
00340 strcpy(v->file, to_file);
00341 else {
00342 free(v->file);
00343 v->file = strdup(to_file);
00344 }
00345 }
00346 }
00347 }
00348 }
00349
00350 struct ast_config_include *ast_include_find(struct ast_config *conf, const char *included_file)
00351 {
00352 struct ast_config_include *x;
00353 for (x=conf->includes;x;x=x->next) {
00354 if (strcmp(x->included_file,included_file) == 0)
00355 return x;
00356 }
00357 return 0;
00358 }
00359
00360
00361 void ast_variable_append(struct ast_category *category, struct ast_variable *variable)
00362 {
00363 if (!variable)
00364 return;
00365 if (category->last)
00366 category->last->next = variable;
00367 else
00368 category->root = variable;
00369 category->last = variable;
00370 while (category->last->next)
00371 category->last = category->last->next;
00372 }
00373
00374 void ast_variable_insert(struct ast_category *category, struct ast_variable *variable, const char *line)
00375 {
00376 struct ast_variable *cur = category->root;
00377 int lineno;
00378 int insertline;
00379
00380 if (!variable || sscanf(line, "%d", &insertline) != 1)
00381 return;
00382 if (!insertline) {
00383 variable->next = category->root;
00384 category->root = variable;
00385 } else {
00386 for (lineno = 1; lineno < insertline; lineno++) {
00387 cur = cur->next;
00388 if (!cur->next)
00389 break;
00390 }
00391 variable->next = cur->next;
00392 cur->next = variable;
00393 }
00394 }
00395
00396 void ast_variables_destroy(struct ast_variable *v)
00397 {
00398 struct ast_variable *vn;
00399
00400 while (v) {
00401 vn = v;
00402 v = v->next;
00403 ast_free(vn);
00404 }
00405 }
00406
00407 struct ast_variable *ast_variable_browse(const struct ast_config *config, const char *category)
00408 {
00409 struct ast_category *cat = NULL;
00410
00411 if (category && config->last_browse && (config->last_browse->name == category))
00412 cat = config->last_browse;
00413 else
00414 cat = ast_category_get(config, category);
00415
00416 return (cat) ? cat->root : NULL;
00417 }
00418
00419 const char *ast_config_option(struct ast_config *cfg, const char *cat, const char *var)
00420 {
00421 const char *tmp;
00422 tmp = ast_variable_retrieve(cfg, cat, var);
00423 if (!tmp)
00424 tmp = ast_variable_retrieve(cfg, "general", var);
00425 return tmp;
00426 }
00427
00428
00429 const char *ast_variable_retrieve(const struct ast_config *config, const char *category, const char *variable)
00430 {
00431 struct ast_variable *v;
00432
00433 if (category) {
00434 for (v = ast_variable_browse(config, category); v; v = v->next) {
00435 if (!strcasecmp(variable, v->name))
00436 return v->value;
00437 }
00438 } else {
00439 struct ast_category *cat;
00440
00441 for (cat = config->root; cat; cat = cat->next)
00442 for (v = cat->root; v; v = v->next)
00443 if (!strcasecmp(variable, v->name))
00444 return v->value;
00445 }
00446
00447 return NULL;
00448 }
00449
00450 static struct ast_variable *variable_clone(const struct ast_variable *old)
00451 {
00452 struct ast_variable *new = ast_variable_new(old->name, old->value, old->file);
00453
00454 if (new) {
00455 new->lineno = old->lineno;
00456 new->object = old->object;
00457 new->blanklines = old->blanklines;
00458
00459 }
00460
00461 return new;
00462 }
00463
00464 static void move_variables(struct ast_category *old, struct ast_category *new)
00465 {
00466 struct ast_variable *var = old->root;
00467
00468 old->root = NULL;
00469
00470 ast_variable_append(new, var);
00471 }
00472
00473 struct ast_category *ast_category_new(const char *name, const char *in_file, int lineno)
00474 {
00475 struct ast_category *category;
00476
00477 if ((category = ast_calloc(1, sizeof(*category))))
00478 ast_copy_string(category->name, name, sizeof(category->name));
00479 category->file = strdup(in_file);
00480 category->lineno = lineno;
00481 return category;
00482 }
00483
00484 static struct ast_category *category_get(const struct ast_config *config, const char *category_name, int ignored)
00485 {
00486 struct ast_category *cat;
00487
00488
00489 for (cat = config->root; cat; cat = cat->next) {
00490 if (cat->name == category_name && (ignored || !cat->ignored))
00491 return cat;
00492 }
00493
00494 for (cat = config->root; cat; cat = cat->next) {
00495 if (!strcasecmp(cat->name, category_name) && (ignored || !cat->ignored))
00496 return cat;
00497 }
00498
00499 return NULL;
00500 }
00501
00502 struct ast_category *ast_category_get(const struct ast_config *config, const char *category_name)
00503 {
00504 return category_get(config, category_name, 0);
00505 }
00506
00507 int ast_category_exist(const struct ast_config *config, const char *category_name)
00508 {
00509 return !!ast_category_get(config, category_name);
00510 }
00511
00512 void ast_category_append(struct ast_config *config, struct ast_category *category)
00513 {
00514 if (config->last)
00515 config->last->next = category;
00516 else
00517 config->root = category;
00518 category->include_level = config->include_level;
00519 config->last = category;
00520 config->current = category;
00521 }
00522
00523 void ast_category_insert(struct ast_config *config, struct ast_category *cat, const char *match)
00524 {
00525 struct ast_category *cur_category;
00526
00527 if (!cat || !match)
00528 return;
00529 if (!strcasecmp(config->root->name, match)) {
00530 cat->next = config->root;
00531 config->root = cat;
00532 return;
00533 }
00534 for (cur_category = config->root; cur_category; cur_category = cur_category->next) {
00535 if (!strcasecmp(cur_category->next->name, match)) {
00536 cat->next = cur_category->next;
00537 cur_category->next = cat;
00538 break;
00539 }
00540 }
00541 }
00542
00543 static void ast_destroy_comments(struct ast_category *cat)
00544 {
00545 struct ast_comment *n, *p;
00546
00547 for (p=cat->precomments; p; p=n) {
00548 n = p->next;
00549 free(p);
00550 }
00551 for (p=cat->sameline; p; p=n) {
00552 n = p->next;
00553 free(p);
00554 }
00555 for (p=cat->trailing; p; p=n) {
00556 n = p->next;
00557 free(p);
00558 }
00559 cat->precomments = NULL;
00560 cat->sameline = NULL;
00561 cat->trailing = NULL;
00562 }
00563
00564 static void ast_destroy_template_list(struct ast_category *cat)
00565 {
00566 struct ast_category_template_instance *x;
00567
00568 while ((x = AST_LIST_REMOVE_HEAD(&cat->template_instances, next)))
00569 free(x);
00570 }
00571
00572 void ast_category_destroy(struct ast_category *cat)
00573 {
00574 ast_variables_destroy(cat->root);
00575 if (cat->file) {
00576 free(cat->file);
00577 cat->file = 0;
00578 }
00579 ast_destroy_comments(cat);
00580 ast_destroy_template_list(cat);
00581 ast_free(cat);
00582 }
00583
00584 static void ast_includes_destroy(struct ast_config_include *incls)
00585 {
00586 struct ast_config_include *incl,*inclnext;
00587
00588 for (incl=incls; incl; incl = inclnext) {
00589 inclnext = incl->next;
00590 if (incl->include_location_file)
00591 free(incl->include_location_file);
00592 if (incl->exec_file)
00593 free(incl->exec_file);
00594 if (incl->included_file)
00595 free(incl->included_file);
00596 free(incl);
00597 }
00598 }
00599
00600 static struct ast_category *next_available_category(struct ast_category *cat)
00601 {
00602 for (; cat && cat->ignored; cat = cat->next);
00603
00604 return cat;
00605 }
00606
00607
00608 struct ast_variable *ast_category_first(struct ast_category *cat)
00609 {
00610 return (cat) ? cat->root : NULL;
00611 }
00612
00613 struct ast_variable *ast_category_root(struct ast_config *config, char *cat)
00614 {
00615 struct ast_category *category = ast_category_get(config, cat);
00616
00617 if (category)
00618 return category->root;
00619 return NULL;
00620 }
00621
00622 char *ast_category_browse(struct ast_config *config, const char *prev)
00623 {
00624 struct ast_category *cat = NULL;
00625
00626 if (prev && config->last_browse && (config->last_browse->name == prev))
00627 cat = config->last_browse->next;
00628 else if (!prev && config->root)
00629 cat = config->root;
00630 else if (prev) {
00631 for (cat = config->root; cat; cat = cat->next) {
00632 if (cat->name == prev) {
00633 cat = cat->next;
00634 break;
00635 }
00636 }
00637 if (!cat) {
00638 for (cat = config->root; cat; cat = cat->next) {
00639 if (!strcasecmp(cat->name, prev)) {
00640 cat = cat->next;
00641 break;
00642 }
00643 }
00644 }
00645 }
00646
00647 if (cat)
00648 cat = next_available_category(cat);
00649
00650 config->last_browse = cat;
00651 return (cat) ? cat->name : NULL;
00652 }
00653
00654 struct ast_variable *ast_category_detach_variables(struct ast_category *cat)
00655 {
00656 struct ast_variable *v;
00657
00658 v = cat->root;
00659 cat->root = NULL;
00660 cat->last = NULL;
00661
00662 return v;
00663 }
00664
00665 void ast_category_rename(struct ast_category *cat, const char *name)
00666 {
00667 ast_copy_string(cat->name, name, sizeof(cat->name));
00668 }
00669
00670 static void inherit_category(struct ast_category *new, const struct ast_category *base)
00671 {
00672 struct ast_variable *var;
00673 struct ast_category_template_instance *x = ast_calloc(1,sizeof(struct ast_category_template_instance));
00674
00675 strcpy(x->name, base->name);
00676 x->inst = base;
00677 AST_LIST_INSERT_TAIL(&new->template_instances, x, next);
00678 for (var = base->root; var; var = var->next)
00679 ast_variable_append(new, variable_clone(var));
00680 }
00681
00682 struct ast_config *ast_config_new(void)
00683 {
00684 struct ast_config *config;
00685
00686 if ((config = ast_calloc(1, sizeof(*config))))
00687 config->max_include_level = MAX_INCLUDE_LEVEL;
00688 return config;
00689 }
00690
00691 int ast_variable_delete(struct ast_category *category, const char *variable, const char *match, const char *line)
00692 {
00693 struct ast_variable *cur, *prev=NULL, *curn;
00694 int res = -1;
00695 int lineno = 0;
00696
00697 cur = category->root;
00698 while (cur) {
00699 if (cur->name == variable) {
00700 if (prev) {
00701 prev->next = cur->next;
00702 if (cur == category->last)
00703 category->last = prev;
00704 } else {
00705 category->root = cur->next;
00706 if (cur == category->last)
00707 category->last = NULL;
00708 }
00709 cur->next = NULL;
00710 ast_variables_destroy(cur);
00711 return 0;
00712 }
00713 prev = cur;
00714 cur = cur->next;
00715 }
00716
00717 prev = NULL;
00718 cur = category->root;
00719 while (cur) {
00720 curn = cur->next;
00721 if ((!ast_strlen_zero(line) && lineno == atoi(line)) || (ast_strlen_zero(line) && !strcasecmp(cur->name, variable) && (ast_strlen_zero(match) || !strcasecmp(cur->value, match)))) {
00722 if (prev) {
00723 prev->next = cur->next;
00724 if (cur == category->last)
00725 category->last = prev;
00726 } else {
00727 category->root = cur->next;
00728 if (cur == category->last)
00729 category->last = NULL;
00730 }
00731 cur->next = NULL;
00732 ast_variables_destroy(cur);
00733 res = 0;
00734 } else
00735 prev = cur;
00736
00737 cur = curn;
00738 lineno++;
00739 }
00740 return res;
00741 }
00742
00743 int ast_variable_update(struct ast_category *category, const char *variable,
00744 const char *value, const char *match, unsigned int object)
00745 {
00746 struct ast_variable *cur, *prev=NULL, *newer=NULL;
00747
00748 for (cur = category->root; cur; prev = cur, cur = cur->next) {
00749 if (strcasecmp(cur->name, variable) ||
00750 (!ast_strlen_zero(match) && strcasecmp(cur->value, match)))
00751 continue;
00752
00753 if (!(newer = ast_variable_new(variable, value, cur->file)))
00754 return -1;
00755
00756 newer->next = cur->next;
00757 newer->object = cur->object || object;
00758 if (prev)
00759 prev->next = newer;
00760 else
00761 category->root = newer;
00762 if (category->last == cur)
00763 category->last = newer;
00764
00765 cur->next = NULL;
00766 ast_variables_destroy(cur);
00767
00768 return 0;
00769 }
00770
00771
00772 return -1;
00773 }
00774
00775 int ast_category_delete(struct ast_config *cfg, const char *category)
00776 {
00777 struct ast_category *prev=NULL, *cat;
00778
00779 cat = cfg->root;
00780 while (cat) {
00781 if (cat->name == category) {
00782 if (prev) {
00783 prev->next = cat->next;
00784 if (cat == cfg->last)
00785 cfg->last = prev;
00786 } else {
00787 cfg->root = cat->next;
00788 if (cat == cfg->last)
00789 cfg->last = NULL;
00790 }
00791 ast_category_destroy(cat);
00792 return 0;
00793 }
00794 prev = cat;
00795 cat = cat->next;
00796 }
00797
00798 prev = NULL;
00799 cat = cfg->root;
00800 while (cat) {
00801 if (!strcasecmp(cat->name, category)) {
00802 if (prev) {
00803 prev->next = cat->next;
00804 if (cat == cfg->last)
00805 cfg->last = prev;
00806 } else {
00807 cfg->root = cat->next;
00808 if (cat == cfg->last)
00809 cfg->last = NULL;
00810 }
00811 ast_category_destroy(cat);
00812 return 0;
00813 }
00814 prev = cat;
00815 cat = cat->next;
00816 }
00817 return -1;
00818 }
00819
00820 int ast_category_empty(struct ast_config *cfg, const char *category)
00821 {
00822 struct ast_category *cat;
00823
00824 for (cat = cfg->root; cat; cat = cat->next) {
00825 if (!strcasecmp(cat->name, category))
00826 continue;
00827 ast_variables_destroy(cat->root);
00828 cat->root = NULL;
00829 cat->last = NULL;
00830 return 0;
00831 }
00832
00833 return -1;
00834 }
00835
00836 void ast_config_destroy(struct ast_config *cfg)
00837 {
00838 struct ast_category *cat, *catn;
00839
00840 if (!cfg)
00841 return;
00842
00843 ast_includes_destroy(cfg->includes);
00844
00845 cat = cfg->root;
00846 while (cat) {
00847 catn = cat;
00848 cat = cat->next;
00849 ast_category_destroy(catn);
00850 }
00851 ast_free(cfg);
00852 }
00853
00854 struct ast_category *ast_config_get_current_category(const struct ast_config *cfg)
00855 {
00856 return cfg->current;
00857 }
00858
00859 void ast_config_set_current_category(struct ast_config *cfg, const struct ast_category *cat)
00860 {
00861
00862 cfg->current = (struct ast_category *) cat;
00863 }
00864
00865 enum config_cache_attribute_enum {
00866 ATTRIBUTE_INCLUDE = 0,
00867 ATTRIBUTE_EXEC = 1,
00868 };
00869
00870 static void config_cache_attribute(const char *configfile, enum config_cache_attribute_enum attrtype, const char *filename, const char *who_asked)
00871 {
00872 struct cache_file_mtime *cfmtime;
00873 struct cache_file_include *cfinclude;
00874 struct stat statbuf = { 0, };
00875
00876
00877 AST_LIST_LOCK(&cfmtime_head);
00878 AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
00879 if (!strcmp(cfmtime->filename, configfile) && !strcmp(cfmtime->who_asked, who_asked))
00880 break;
00881 }
00882 if (!cfmtime) {
00883 cfmtime = ast_calloc(1, sizeof(*cfmtime) + strlen(configfile) + 1 + strlen(who_asked) + 1);
00884 if (!cfmtime) {
00885 AST_LIST_UNLOCK(&cfmtime_head);
00886 return;
00887 }
00888 AST_LIST_HEAD_INIT(&cfmtime->includes);
00889 strcpy(cfmtime->filename, configfile);
00890 cfmtime->who_asked = cfmtime->filename + strlen(configfile) + 1;
00891 strcpy(cfmtime->who_asked, who_asked);
00892
00893 AST_LIST_INSERT_SORTALPHA(&cfmtime_head, cfmtime, list, filename);
00894 }
00895
00896 if (!stat(configfile, &statbuf))
00897 cfmtime->mtime = 0;
00898 else
00899 cfmtime->mtime = statbuf.st_mtime;
00900
00901 switch (attrtype) {
00902 case ATTRIBUTE_INCLUDE:
00903 AST_LIST_TRAVERSE(&cfmtime->includes, cfinclude, list) {
00904 if (!strcmp(cfinclude->include, filename)) {
00905 AST_LIST_UNLOCK(&cfmtime_head);
00906 return;
00907 }
00908 }
00909 cfinclude = ast_calloc(1, sizeof(*cfinclude) + strlen(filename) + 1);
00910 if (!cfinclude) {
00911 AST_LIST_UNLOCK(&cfmtime_head);
00912 return;
00913 }
00914 strcpy(cfinclude->include, filename);
00915 AST_LIST_INSERT_TAIL(&cfmtime->includes, cfinclude, list);
00916 break;
00917 case ATTRIBUTE_EXEC:
00918 cfmtime->has_exec = 1;
00919 break;
00920 }
00921 AST_LIST_UNLOCK(&cfmtime_head);
00922 }
00923
00924
00925
00926
00927
00928
00929
00930
00931 static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
00932 char *buf, int lineno, const char *configfile, struct ast_flags flags,
00933 struct ast_str *comment_buffer,
00934 struct ast_str *lline_buffer,
00935 const char *suggested_include_file,
00936 struct ast_category **last_cat, struct ast_variable **last_var, const char *who_asked)
00937 {
00938 char *c;
00939 char *cur = buf;
00940 struct ast_variable *v;
00941 char cmd[512], exec_file[512];
00942
00943
00944 if (cur[0] == '[') {
00945
00946
00947
00948
00949
00950
00951
00952
00953 struct ast_category *newcat = NULL;
00954 char *catname;
00955
00956 c = strchr(cur, ']');
00957 if (!c) {
00958 ast_log(LOG_WARNING, "parse error: no closing ']', line %d of %s\n", lineno, configfile);
00959 return -1;
00960 }
00961 *c++ = '\0';
00962 cur++;
00963 if (*c++ != '(')
00964 c = NULL;
00965 catname = cur;
00966 if (!(*cat = newcat = ast_category_new(catname,
00967 S_OR(suggested_include_file, cfg->include_level == 1 ? "" : configfile),
00968 lineno))) {
00969 return -1;
00970 }
00971 (*cat)->lineno = lineno;
00972 *last_var = 0;
00973 *last_cat = newcat;
00974
00975
00976 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
00977 newcat->precomments = ALLOC_COMMENT(comment_buffer);
00978 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
00979 newcat->sameline = ALLOC_COMMENT(lline_buffer);
00980 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
00981 CB_RESET(comment_buffer, lline_buffer);
00982
00983
00984 if (c) {
00985 if (!(cur = strchr(c, ')'))) {
00986 ast_log(LOG_WARNING, "parse error: no closing ')', line %d of %s\n", lineno, configfile);
00987 return -1;
00988 }
00989 *cur = '\0';
00990 while ((cur = strsep(&c, ","))) {
00991 if (!strcasecmp(cur, "!")) {
00992 (*cat)->ignored = 1;
00993 } else if (!strcasecmp(cur, "+")) {
00994 *cat = category_get(cfg, catname, 1);
00995 if (!(*cat)) {
00996 if (newcat)
00997 ast_category_destroy(newcat);
00998 ast_log(LOG_WARNING, "Category addition requested, but category '%s' does not exist, line %d of %s\n", catname, lineno, configfile);
00999 return -1;
01000 }
01001 if (newcat) {
01002 move_variables(newcat, *cat);
01003 ast_category_destroy(newcat);
01004 newcat = NULL;
01005 }
01006 } else {
01007 struct ast_category *base;
01008
01009 base = category_get(cfg, cur, 1);
01010 if (!base) {
01011 ast_log(LOG_WARNING, "Inheritance requested, but category '%s' does not exist, line %d of %s\n", cur, lineno, configfile);
01012 return -1;
01013 }
01014 inherit_category(*cat, base);
01015 }
01016 }
01017 }
01018 if (newcat)
01019 ast_category_append(cfg, *cat);
01020 } else if (cur[0] == '#') {
01021 char *cur2;
01022 char real_inclusion_name[256];
01023 struct ast_config_include *inclu;
01024 int do_include = 0;
01025
01026 cur++;
01027 c = cur;
01028 while (*c && (*c > 32)) c++;
01029 if (*c) {
01030 *c = '\0';
01031
01032 c = ast_skip_blanks(c + 1);
01033 if (!(*c))
01034 c = NULL;
01035 } else
01036 c = NULL;
01037 if (!strcasecmp(cur, "include")) {
01038 do_include = 1;
01039 } else if (!strcasecmp(cur, "exec")) {
01040 if (!ast_opt_exec_includes) {
01041 ast_log(LOG_WARNING, "Cannot perform #exec unless execincludes option is enabled in asterisk.conf (options section)!\n");
01042 return 0;
01043 }
01044 } else {
01045 ast_log(LOG_WARNING, "Unknown directive '#%s' at line %d of %s\n", cur, lineno, configfile);
01046 return 0;
01047 }
01048
01049 if (c == NULL) {
01050 ast_log(LOG_WARNING, "Directive '#%s' needs an argument (%s) at line %d of %s\n",
01051 do_include ? "include" : "exec",
01052 do_include ? "filename" : "/path/to/executable",
01053 lineno,
01054 configfile);
01055 return 0;
01056 }
01057
01058
01059 while ((*c == '<') || (*c == '>') || (*c == '\"')) c++;
01060
01061 cur = c;
01062 cur2 = cur;
01063 while (!ast_strlen_zero(cur)) {
01064 c = cur + strlen(cur) - 1;
01065 if ((*c == '>') || (*c == '<') || (*c == '\"'))
01066 *c = '\0';
01067 else
01068 break;
01069 }
01070
01071
01072 if (!do_include) {
01073 struct timeval now = ast_tvnow();
01074 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
01075 config_cache_attribute(configfile, ATTRIBUTE_EXEC, NULL, who_asked);
01076 snprintf(exec_file, sizeof(exec_file), "/var/tmp/exec.%d%d.%ld", (int)now.tv_sec, (int)now.tv_usec, (long)pthread_self());
01077 snprintf(cmd, sizeof(cmd), "%s > %s 2>&1", cur, exec_file);
01078 ast_safe_system(cmd);
01079 cur = exec_file;
01080 } else {
01081 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
01082 config_cache_attribute(configfile, ATTRIBUTE_INCLUDE, cur, who_asked);
01083 exec_file[0] = '\0';
01084 }
01085
01086
01087 inclu = ast_include_new(cfg, cfg->include_level == 1 ? "" : configfile, cur, !do_include, cur2, lineno, real_inclusion_name, sizeof(real_inclusion_name));
01088
01089 do_include = ast_config_internal_load(cur, cfg, flags, real_inclusion_name, who_asked) ? 1 : 0;
01090 if (!ast_strlen_zero(exec_file))
01091 unlink(exec_file);
01092 if (!do_include) {
01093 ast_log(LOG_ERROR, "The file '%s' was listed as a #include but it does not exist.\n", cur);
01094 return -1;
01095 }
01096
01097
01098 } else {
01099
01100 int object = 0;
01101 if (!(*cat)) {
01102 ast_log(LOG_WARNING,
01103 "parse error: No category context for line %d of %s\n", lineno, configfile);
01104 return -1;
01105 }
01106 c = strchr(cur, '=');
01107
01108 if (c && c > cur && (*(c - 1) == '+')) {
01109 struct ast_variable *var, *replace = NULL;
01110 struct ast_str **str = ast_threadstorage_get(&appendbuf, sizeof(*str));
01111
01112 if (!str || !*str) {
01113 return -1;
01114 }
01115
01116 *(c - 1) = '\0';
01117 c++;
01118 cur = ast_strip(cur);
01119
01120
01121 for (var = ast_category_first(*cat); var; var = var->next) {
01122 if (!strcmp(var->name, cur)) {
01123 replace = var;
01124 }
01125 }
01126
01127 if (!replace) {
01128
01129 goto set_new_variable;
01130 }
01131
01132 ast_str_set(str, 0, "%s", replace->value);
01133 ast_str_append(str, 0, "%s", c);
01134 ast_variable_update(*cat, replace->name, ast_strip((*str)->str), replace->value, object);
01135 } else if (c) {
01136 *c = 0;
01137 c++;
01138
01139 if (*c== '>') {
01140 object = 1;
01141 c++;
01142 }
01143 set_new_variable:
01144 if ((v = ast_variable_new(ast_strip(cur), ast_strip(c), S_OR(suggested_include_file, cfg->include_level == 1 ? "" : configfile)))) {
01145 v->lineno = lineno;
01146 v->object = object;
01147 *last_cat = 0;
01148 *last_var = v;
01149
01150 v->blanklines = 0;
01151 ast_variable_append(*cat, v);
01152
01153 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
01154 v->precomments = ALLOC_COMMENT(comment_buffer);
01155 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
01156 v->sameline = ALLOC_COMMENT(lline_buffer);
01157 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
01158 CB_RESET(comment_buffer, lline_buffer);
01159
01160 } else {
01161 return -1;
01162 }
01163 } else {
01164 ast_log(LOG_WARNING, "No '=' (equal sign) in line %d of %s\n", lineno, configfile);
01165 }
01166 }
01167 return 0;
01168 }
01169
01170 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)
01171 {
01172 char fn[256];
01173 #if defined(LOW_MEMORY)
01174 char buf[512];
01175 #else
01176 char buf[8192];
01177 #endif
01178 char *new_buf, *comment_p, *process_buf;
01179 FILE *f;
01180 int lineno=0;
01181 int comment = 0, nest[MAX_NESTED_COMMENTS];
01182 struct ast_category *cat = NULL;
01183 int count = 0;
01184 struct stat statbuf;
01185 struct cache_file_mtime *cfmtime = NULL;
01186 struct cache_file_include *cfinclude;
01187 struct ast_variable *last_var = 0;
01188 struct ast_category *last_cat = 0;
01189
01190 struct ast_str *comment_buffer = NULL;
01191 struct ast_str *lline_buffer = NULL;
01192
01193 if (cfg)
01194 cat = ast_config_get_current_category(cfg);
01195
01196 if (filename[0] == '/') {
01197 ast_copy_string(fn, filename, sizeof(fn));
01198 } else {
01199 snprintf(fn, sizeof(fn), "%s/%s", ast_config_AST_CONFIG_DIR, filename);
01200 }
01201
01202 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
01203 comment_buffer = ast_str_create(CB_SIZE);
01204 if (comment_buffer)
01205 lline_buffer = ast_str_create(CB_SIZE);
01206 if (!lline_buffer) {
01207 if (comment_buffer)
01208 ast_free(comment_buffer);
01209 ast_log(LOG_ERROR, "Failed to initialize the comment buffer!\n");
01210 return NULL;
01211 }
01212 }
01213 #ifdef AST_INCLUDE_GLOB
01214 {
01215 int glob_ret;
01216 glob_t globbuf;
01217 globbuf.gl_offs = 0;
01218 glob_ret = glob(fn, MY_GLOB_FLAGS, NULL, &globbuf);
01219 if (glob_ret == GLOB_NOSPACE)
01220 ast_log(LOG_WARNING,
01221 "Glob Expansion of pattern '%s' failed: Not enough memory\n", fn);
01222 else if (glob_ret == GLOB_ABORTED)
01223 ast_log(LOG_WARNING,
01224 "Glob Expansion of pattern '%s' failed: Read error\n", fn);
01225 else {
01226
01227 int i;
01228 for (i=0; i<globbuf.gl_pathc; i++) {
01229 ast_copy_string(fn, globbuf.gl_pathv[i], sizeof(fn));
01230 #endif
01231
01232
01233
01234
01235
01236 do {
01237 if (stat(fn, &statbuf))
01238 continue;
01239
01240 if (!S_ISREG(statbuf.st_mode)) {
01241 ast_log(LOG_WARNING, "'%s' is not a regular file, ignoring\n", fn);
01242 continue;
01243 }
01244
01245 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE)) {
01246
01247 AST_LIST_LOCK(&cfmtime_head);
01248 AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
01249 if (!strcmp(cfmtime->filename, fn) && !strcmp(cfmtime->who_asked, who_asked))
01250 break;
01251 }
01252 if (!cfmtime) {
01253 cfmtime = ast_calloc(1, sizeof(*cfmtime) + strlen(fn) + 1 + strlen(who_asked) + 1);
01254 if (!cfmtime)
01255 continue;
01256 AST_LIST_HEAD_INIT(&cfmtime->includes);
01257 strcpy(cfmtime->filename, fn);
01258 cfmtime->who_asked = cfmtime->filename + strlen(fn) + 1;
01259 strcpy(cfmtime->who_asked, who_asked);
01260
01261 AST_LIST_INSERT_SORTALPHA(&cfmtime_head, cfmtime, list, filename);
01262 }
01263 }
01264
01265 if (cfmtime && (!cfmtime->has_exec) && (cfmtime->mtime == statbuf.st_mtime) && ast_test_flag(&flags, CONFIG_FLAG_FILEUNCHANGED)) {
01266
01267 int unchanged = 1;
01268 AST_LIST_TRAVERSE(&cfmtime->includes, cfinclude, list) {
01269
01270
01271 char fn2[256];
01272 #ifdef AST_INCLUDE_GLOB
01273 int glob_return;
01274 glob_t glob_buf = { .gl_offs = 0 };
01275 glob_return = glob(cfinclude->include, MY_GLOB_FLAGS, NULL, &glob_buf);
01276
01277 if (glob_return == GLOB_NOSPACE || glob_return == GLOB_ABORTED)
01278 unchanged = 0;
01279 else {
01280
01281 int j;
01282 for (j = 0; j < glob_buf.gl_pathc; j++) {
01283 ast_copy_string(fn2, glob_buf.gl_pathv[j], sizeof(fn2));
01284 #else
01285 ast_copy_string(fn2, cfinclude->include);
01286 #endif
01287 if (config_text_file_load(NULL, NULL, fn2, NULL, flags, "", who_asked) == NULL) {
01288
01289 unchanged = 0;
01290
01291 break;
01292 }
01293 #ifdef AST_INCLUDE_GLOB
01294 }
01295 }
01296 #endif
01297 }
01298
01299 if (unchanged) {
01300 AST_LIST_UNLOCK(&cfmtime_head);
01301 return CONFIG_STATUS_FILEUNCHANGED;
01302 }
01303 }
01304 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
01305 AST_LIST_UNLOCK(&cfmtime_head);
01306
01307
01308 if (cfg == NULL)
01309 return NULL;
01310
01311 if (cfmtime)
01312 cfmtime->mtime = statbuf.st_mtime;
01313
01314 ast_verb(2, "Parsing '%s': ", fn);
01315 fflush(stdout);
01316 if (!(f = fopen(fn, "r"))) {
01317 ast_debug(1, "No file to parse: %s\n", fn);
01318 ast_verb(2, "Not found (%s)\n", strerror(errno));
01319 continue;
01320 }
01321 count++;
01322
01323 ast_clear_flag(&flags, CONFIG_FLAG_FILEUNCHANGED);
01324 ast_debug(1, "Parsing %s\n", fn);
01325 ast_verb(2, "Found\n");
01326 while (!feof(f)) {
01327 lineno++;
01328 if (fgets(buf, sizeof(buf), f)) {
01329 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && lline_buffer && lline_buffer->used) {
01330 CB_ADD(&comment_buffer, lline_buffer->str);
01331 lline_buffer->used = 0;
01332 }
01333
01334 new_buf = buf;
01335 if (comment)
01336 process_buf = NULL;
01337 else
01338 process_buf = buf;
01339
01340 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && comment_buffer->used && (ast_strlen_zero(buf) || strlen(buf) == strspn(buf," \t\n\r"))) {
01341
01342 CB_ADD(&comment_buffer, "\n");
01343 continue;
01344 }
01345
01346 while ((comment_p = strchr(new_buf, COMMENT_META))) {
01347 if ((comment_p > new_buf) && (*(comment_p-1) == '\\')) {
01348
01349 new_buf = comment_p + 1;
01350 } else if (comment_p[1] == COMMENT_TAG && comment_p[2] == COMMENT_TAG && (comment_p[3] != '-')) {
01351
01352 if (comment < MAX_NESTED_COMMENTS) {
01353 *comment_p = '\0';
01354 new_buf = comment_p + 3;
01355 comment++;
01356 nest[comment-1] = lineno;
01357 } else {
01358 ast_log(LOG_ERROR, "Maximum nest limit of %d reached.\n", MAX_NESTED_COMMENTS);
01359 }
01360 } else if ((comment_p >= new_buf + 2) &&
01361 (*(comment_p - 1) == COMMENT_TAG) &&
01362 (*(comment_p - 2) == COMMENT_TAG)) {
01363
01364 comment--;
01365 new_buf = comment_p + 1;
01366 if (!comment) {
01367
01368 if (process_buf) {
01369
01370 char *oldptr;
01371 oldptr = process_buf + strlen(process_buf);
01372 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
01373 CB_ADD(&comment_buffer, ";");
01374 CB_ADD_LEN(&comment_buffer, oldptr+1, new_buf-oldptr-1);
01375 }
01376
01377 memmove(oldptr, new_buf, strlen(new_buf) + 1);
01378 new_buf = oldptr;
01379 } else
01380 process_buf = new_buf;
01381 }
01382 } else {
01383 if (!comment) {
01384
01385
01386 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
01387 CB_ADD(&lline_buffer, comment_p);
01388 }
01389 *comment_p = '\0';
01390 new_buf = comment_p;
01391 } else
01392 new_buf = comment_p + 1;
01393 }
01394 }
01395 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment && !process_buf ) {
01396 CB_ADD(&comment_buffer, buf);
01397 }
01398
01399 if (process_buf) {
01400 char *buffer = ast_strip(process_buf);
01401 if (!ast_strlen_zero(buffer)) {
01402 if (process_text_line(cfg, &cat, buffer, lineno, fn, flags, comment_buffer, lline_buffer, suggested_include_file, &last_cat, &last_var, who_asked)) {
01403 cfg = NULL;
01404 break;
01405 }
01406 }
01407 }
01408 }
01409 }
01410
01411 if (last_cat) {
01412 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && comment_buffer->used ) {
01413 if (lline_buffer && lline_buffer->used) {
01414 CB_ADD(&comment_buffer, lline_buffer->str);
01415 lline_buffer->used = 0;
01416 }
01417 last_cat->trailing = ALLOC_COMMENT(comment_buffer);
01418 }
01419 } else if (last_var) {
01420 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && comment_buffer->used ) {
01421 if (lline_buffer && lline_buffer->used) {
01422 CB_ADD(&comment_buffer, lline_buffer->str);
01423 lline_buffer->used = 0;
01424 }
01425 last_var->trailing = ALLOC_COMMENT(comment_buffer);
01426 }
01427 } else {
01428 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && comment_buffer->used) {
01429 ast_debug(1, "Nothing to attach comments to, discarded: %s\n", comment_buffer->str);
01430 }
01431 }
01432 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
01433 CB_RESET(comment_buffer, lline_buffer);
01434
01435 fclose(f);
01436 } while (0);
01437 if (comment) {
01438 ast_log(LOG_WARNING,"Unterminated comment detected beginning on line %d\n", nest[comment - 1]);
01439 }
01440 #ifdef AST_INCLUDE_GLOB
01441 if (cfg == NULL || cfg == CONFIG_STATUS_FILEUNCHANGED)
01442 break;
01443 }
01444 globfree(&globbuf);
01445 }
01446 }
01447 #endif
01448
01449 if (cfg && cfg != CONFIG_STATUS_FILEUNCHANGED && cfg->include_level == 1 && ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
01450 if (comment_buffer)
01451 ast_free(comment_buffer);
01452 if (lline_buffer)
01453 ast_free(lline_buffer);
01454 comment_buffer = NULL;
01455 lline_buffer = NULL;
01456 }
01457
01458 if (count == 0)
01459 return NULL;
01460
01461 return cfg;
01462 }
01463
01464
01465
01466
01467
01468
01469
01470
01471
01472
01473
01474
01475
01476
01477
01478
01479
01480
01481
01482
01483
01484
01485 static void gen_header(FILE *f1, const char *configfile, const char *fn, const char *generator)
01486 {
01487 char date[256]="";
01488 time_t t;
01489
01490 time(&t);
01491 ast_copy_string(date, ctime(&t), sizeof(date));
01492
01493 fprintf(f1, ";!\n");
01494 fprintf(f1, ";! Automatically generated configuration file\n");
01495 if (strcmp(configfile, fn))
01496 fprintf(f1, ";! Filename: %s (%s)\n", configfile, fn);
01497 else
01498 fprintf(f1, ";! Filename: %s\n", configfile);
01499 fprintf(f1, ";! Generator: %s\n", generator);
01500 fprintf(f1, ";! Creation Date: %s", date);
01501 fprintf(f1, ";!\n");
01502 }
01503
01504 static void inclfile_destroy(void *obj)
01505 {
01506 const struct inclfile *o = obj;
01507
01508 if (o->fname)
01509 free(o->fname);
01510 }
01511
01512
01513 static void set_fn(char *fn, int fn_size, const char *file, const char *configfile, struct ao2_container *fileset, struct inclfile **fi)
01514 {
01515 struct inclfile lookup;
01516
01517 if (!file || file[0] == 0) {
01518 if (configfile[0] == '/')
01519 ast_copy_string(fn, configfile, fn_size);
01520 else
01521 snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, configfile);
01522 } else if (file[0] == '/')
01523 ast_copy_string(fn, file, fn_size);
01524 else
01525 snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, file);
01526 lookup.fname = fn;
01527 *fi = ao2_find(fileset, &lookup, OBJ_POINTER);
01528 if (!(*fi)) {
01529
01530 struct inclfile *fx = ao2_alloc(sizeof(struct inclfile), inclfile_destroy);
01531 fx->fname = ast_strdup(fn);
01532 fx->lineno = 1;
01533 *fi = fx;
01534 ao2_link(fileset, fx);
01535 }
01536 }
01537
01538 static int count_linefeeds(char *str)
01539 {
01540 int count = 0;
01541
01542 while (*str) {
01543 if (*str =='\n')
01544 count++;
01545 str++;
01546 }
01547 return count;
01548 }
01549
01550 static int count_linefeeds_in_comments(struct ast_comment *x)
01551 {
01552 int count = 0;
01553
01554 while (x) {
01555 count += count_linefeeds(x->cmt);
01556 x = x->next;
01557 }
01558 return count;
01559 }
01560
01561 static void insert_leading_blank_lines(FILE *fp, struct inclfile *fi, struct ast_comment *precomments, int lineno)
01562 {
01563 int precomment_lines = count_linefeeds_in_comments(precomments);
01564 int i;
01565
01566
01567
01568
01569
01570 if (lineno - precomment_lines - fi->lineno < 0) {
01571 return;
01572 }
01573 for (i=fi->lineno; i<lineno - precomment_lines; i++) {
01574 fprintf(fp,"\n");
01575 }
01576 fi->lineno = lineno+1;
01577 }
01578
01579 int config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator)
01580 {
01581 FILE *f;
01582 char fn[256];
01583 struct ast_variable *var;
01584 struct ast_category *cat;
01585 struct ast_comment *cmt;
01586 struct ast_config_include *incl;
01587 int blanklines = 0;
01588 struct ao2_container *fileset = ao2_container_alloc(180000, hash_string, hashtab_compare_strings);
01589 struct inclfile *fi = 0;
01590
01591
01592
01593 for (incl=cfg->includes; incl; incl = incl->next)
01594 incl->output = 0;
01595
01596
01597
01598
01599 for (incl=cfg->includes; incl; incl = incl->next)
01600 {
01601 if (!incl->exec) {
01602 FILE *f1;
01603
01604 set_fn(fn, sizeof(fn), incl->included_file, configfile, fileset, &fi);
01605 f1 = fopen(fn,"w");
01606 if (f1) {
01607 gen_header(f1, configfile, fn, generator);
01608 fclose(f1);
01609 } else {
01610 ast_debug(1, "Unable to open for writing: %s\n", fn);
01611 ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
01612 }
01613 ao2_ref(fi,-1);
01614 fi = 0;
01615 }
01616 }
01617
01618 set_fn(fn, sizeof(fn), 0, configfile, fileset, &fi);
01619 #ifdef __CYGWIN__
01620 if ((f = fopen(fn, "w+"))) {
01621 #else
01622 if ((f = fopen(fn, "w"))) {
01623 #endif
01624 ast_verb(2, "Saving '%s': ", fn);
01625 gen_header(f, configfile, fn, generator);
01626 cat = cfg->root;
01627 fclose(f);
01628 ao2_ref(fi,-1);
01629
01630
01631
01632
01633
01634 while (cat) {
01635 set_fn(fn, sizeof(fn), cat->file, configfile, fileset, &fi);
01636 f = fopen(fn, "a");
01637 if (!f)
01638 {
01639 ast_debug(1, "Unable to open for writing: %s\n", fn);
01640 ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
01641 ao2_ref(fileset, -1);
01642 return -1;
01643 }
01644
01645
01646 for (incl=cfg->includes; incl; incl = incl->next) {
01647 if (strcmp(incl->include_location_file, cat->file) == 0){
01648 if (cat->lineno > incl->include_location_lineno && !incl->output) {
01649 if (incl->exec)
01650 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
01651 else
01652 fprintf(f,"#include \"%s\"\n", incl->included_file);
01653 incl->output = 1;
01654 }
01655 }
01656 }
01657
01658 insert_leading_blank_lines(f, fi, cat->precomments, cat->lineno);
01659
01660 for (cmt = cat->precomments; cmt; cmt=cmt->next) {
01661 char *cmtp = cmt->cmt;
01662 while (*cmtp == ';' && *(cmtp+1) == '!') {
01663 char *cmtp2 = strchr(cmtp+1, '\n');
01664 if (cmtp2)
01665 cmtp = cmtp2+1;
01666 else cmtp = 0;
01667 }
01668 if (cmtp)
01669 fprintf(f,"%s", cmtp);
01670 }
01671 fprintf(f, "[%s]", cat->name);
01672 if (cat->ignored || !AST_LIST_EMPTY(&cat->template_instances)) {
01673 fprintf(f, "(");
01674 if (cat->ignored) {
01675 fprintf(f, "!");
01676 }
01677 if (cat->ignored && !AST_LIST_EMPTY(&cat->template_instances)) {
01678 fprintf(f, ",");
01679 }
01680 if (!AST_LIST_EMPTY(&cat->template_instances)) {
01681 struct ast_category_template_instance *x;
01682 AST_LIST_TRAVERSE(&cat->template_instances, x, next) {
01683 fprintf(f,"%s",x->name);
01684 if (x != AST_LIST_LAST(&cat->template_instances))
01685 fprintf(f,",");
01686 }
01687 }
01688 fprintf(f, ")");
01689 }
01690 for(cmt = cat->sameline; cmt; cmt=cmt->next)
01691 {
01692 fprintf(f,"%s", cmt->cmt);
01693 }
01694 if (!cat->sameline)
01695 fprintf(f,"\n");
01696 for (cmt = cat->trailing; cmt; cmt=cmt->next) {
01697 if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
01698 fprintf(f,"%s", cmt->cmt);
01699 }
01700 fclose(f);
01701 ao2_ref(fi,-1);
01702 fi = 0;
01703
01704 var = cat->root;
01705 while (var) {
01706 struct ast_category_template_instance *x;
01707 int found = 0;
01708 AST_LIST_TRAVERSE(&cat->template_instances, x, next) {
01709 struct ast_variable *v;
01710 for (v = x->inst->root; v; v = v->next) {
01711 if (!strcasecmp(var->name, v->name) && !strcmp(var->value, v->value)) {
01712 found = 1;
01713 break;
01714 }
01715 }
01716 if (found)
01717 break;
01718 }
01719 if (found) {
01720 var = var->next;
01721 continue;
01722 }
01723 set_fn(fn, sizeof(fn), var->file, configfile, fileset, &fi);
01724 f = fopen(fn, "a");
01725 if (!f)
01726 {
01727 ast_debug(1, "Unable to open for writing: %s\n", fn);
01728 ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
01729 ao2_ref(fi,-1);
01730 fi = 0;
01731 ao2_ref(fileset, -1);
01732 return -1;
01733 }
01734
01735
01736 for (incl=cfg->includes; incl; incl = incl->next) {
01737 if (strcmp(incl->include_location_file, var->file) == 0){
01738 if (var->lineno > incl->include_location_lineno && !incl->output) {
01739 if (incl->exec)
01740 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
01741 else
01742 fprintf(f,"#include \"%s\"\n", incl->included_file);
01743 incl->output = 1;
01744 }
01745 }
01746 }
01747
01748 insert_leading_blank_lines(f, fi, var->precomments, var->lineno);
01749 for (cmt = var->precomments; cmt; cmt=cmt->next) {
01750 if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
01751 fprintf(f,"%s", cmt->cmt);
01752 }
01753 if (var->sameline)
01754 fprintf(f, "%s %s %s %s", var->name, (var->object ? "=>" : "="), var->value, var->sameline->cmt);
01755 else
01756 fprintf(f, "%s %s %s\n", var->name, (var->object ? "=>" : "="), var->value);
01757 for (cmt = var->trailing; cmt; cmt=cmt->next) {
01758 if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
01759 fprintf(f,"%s", cmt->cmt);
01760 }
01761 if (var->blanklines) {
01762 blanklines = var->blanklines;
01763 while (blanklines--)
01764 fprintf(f, "\n");
01765 }
01766
01767 fclose(f);
01768 ao2_ref(fi,-1);
01769 fi = 0;
01770
01771 var = var->next;
01772 }
01773 cat = cat->next;
01774 }
01775 if (!option_debug)
01776 ast_verb(2, "Saved\n");
01777 } else {
01778 ast_debug(1, "Unable to open for writing: %s\n", fn);
01779 ast_verb(2, "Unable to write (%s)", strerror(errno));
01780 ao2_ref(fi,-1);
01781 ao2_ref(fileset, -1);
01782 return -1;
01783 }
01784
01785
01786
01787
01788 for (incl=cfg->includes; incl; incl = incl->next) {
01789 if (!incl->output) {
01790
01791 set_fn(fn, sizeof(fn), incl->include_location_file, configfile, fileset, &fi);
01792 f = fopen(fn, "a");
01793 if (!f)
01794 {
01795 ast_debug(1, "Unable to open for writing: %s\n", fn);
01796 ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
01797 ao2_ref(fi,-1);
01798 fi = 0;
01799 ao2_ref(fileset, -1);
01800 return -1;
01801 }
01802
01803
01804 if (incl->exec)
01805 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
01806 else
01807 fprintf(f,"#include \"%s\"\n", incl->included_file);
01808 fclose(f);
01809 incl->output = 1;
01810 ao2_ref(fi,-1);
01811 fi = 0;
01812 }
01813 }
01814 ao2_ref(fileset, -1);
01815
01816 return 0;
01817 }
01818
01819 static void clear_config_maps(void)
01820 {
01821 struct ast_config_map *map;
01822
01823 ast_mutex_lock(&config_lock);
01824
01825 while (config_maps) {
01826 map = config_maps;
01827 config_maps = config_maps->next;
01828 ast_free(map);
01829 }
01830
01831 ast_mutex_unlock(&config_lock);
01832 }
01833
01834 static int append_mapping(const char *name, const char *driver, const char *database, const char *table)
01835 {
01836 struct ast_config_map *map;
01837 int length;
01838
01839 length = sizeof(*map);
01840 length += strlen(name) + 1;
01841 length += strlen(driver) + 1;
01842 length += strlen(database) + 1;
01843 if (table)
01844 length += strlen(table) + 1;
01845
01846 if (!(map = ast_calloc(1, length)))
01847 return -1;
01848
01849 map->name = map->stuff;
01850 strcpy(map->name, name);
01851 map->driver = map->name + strlen(map->name) + 1;
01852 strcpy(map->driver, driver);
01853 map->database = map->driver + strlen(map->driver) + 1;
01854 strcpy(map->database, database);
01855 if (table) {
01856 map->table = map->database + strlen(map->database) + 1;
01857 strcpy(map->table, table);
01858 }
01859 map->next = config_maps;
01860
01861 ast_verb(2, "Binding %s to %s/%s/%s\n", map->name, map->driver, map->database, map->table ? map->table : map->name);
01862
01863 config_maps = map;
01864 return 0;
01865 }
01866
01867 int read_config_maps(void)
01868 {
01869 struct ast_config *config, *configtmp;
01870 struct ast_variable *v;
01871 char *driver, *table, *database, *stringp, *tmp;
01872 struct ast_flags flags = { 0 };
01873
01874 clear_config_maps();
01875
01876 configtmp = ast_config_new();
01877 configtmp->max_include_level = 1;
01878 config = ast_config_internal_load(extconfig_conf, configtmp, flags, "", "extconfig");
01879 if (!config) {
01880 ast_config_destroy(configtmp);
01881 return 0;
01882 }
01883
01884 for (v = ast_variable_browse(config, "settings"); v; v = v->next) {
01885 char buf[512];
01886 ast_copy_string(buf, v->value, sizeof(buf));
01887 stringp = buf;
01888 driver = strsep(&stringp, ",");
01889
01890 if ((tmp = strchr(stringp, '\"')))
01891 stringp = tmp;
01892
01893
01894 if (*stringp == '"') {
01895 stringp++;
01896 database = strsep(&stringp, "\"");
01897 strsep(&stringp, ",");
01898 } else {
01899
01900 database = strsep(&stringp, ",");
01901 }
01902
01903 table = strsep(&stringp, ",");
01904
01905 if (!strcmp(v->name, extconfig_conf)) {
01906 ast_log(LOG_WARNING, "Cannot bind '%s'!\n", extconfig_conf);
01907 continue;
01908 }
01909
01910 if (!strcmp(v->name, "asterisk.conf")) {
01911 ast_log(LOG_WARNING, "Cannot bind 'asterisk.conf'!\n");
01912 continue;
01913 }
01914
01915 if (!strcmp(v->name, "logger.conf")) {
01916 ast_log(LOG_WARNING, "Cannot bind 'logger.conf'!\n");
01917 continue;
01918 }
01919
01920 if (!driver || !database)
01921 continue;
01922 if (!strcasecmp(v->name, "sipfriends")) {
01923 ast_log(LOG_WARNING, "The 'sipfriends' table is obsolete, update your config to use sipusers and sippeers, though they can point to the same table.\n");
01924 append_mapping("sipusers", driver, database, table ? table : "sipfriends");
01925 append_mapping("sippeers", driver, database, table ? table : "sipfriends");
01926 } else if (!strcasecmp(v->name, "iaxfriends")) {
01927 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");
01928 append_mapping("iaxusers", driver, database, table ? table : "iaxfriends");
01929 append_mapping("iaxpeers", driver, database, table ? table : "iaxfriends");
01930 } else
01931 append_mapping(v->name, driver, database, table);
01932 }
01933
01934 ast_config_destroy(config);
01935 return 0;
01936 }
01937
01938 int ast_config_engine_register(struct ast_config_engine *new)
01939 {
01940 struct ast_config_engine *ptr;
01941
01942 ast_mutex_lock(&config_lock);
01943
01944 if (!config_engine_list) {
01945 config_engine_list = new;
01946 } else {
01947 for (ptr = config_engine_list; ptr->next; ptr=ptr->next);
01948 ptr->next = new;
01949 }
01950
01951 ast_mutex_unlock(&config_lock);
01952 ast_log(LOG_NOTICE,"Registered Config Engine %s\n", new->name);
01953
01954 return 1;
01955 }
01956
01957 int ast_config_engine_deregister(struct ast_config_engine *del)
01958 {
01959 struct ast_config_engine *ptr, *last=NULL;
01960
01961 ast_mutex_lock(&config_lock);
01962
01963 for (ptr = config_engine_list; ptr; ptr=ptr->next) {
01964 if (ptr == del) {
01965 if (last)
01966 last->next = ptr->next;
01967 else
01968 config_engine_list = ptr->next;
01969 break;
01970 }
01971 last = ptr;
01972 }
01973
01974 ast_mutex_unlock(&config_lock);
01975
01976 return 0;
01977 }
01978
01979
01980 static struct ast_config_engine *find_engine(const char *family, char *database, int dbsiz, char *table, int tabsiz)
01981 {
01982 struct ast_config_engine *eng, *ret = NULL;
01983 struct ast_config_map *map;
01984
01985 ast_mutex_lock(&config_lock);
01986
01987 for (map = config_maps; map; map = map->next) {
01988 if (!strcasecmp(family, map->name)) {
01989 if (database)
01990 ast_copy_string(database, map->database, dbsiz);
01991 if (table)
01992 ast_copy_string(table, map->table ? map->table : family, tabsiz);
01993 break;
01994 }
01995 }
01996
01997
01998 if (map) {
01999 for (eng = config_engine_list; !ret && eng; eng = eng->next) {
02000 if (!strcasecmp(eng->name, map->driver))
02001 ret = eng;
02002 }
02003 }
02004
02005 ast_mutex_unlock(&config_lock);
02006
02007
02008 if (map && !ret)
02009 ast_log(LOG_WARNING, "Realtime mapping for '%s' found to engine '%s', but the engine is not available\n", map->name, map->driver);
02010
02011 return ret;
02012 }
02013
02014 static struct ast_config_engine text_file_engine = {
02015 .name = "text",
02016 .load_func = config_text_file_load,
02017 };
02018
02019 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)
02020 {
02021 char db[256];
02022 char table[256];
02023 struct ast_config_engine *loader = &text_file_engine;
02024 struct ast_config *result;
02025
02026
02027 if (cfg->max_include_level > 0 && cfg->include_level == cfg->max_include_level + 1) {
02028 ast_log(LOG_WARNING, "Maximum Include level (%d) exceeded\n", cfg->max_include_level);
02029 return NULL;
02030 }
02031
02032 cfg->include_level++;
02033
02034 if (strcmp(filename, extconfig_conf) && strcmp(filename, "asterisk.conf") && config_engine_list) {
02035 struct ast_config_engine *eng;
02036
02037 eng = find_engine(filename, db, sizeof(db), table, sizeof(table));
02038
02039
02040 if (eng && eng->load_func) {
02041 loader = eng;
02042 } else {
02043 eng = find_engine("global", db, sizeof(db), table, sizeof(table));
02044 if (eng && eng->load_func)
02045 loader = eng;
02046 }
02047 }
02048
02049 result = loader->load_func(db, table, filename, cfg, flags, suggested_include_file, who_asked);
02050
02051 if (result && result != CONFIG_STATUS_FILEUNCHANGED)
02052 result->include_level--;
02053 else
02054 cfg->include_level--;
02055
02056 return result;
02057 }
02058
02059 struct ast_config *ast_config_load2(const char *filename, const char *who_asked, struct ast_flags flags)
02060 {
02061 struct ast_config *cfg;
02062 struct ast_config *result;
02063
02064 cfg = ast_config_new();
02065 if (!cfg)
02066 return NULL;
02067
02068 result = ast_config_internal_load(filename, cfg, flags, "", who_asked);
02069 if (!result || result == CONFIG_STATUS_FILEUNCHANGED)
02070 ast_config_destroy(cfg);
02071
02072 return result;
02073 }
02074
02075 static struct ast_variable *ast_load_realtime_helper(const char *family, va_list ap)
02076 {
02077 struct ast_config_engine *eng;
02078 char db[256]="";
02079 char table[256]="";
02080 struct ast_variable *res=NULL;
02081
02082 eng = find_engine(family, db, sizeof(db), table, sizeof(table));
02083 if (eng && eng->realtime_func)
02084 res = eng->realtime_func(db, table, ap);
02085
02086 return res;
02087 }
02088
02089 struct ast_variable *ast_load_realtime_all(const char *family, ...)
02090 {
02091 struct ast_variable *res;
02092 va_list ap;
02093
02094 va_start(ap, family);
02095 res = ast_load_realtime_helper(family, ap);
02096 va_end(ap);
02097
02098 return res;
02099 }
02100
02101 struct ast_variable *ast_load_realtime(const char *family, ...)
02102 {
02103 struct ast_variable *res, *cur, *prev = NULL, *freeme = NULL;
02104 va_list ap;
02105
02106 va_start(ap, family);
02107 res = ast_load_realtime_helper(family, ap);
02108 va_end(ap);
02109
02110
02111 for (cur = res; cur; cur = cur->next) {
02112 if (freeme) {
02113 ast_free(freeme);
02114 freeme = NULL;
02115 }
02116
02117 if (ast_strlen_zero(cur->value)) {
02118 if (prev)
02119 prev->next = cur->next;
02120 else
02121 res = cur->next;
02122 freeme = cur;
02123 } else {
02124 prev = cur;
02125 }
02126 }
02127 return res;
02128 }
02129
02130
02131 int ast_check_realtime(const char *family)
02132 {
02133 struct ast_config_engine *eng;
02134
02135 eng = find_engine(family, NULL, 0, NULL, 0);
02136 if (eng)
02137 return 1;
02138 return 0;
02139 }
02140
02141
02142 int ast_realtime_enabled()
02143 {
02144 return config_maps ? 1 : 0;
02145 }
02146
02147 int ast_realtime_require_field(const char *family, ...)
02148 {
02149 struct ast_config_engine *eng;
02150 char db[256] = "";
02151 char table[256] = "";
02152 va_list ap;
02153 int res = -1;
02154
02155 va_start(ap, family);
02156 eng = find_engine(family, db, sizeof(db), table, sizeof(table));
02157 if (eng && eng->require_func) {
02158 res = eng->require_func(db, table, ap);
02159 }
02160 va_end(ap);
02161
02162 return res;
02163 }
02164
02165 int ast_unload_realtime(const char *family)
02166 {
02167 struct ast_config_engine *eng;
02168 char db[256] = "";
02169 char table[256] = "";
02170 int res = -1;
02171
02172 eng = find_engine(family, db, sizeof(db), table, sizeof(table));
02173 if (eng && eng->unload_func) {
02174 res = eng->unload_func(db, table);
02175 }
02176 return res;
02177 }
02178
02179 struct ast_config *ast_load_realtime_multientry(const char *family, ...)
02180 {
02181 struct ast_config_engine *eng;
02182 char db[256]="";
02183 char table[256]="";
02184 struct ast_config *res=NULL;
02185 va_list ap;
02186
02187 va_start(ap, family);
02188 eng = find_engine(family, db, sizeof(db), table, sizeof(table));
02189 if (eng && eng->realtime_multi_func)
02190 res = eng->realtime_multi_func(db, table, ap);
02191 va_end(ap);
02192
02193 return res;
02194 }
02195
02196 int ast_update_realtime(const char *family, const char *keyfield, const char *lookup, ...)
02197 {
02198 struct ast_config_engine *eng;
02199 int res = -1;
02200 char db[256]="";
02201 char table[256]="";
02202 va_list ap;
02203
02204 va_start(ap, lookup);
02205 eng = find_engine(family, db, sizeof(db), table, sizeof(table));
02206 if (eng && eng->update_func)
02207 res = eng->update_func(db, table, keyfield, lookup, ap);
02208 va_end(ap);
02209
02210 return res;
02211 }
02212
02213 int ast_store_realtime(const char *family, ...)
02214 {
02215 struct ast_config_engine *eng;
02216 int res = -1;
02217 char db[256]="";
02218 char table[256]="";
02219 va_list ap;
02220
02221 va_start(ap, family);
02222 eng = find_engine(family, db, sizeof(db), table, sizeof(table));
02223 if (eng && eng->store_func)
02224 res = eng->store_func(db, table, ap);
02225 va_end(ap);
02226
02227 return res;
02228 }
02229
02230 int ast_destroy_realtime(const char *family, const char *keyfield, const char *lookup, ...)
02231 {
02232 struct ast_config_engine *eng;
02233 int res = -1;
02234 char db[256]="";
02235 char table[256]="";
02236 va_list ap;
02237
02238 va_start(ap, lookup);
02239 eng = find_engine(family, db, sizeof(db), table, sizeof(table));
02240 if (eng && eng->destroy_func)
02241 res = eng->destroy_func(db, table, keyfield, lookup, ap);
02242 va_end(ap);
02243
02244 return res;
02245 }
02246
02247
02248
02249
02250 int ast_parse_arg(const char *arg, enum ast_parse_flags flags,
02251 void *p_result, ...)
02252 {
02253 va_list ap;
02254 int error = 0;
02255
02256 va_start(ap, p_result);
02257 switch (flags & PARSE_TYPE) {
02258 case PARSE_INT32:
02259 {
02260 int32_t *result = p_result;
02261 int32_t x, def = result ? *result : 0,
02262 high = (int32_t)0x7fffffff,
02263 low = (int32_t)0x80000000;
02264
02265 if (flags & PARSE_DEFAULT)
02266 def = va_arg(ap, int32_t);
02267 if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) {
02268
02269 low = va_arg(ap, int32_t);
02270 high = va_arg(ap, int32_t);
02271 }
02272 x = strtol(arg, NULL, 0);
02273 error = (x < low) || (x > high);
02274 if (flags & PARSE_OUT_RANGE)
02275 error = !error;
02276 if (result)
02277 *result = error ? def : x;
02278 ast_debug(3,
02279 "extract int from [%s] in [%d, %d] gives [%d](%d)\n",
02280 arg, low, high,
02281 result ? *result : x, error);
02282 break;
02283 }
02284
02285 case PARSE_UINT32:
02286 {
02287 uint32_t *result = p_result;
02288 uint32_t x, def = result ? *result : 0,
02289 low = 0, high = (uint32_t)~0;
02290
02291 if (flags & PARSE_DEFAULT)
02292 def = va_arg(ap, uint32_t);
02293 if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) {
02294
02295 low = va_arg(ap, uint32_t);
02296 high = va_arg(ap, uint32_t);
02297 }
02298 x = strtoul(arg, NULL, 0);
02299 error = (x < low) || (x > high);
02300 if (flags & PARSE_OUT_RANGE)
02301 error = !error;
02302 if (result)
02303 *result = error ? def : x;
02304 ast_debug(3,
02305 "extract uint from [%s] in [%u, %u] gives [%u](%d)\n",
02306 arg, low, high,
02307 result ? *result : x, error);
02308 break;
02309 }
02310
02311 case PARSE_DOUBLE:
02312 {
02313 double *result = p_result;
02314 double x, def = result ? *result : 0,
02315 low = -HUGE_VAL, high = HUGE_VAL;
02316
02317
02318 if (flags & PARSE_DEFAULT)
02319 def = va_arg(ap, double);
02320 if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) {
02321
02322 low = va_arg(ap, double);
02323 high = va_arg(ap, double);
02324 }
02325 x = strtod(arg, NULL);
02326 error = (x < low) || (x > high);
02327 if (flags & PARSE_OUT_RANGE)
02328 error = !error;
02329 if (result)
02330 *result = error ? def : x;
02331 ast_debug(3,
02332 "extract double from [%s] in [%f, %f] gives [%f](%d)\n",
02333 arg, low, high,
02334 result ? *result : x, error);
02335 break;
02336 }
02337 case PARSE_INADDR:
02338 {
02339 char *port, *buf;
02340 struct sockaddr_in _sa_buf;
02341 struct sockaddr_in *sa = p_result ?
02342 (struct sockaddr_in *)p_result : &_sa_buf;
02343
02344 struct sockaddr_in *def = (flags & PARSE_DEFAULT) ?
02345 va_arg(ap, struct sockaddr_in *) : sa;
02346 struct hostent *hp;
02347 struct ast_hostent ahp;
02348
02349 memset(&_sa_buf, '\0', sizeof(_sa_buf));
02350
02351 port = ast_strdupa(arg);
02352 buf = strsep(&port, ":");
02353 sa->sin_family = AF_INET;
02354
02355
02356
02357
02358 flags &= PARSE_PORT_MASK;
02359 if (port) {
02360 if (flags == PARSE_PORT_FORBID) {
02361 error = 1;
02362 sa->sin_port = def->sin_port;
02363 } else if (flags == PARSE_PORT_IGNORE)
02364 sa->sin_port = def->sin_port;
02365 else
02366 sa->sin_port = htons(strtol(port, NULL, 0));
02367 } else {
02368 sa->sin_port = def->sin_port;
02369 if (flags == PARSE_PORT_REQUIRE)
02370 error = 1;
02371 }
02372
02373 hp = ast_gethostbyname(buf, &ahp);
02374 if (hp)
02375 memcpy(&sa->sin_addr, hp->h_addr, sizeof(sa->sin_addr));
02376 else {
02377 error = 1;
02378 sa->sin_addr = def->sin_addr;
02379 }
02380 ast_debug(3,
02381 "extract inaddr from [%s] gives [%s:%d](%d)\n",
02382 arg, ast_inet_ntoa(sa->sin_addr),
02383 ntohs(sa->sin_port), error);
02384 break;
02385 }
02386 }
02387 va_end(ap);
02388 return error;
02389 }
02390
02391 static char *handle_cli_core_show_config_mappings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02392 {
02393 struct ast_config_engine *eng;
02394 struct ast_config_map *map;
02395
02396 switch (cmd) {
02397 case CLI_INIT:
02398 e->command = "core show config mappings";
02399 e->usage =
02400 "Usage: core show config mappings\n"
02401 " Shows the filenames to config engines.\n";
02402 return NULL;
02403 case CLI_GENERATE:
02404 return NULL;
02405 }
02406
02407 ast_mutex_lock(&config_lock);
02408
02409 if (!config_engine_list) {
02410 ast_cli(a->fd, "No config mappings found.\n");
02411 } else {
02412 ast_cli(a->fd, "\n\n");
02413 for (eng = config_engine_list; eng; eng = eng->next) {
02414 ast_cli(a->fd, "\nConfig Engine: %s\n", eng->name);
02415 for (map = config_maps; map; map = map->next) {
02416 if (!strcasecmp(map->driver, eng->name)) {
02417 ast_cli(a->fd, "===> %s (db=%s, table=%s)\n", map->name, map->database,
02418 map->table ? map->table : map->name);
02419 }
02420 }
02421 }
02422 ast_cli(a->fd,"\n\n");
02423 }
02424
02425 ast_mutex_unlock(&config_lock);
02426
02427 return CLI_SUCCESS;
02428 }
02429
02430 static char *handle_cli_config_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02431 {
02432 struct cache_file_mtime *cfmtime;
02433 char *prev = "", *completion_value = NULL;
02434 int wordlen, which = 0;
02435
02436 switch (cmd) {
02437 case CLI_INIT:
02438 e->command = "config reload";
02439 e->usage =
02440 "Usage: config reload <filename.conf>\n"
02441 " Reloads all modules that reference <filename.conf>\n";
02442 return NULL;
02443 case CLI_GENERATE:
02444 if (a->pos > 2) {
02445 return NULL;
02446 }
02447
02448 wordlen = strlen(a->word);
02449
02450 AST_LIST_LOCK(&cfmtime_head);
02451 AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
02452
02453 if (strcmp(cfmtime->filename, prev) == 0) {
02454 continue;
02455 }
02456
02457
02458 if (ast_strlen_zero(cfmtime->who_asked)) {
02459 continue;
02460 }
02461
02462 if (++which > a->n && strncmp(cfmtime->filename, a->word, wordlen) == 0) {
02463 completion_value = ast_strdup(cfmtime->filename);
02464 break;
02465 }
02466
02467
02468 prev = cfmtime->filename;
02469 }
02470 AST_LIST_UNLOCK(&cfmtime_head);
02471
02472 return completion_value;
02473 }
02474
02475 if (a->argc != 3) {
02476 return CLI_SHOWUSAGE;
02477 }
02478
02479 AST_LIST_LOCK(&cfmtime_head);
02480 AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
02481 if (!strcmp(cfmtime->filename, a->argv[2])) {
02482 char *buf = alloca(strlen("module reload ") + strlen(cfmtime->who_asked) + 1);
02483 sprintf(buf, "module reload %s", cfmtime->who_asked);
02484 ast_cli_command(a->fd, buf);
02485 }
02486 }
02487 AST_LIST_UNLOCK(&cfmtime_head);
02488
02489 return CLI_SUCCESS;
02490 }
02491
02492 static char *handle_cli_config_list(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02493 {
02494 struct cache_file_mtime *cfmtime;
02495
02496 switch (cmd) {
02497 case CLI_INIT:
02498 e->command = "config list";
02499 e->usage =
02500 "Usage: config list\n"
02501 " Show all modules that have loaded a configuration file\n";
02502 return NULL;
02503 case CLI_GENERATE:
02504 return NULL;
02505 }
02506
02507 AST_LIST_LOCK(&cfmtime_head);
02508 AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
02509 ast_cli(a->fd, "%-20.20s %-50s\n", S_OR(cfmtime->who_asked, "core"), cfmtime->filename);
02510 }
02511 AST_LIST_UNLOCK(&cfmtime_head);
02512
02513 return CLI_SUCCESS;
02514 }
02515
02516 static struct ast_cli_entry cli_config[] = {
02517 AST_CLI_DEFINE(handle_cli_core_show_config_mappings, "Display config mappings (file names to config engines)"),
02518 AST_CLI_DEFINE(handle_cli_config_reload, "Force a reload on modules using a particular configuration file"),
02519 AST_CLI_DEFINE(handle_cli_config_list, "Show all files that have loaded a configuration file"),
02520 };
02521
02522 int register_config_cli()
02523 {
02524 ast_cli_register_multiple(cli_config, sizeof(cli_config) / sizeof(struct ast_cli_entry));
02525 return 0;
02526 }