![]() |
Home page |
Mailing list |
Docs
Asterisk developer's documentation :: Codename Pineapple
config.c
Go to the documentation of this file.
00001 /* 00002 * Asterisk -- An open source telephony toolkit. 00003 * 00004 * Copyright (C) 1999 - 2005, 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 doc/realtime.txt and doc/extconfig.txt 00027 */ 00028 00029 #include "asterisk.h" 00030 00031 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 51499 $") 00032 00033 #include <stdio.h> 00034 #include <unistd.h> 00035 #include <stdlib.h> 00036 #include <string.h> 00037 #include <errno.h> 00038 #include <time.h> 00039 #include <sys/stat.h> 00040 #define AST_INCLUDE_GLOB 1 00041 #ifdef AST_INCLUDE_GLOB 00042 #if defined(__Darwin__) || defined(__CYGWIN__) 00043 #define GLOB_ABORTED GLOB_ABEND 00044 #endif 00045 # include <glob.h> 00046 #endif 00047 00048 #include "asterisk/config.h" 00049 #include "asterisk/cli.h" 00050 #include "asterisk/lock.h" 00051 #include "asterisk/options.h" 00052 #include "asterisk/logger.h" 00053 #include "asterisk/utils.h" 00054 #include "asterisk/channel.h" 00055 #include "asterisk/app.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 static char *extconfig_conf = "extconfig.conf"; 00064 00065 /*! Growable string buffer */ 00066 static char *comment_buffer; /*!< this will be a comment collector.*/ 00067 static int comment_buffer_size; /*!< the amount of storage so far alloc'd for the comment_buffer */ 00068 00069 static char *lline_buffer; /*!< A buffer for stuff behind the ; */ 00070 static int lline_buffer_size; 00071 00072 00073 struct ast_comment { 00074 struct ast_comment *next; 00075 char cmt[0]; 00076 }; 00077 00078 #define CB_INCR 250 00079 00080 static void CB_INIT(void) 00081 { 00082 if (!comment_buffer) { 00083 comment_buffer = ast_malloc(CB_INCR); 00084 if (!comment_buffer) 00085 return; 00086 comment_buffer[0] = 0; 00087 comment_buffer_size = CB_INCR; 00088 lline_buffer = ast_malloc(CB_INCR); 00089 if (!lline_buffer) 00090 return; 00091 lline_buffer[0] = 0; 00092 lline_buffer_size = CB_INCR; 00093 } else { 00094 comment_buffer[0] = 0; 00095 lline_buffer[0] = 0; 00096 } 00097 } 00098 00099 static void CB_ADD(char *str) 00100 { 00101 int rem = comment_buffer_size - strlen(comment_buffer) - 1; 00102 int siz = strlen(str); 00103 if (rem < siz+1) { 00104 comment_buffer = ast_realloc(comment_buffer, comment_buffer_size + CB_INCR + siz + 1); 00105 if (!comment_buffer) 00106 return; 00107 comment_buffer_size += CB_INCR+siz+1; 00108 } 00109 strcat(comment_buffer,str); 00110 } 00111 00112 static void CB_ADD_LEN(char *str, int len) 00113 { 00114 int cbl = strlen(comment_buffer) + 1; 00115 int rem = comment_buffer_size - cbl; 00116 if (rem < len+1) { 00117 comment_buffer = ast_realloc(comment_buffer, comment_buffer_size + CB_INCR + len + 1); 00118 if (!comment_buffer) 00119 return; 00120 comment_buffer_size += CB_INCR+len+1; 00121 } 00122 strncat(comment_buffer,str,len); 00123 comment_buffer[cbl+len-1] = 0; 00124 } 00125 00126 static void LLB_ADD(char *str) 00127 { 00128 int rem = lline_buffer_size - strlen(lline_buffer) - 1; 00129 int siz = strlen(str); 00130 if (rem < siz+1) { 00131 lline_buffer = ast_realloc(lline_buffer, lline_buffer_size + CB_INCR + siz + 1); 00132 if (!lline_buffer) 00133 return; 00134 lline_buffer_size += CB_INCR + siz + 1; 00135 } 00136 strcat(lline_buffer,str); 00137 } 00138 00139 static void CB_RESET(void ) 00140 { 00141 comment_buffer[0] = 0; 00142 lline_buffer[0] = 0; 00143 } 00144 00145 00146 00147 static struct ast_comment *ALLOC_COMMENT(const char *buffer) 00148 { 00149 struct ast_comment *x = ast_calloc(1,sizeof(struct ast_comment)+strlen(buffer)+1); 00150 strcpy(x->cmt, buffer); 00151 return x; 00152 } 00153 00154 00155 static struct ast_config_map { 00156 struct ast_config_map *next; 00157 char *name; 00158 char *driver; 00159 char *database; 00160 char *table; 00161 char stuff[0]; 00162 } *config_maps = NULL; 00163 00164 AST_MUTEX_DEFINE_STATIC(config_lock); 00165 static struct ast_config_engine *config_engine_list; 00166 00167 #define MAX_INCLUDE_LEVEL 10 00168 00169 struct ast_category { 00170 char name[80]; 00171 int ignored; /*!< do not let user of the config see this category */ 00172 int include_level; 00173 struct ast_comment *precomments; 00174 struct ast_comment *sameline; 00175 struct ast_variable *root; 00176 struct ast_variable *last; 00177 struct ast_category *next; 00178 }; 00179 00180 struct ast_config { 00181 struct ast_category *root; 00182 struct ast_category *last; 00183 struct ast_category *current; 00184 struct ast_category *last_browse; /*!< used to cache the last category supplied via category_browse */ 00185 int include_level; 00186 int max_include_level; 00187 }; 00188 00189 struct ast_variable *ast_variable_new(const char *name, const char *value) 00190 { 00191 struct ast_variable *variable; 00192 int name_len = strlen(name) + 1; 00193 00194 if ((variable = ast_calloc(1, name_len + strlen(value) + 1 + sizeof(*variable)))) { 00195 variable->name = variable->stuff; 00196 variable->value = variable->stuff + name_len; 00197 strcpy(variable->name,name); 00198 strcpy(variable->value,value); 00199 } 00200 00201 return variable; 00202 } 00203 00204 void ast_variable_append(struct ast_category *category, struct ast_variable *variable) 00205 { 00206 if (!variable) 00207 return; 00208 if (category->last) 00209 category->last->next = variable; 00210 else 00211 category->root = variable; 00212 category->last = variable; 00213 while (category->last->next) 00214 category->last = category->last->next; 00215 } 00216 00217 void ast_variables_destroy(struct ast_variable *v) 00218 { 00219 struct ast_variable *vn; 00220 00221 while (v) { 00222 vn = v; 00223 v = v->next; 00224 free(vn); 00225 } 00226 } 00227 00228 struct ast_variable *ast_variable_browse(const struct ast_config *config, const char *category) 00229 { 00230 struct ast_category *cat = NULL; 00231 00232 if (category && config->last_browse && (config->last_browse->name == category)) 00233 cat = config->last_browse; 00234 else 00235 cat = ast_category_get(config, category); 00236 00237 return (cat) ? cat->root : NULL; 00238 } 00239 00240 const char *ast_config_option(struct ast_config *cfg, const char *cat, const char *var) 00241 { 00242 const char *tmp; 00243 tmp = ast_variable_retrieve(cfg, cat, var); 00244 if (!tmp) 00245 tmp = ast_variable_retrieve(cfg, "general", var); 00246 return tmp; 00247 } 00248 00249 00250 const char *ast_variable_retrieve(const struct ast_config *config, const char *category, const char *variable) 00251 { 00252 struct ast_variable *v; 00253 00254 if (category) { 00255 for (v = ast_variable_browse(config, category); v; v = v->next) { 00256 if (!strcasecmp(variable, v->name)) 00257 return v->value; 00258 } 00259 } else { 00260 struct ast_category *cat; 00261 00262 for (cat = config->root; cat; cat = cat->next) 00263 for (v = cat->root; v; v = v->next) 00264 if (!strcasecmp(variable, v->name)) 00265 return v->value; 00266 } 00267 00268 return NULL; 00269 } 00270 00271 static struct ast_variable *variable_clone(const struct ast_variable *old) 00272 { 00273 struct ast_variable *new = ast_variable_new(old->name, old->value); 00274 00275 if (new) { 00276 new->lineno = old->lineno; 00277 new->object = old->object; 00278 new->blanklines = old->blanklines; 00279 /* TODO: clone comments? */ 00280 } 00281 00282 return new; 00283 } 00284 00285 static void move_variables(struct ast_category *old, struct ast_category *new) 00286 { 00287 struct ast_variable *var = old->root; 00288 old->root = NULL; 00289 #if 1 00290 /* we can just move the entire list in a single op */ 00291 ast_variable_append(new, var); 00292 #else 00293 while (var) { 00294 struct ast_variable *next = var->next; 00295 var->next = NULL; 00296 ast_variable_append(new, var); 00297 var = next; 00298 } 00299 #endif 00300 } 00301 00302 struct ast_category *ast_category_new(const char *name) 00303 { 00304 struct ast_category *category; 00305 00306 if ((category = ast_calloc(1, sizeof(*category)))) 00307 ast_copy_string(category->name, name, sizeof(category->name)); 00308 return category; 00309 } 00310 00311 static struct ast_category *category_get(const struct ast_config *config, const char *category_name, int ignored) 00312 { 00313 struct ast_category *cat; 00314 00315 /* try exact match first, then case-insensitive match */ 00316 for (cat = config->root; cat; cat = cat->next) { 00317 if (cat->name == category_name && (ignored || !cat->ignored)) 00318 return cat; 00319 } 00320 00321 for (cat = config->root; cat; cat = cat->next) { 00322 if (!strcasecmp(cat->name, category_name) && (ignored || !cat->ignored)) 00323 return cat; 00324 } 00325 00326 return NULL; 00327 } 00328 00329 struct ast_category *ast_category_get(const struct ast_config *config, const char *category_name) 00330 { 00331 return category_get(config, category_name, 0); 00332 } 00333 00334 int ast_category_exist(const struct ast_config *config, const char *category_name) 00335 { 00336 return !!ast_category_get(config, category_name); 00337 } 00338 00339 void ast_category_append(struct ast_config *config, struct ast_category *category) 00340 { 00341 if (config->last) 00342 config->last->next = category; 00343 else 00344 config->root = category; 00345 category->include_level = config->include_level; 00346 config->last = category; 00347 config->current = category; 00348 } 00349 00350 void ast_category_destroy(struct ast_category *cat) 00351 { 00352 ast_variables_destroy(cat->root); 00353 free(cat); 00354 } 00355 00356 static struct ast_category *next_available_category(struct ast_category *cat) 00357 { 00358 for (; cat && cat->ignored; cat = cat->next); 00359 00360 return cat; 00361 } 00362 00363 char *ast_category_browse(struct ast_config *config, const char *prev) 00364 { 00365 struct ast_category *cat = NULL; 00366 00367 if (prev && config->last_browse && (config->last_browse->name == prev)) 00368 cat = config->last_browse->next; 00369 else if (!prev && config->root) 00370 cat = config->root; 00371 else if (prev) { 00372 for (cat = config->root; cat; cat = cat->next) { 00373 if (cat->name == prev) { 00374 cat = cat->next; 00375 break; 00376 } 00377 } 00378 if (!cat) { 00379 for (cat = config->root; cat; cat = cat->next) { 00380 if (!strcasecmp(cat->name, prev)) { 00381 cat = cat->next; 00382 break; 00383 } 00384 } 00385 } 00386 } 00387 00388 if (cat) 00389 cat = next_available_category(cat); 00390 00391 config->last_browse = cat; 00392 return (cat) ? cat->name : NULL; 00393 } 00394 00395 struct ast_variable *ast_category_detach_variables(struct ast_category *cat) 00396 { 00397 struct ast_variable *v; 00398 00399 v = cat->root; 00400 cat->root = NULL; 00401 cat->last = NULL; 00402 00403 return v; 00404 } 00405 00406 void ast_category_rename(struct ast_category *cat, const char *name) 00407 { 00408 ast_copy_string(cat->name, name, sizeof(cat->name)); 00409 } 00410 00411 static void inherit_category(struct ast_category *new, const struct ast_category *base) 00412 { 00413 struct ast_variable *var; 00414 00415 for (var = base->root; var; var = var->next) 00416 ast_variable_append(new, variable_clone(var)); 00417 } 00418 00419 struct ast_config *ast_config_new(void) 00420 { 00421 struct ast_config *config; 00422 00423 if ((config = ast_calloc(1, sizeof(*config)))) 00424 config->max_include_level = MAX_INCLUDE_LEVEL; 00425 return config; 00426 } 00427 00428 int ast_variable_delete(struct ast_category *category, const char *variable, const char *match) 00429 { 00430 struct ast_variable *cur, *prev=NULL, *curn; 00431 int res = -1; 00432 cur = category->root; 00433 while (cur) { 00434 if (cur->name == variable) { 00435 if (prev) { 00436 prev->next = cur->next; 00437 if (cur == category->last) 00438 category->last = prev; 00439 } else { 00440 category->root = cur->next; 00441 if (cur == category->last) 00442 category->last = NULL; 00443 } 00444 cur->next = NULL; 00445 ast_variables_destroy(cur); 00446 return 0; 00447 } 00448 prev = cur; 00449 cur = cur->next; 00450 } 00451 00452 prev = NULL; 00453 cur = category->root; 00454 while (cur) { 00455 curn = cur->next; 00456 if (!strcasecmp(cur->name, variable) && (ast_strlen_zero(match) || !strcasecmp(cur->value, match))) { 00457 if (prev) { 00458 prev->next = cur->next; 00459 if (cur == category->last) 00460 category->last = prev; 00461 } else { 00462 category->root = cur->next; 00463 if (cur == category->last) 00464 category->last = NULL; 00465 } 00466 cur->next = NULL; 00467 ast_variables_destroy(cur); 00468 res = 0; 00469 } else 00470 prev = cur; 00471 00472 cur = curn; 00473 } 00474 return res; 00475 } 00476 00477 int ast_variable_update(struct ast_category *category, const char *variable, const char *value, const char *match) 00478 { 00479 struct ast_variable *cur, *prev=NULL, *newer; 00480 newer = ast_variable_new(variable, value); 00481 if (!newer) 00482 return -1; 00483 cur = category->root; 00484 while (cur) { 00485 if (cur->name == variable) { 00486 newer->next = cur->next; 00487 newer->object = cur->object; 00488 if (prev) 00489 prev->next = newer; 00490 else 00491 category->root = newer; 00492 if (category->last == cur) 00493 category->last = newer; 00494 cur->next = NULL; 00495 ast_variables_destroy(cur); 00496 return 0; 00497 } 00498 prev = cur; 00499 cur = cur->next; 00500 } 00501 00502 prev = NULL; 00503 cur = category->root; 00504 while (cur) { 00505 if (!strcasecmp(cur->name, variable) && (ast_strlen_zero(match) || !strcasecmp(cur->value, match))) { 00506 newer->next = cur->next; 00507 newer->object = cur->object; 00508 if (prev) 00509 prev->next = newer; 00510 else 00511 category->root = newer; 00512 if (category->last == cur) 00513 category->last = newer; 00514 cur->next = NULL; 00515 ast_variables_destroy(cur); 00516 return 0; 00517 } 00518 prev = cur; 00519 cur = cur->next; 00520 } 00521 if (prev) 00522 prev->next = newer; 00523 else 00524 category->root = newer; 00525 return 0; 00526 } 00527 00528 int ast_category_delete(struct ast_config *cfg, const char *category) 00529 { 00530 struct ast_category *prev=NULL, *cat; 00531 cat = cfg->root; 00532 while (cat) { 00533 if (cat->name == category) { 00534 ast_variables_destroy(cat->root); 00535 if (prev) { 00536 prev->next = cat->next; 00537 if (cat == cfg->last) 00538 cfg->last = prev; 00539 } else { 00540 cfg->root = cat->next; 00541 if (cat == cfg->last) 00542 cfg->last = NULL; 00543 } 00544 free(cat); 00545 return 0; 00546 } 00547 prev = cat; 00548 cat = cat->next; 00549 } 00550 00551 prev = NULL; 00552 cat = cfg->root; 00553 while (cat) { 00554 if (!strcasecmp(cat->name, category)) { 00555 ast_variables_destroy(cat->root); 00556 if (prev) { 00557 prev->next = cat->next; 00558 if (cat == cfg->last) 00559 cfg->last = prev; 00560 } else { 00561 cfg->root = cat->next; 00562 if (cat == cfg->last) 00563 cfg->last = NULL; 00564 } 00565 free(cat); 00566 return 0; 00567 } 00568 prev = cat; 00569 cat = cat->next; 00570 } 00571 return -1; 00572 } 00573 00574 void ast_config_destroy(struct ast_config *cfg) 00575 { 00576 struct ast_category *cat, *catn; 00577 00578 if (!cfg) 00579 return; 00580 00581 cat = cfg->root; 00582 while (cat) { 00583 ast_variables_destroy(cat->root); 00584 catn = cat; 00585 cat = cat->next; 00586 free(catn); 00587 } 00588 free(cfg); 00589 } 00590 00591 struct ast_category *ast_config_get_current_category(const struct ast_config *cfg) 00592 { 00593 return cfg->current; 00594 } 00595 00596 void ast_config_set_current_category(struct ast_config *cfg, const struct ast_category *cat) 00597 { 00598 /* cast below is just to silence compiler warning about dropping "const" */ 00599 cfg->current = (struct ast_category *) cat; 00600 } 00601 00602 static int process_text_line(struct ast_config *cfg, struct ast_category **cat, char *buf, int lineno, const char *configfile, int withcomments) 00603 { 00604 char *c; 00605 char *cur = buf; 00606 struct ast_variable *v; 00607 char cmd[512], exec_file[512]; 00608 int object, do_exec, do_include; 00609 00610 /* Actually parse the entry */ 00611 if (cur[0] == '[') { 00612 struct ast_category *newcat = NULL; 00613 char *catname; 00614 00615 /* A category header */ 00616 c = strchr(cur, ']'); 00617 if (!c) { 00618 ast_log(LOG_WARNING, "parse error: no closing ']', line %d of %s\n", lineno, configfile); 00619 return -1; 00620 } 00621 *c++ = '\0'; 00622 cur++; 00623 if (*c++ != '(') 00624 c = NULL; 00625 catname = cur; 00626 if (!(*cat = newcat = ast_category_new(catname))) { 00627 return -1; 00628 } 00629 /* add comments */ 00630 if (withcomments && comment_buffer && comment_buffer[0] ) { 00631 newcat->precomments = ALLOC_COMMENT(comment_buffer); 00632 } 00633 if (withcomments && lline_buffer && lline_buffer[0] ) { 00634 newcat->sameline = ALLOC_COMMENT(lline_buffer); 00635 } 00636 if ( withcomments ) 00637 CB_RESET(); 00638 00639 /* If there are options or categories to inherit from, process them now */ 00640 if (c) { 00641 if (!(cur = strchr(c, ')'))) { 00642 ast_log(LOG_WARNING, "parse error: no closing ')', line %d of %s\n", lineno, configfile); 00643 return -1; 00644 } 00645 *cur = '\0'; 00646 while ((cur = strsep(&c, ","))) { 00647 if (!strcasecmp(cur, "!")) { 00648 (*cat)->ignored = 1; 00649 } else if (!strcasecmp(cur, "+")) { 00650 *cat = category_get(cfg, catname, 1); 00651 if (!*cat) { 00652 ast_config_destroy(cfg); 00653 if (newcat) 00654 ast_category_destroy(newcat); 00655 ast_log(LOG_WARNING, "Category addition requested, but category '%s' does not exist, line %d of %s\n", catname, lineno, configfile); 00656 return -1; 00657 } 00658 if (newcat) { 00659 move_variables(newcat, *cat); 00660 ast_category_destroy(newcat); 00661 newcat = NULL; 00662 } 00663 } else { 00664 struct ast_category *base; 00665 00666 base = category_get(cfg, cur, 1); 00667 if (!base) { 00668 ast_log(LOG_WARNING, "Inheritance requested, but category '%s' does not exist, line %d of %s\n", cur, lineno, configfile); 00669 return -1; 00670 } 00671 inherit_category(*cat, base); 00672 } 00673 } 00674 } 00675 if (newcat) 00676 ast_category_append(cfg, *cat); 00677 } else if (cur[0] == '#') { 00678 /* A directive */ 00679 cur++; 00680 c = cur; 00681 while (*c && (*c > 32)) c++; 00682 if (*c) { 00683 *c = '\0'; 00684 /* Find real argument */ 00685 c = ast_skip_blanks(c + 1); 00686 if (!*c) 00687 c = NULL; 00688 } else 00689 c = NULL; 00690 do_include = !strcasecmp(cur, "include"); 00691 if (!do_include) 00692 do_exec = !strcasecmp(cur, "exec"); 00693 else 00694 do_exec = 0; 00695 if (do_exec && !ast_opt_exec_includes) { 00696 ast_log(LOG_WARNING, "Cannot perform #exec unless execincludes option is enabled in asterisk.conf (options section)!\n"); 00697 do_exec = 0; 00698 } 00699 if (do_include || do_exec) { 00700 if (c) { 00701 /* Strip off leading and trailing "'s and <>'s */ 00702 while ((*c == '<') || (*c == '>') || (*c == '\"')) c++; 00703 /* Get rid of leading mess */ 00704 cur = c; 00705 while (!ast_strlen_zero(cur)) { 00706 c = cur + strlen(cur) - 1; 00707 if ((*c == '>') || (*c == '<') || (*c == '\"')) 00708 *c = '\0'; 00709 else 00710 break; 00711 } 00712 /* #exec </path/to/executable> 00713 We create a tmp file, then we #include it, then we delete it. */ 00714 if (do_exec) { 00715 snprintf(exec_file, sizeof(exec_file), "/var/tmp/exec.%d.%ld", (int)time(NULL), (long)pthread_self()); 00716 snprintf(cmd, sizeof(cmd), "%s > %s 2>&1", cur, exec_file); 00717 ast_safe_system(cmd); 00718 cur = exec_file; 00719 } else 00720 exec_file[0] = '\0'; 00721 /* A #include */ 00722 do_include = ast_config_internal_load(cur, cfg, withcomments) ? 1 : 0; 00723 if (!ast_strlen_zero(exec_file)) 00724 unlink(exec_file); 00725 if (!do_include) 00726 return 0; 00727 00728 } else { 00729 ast_log(LOG_WARNING, "Directive '#%s' needs an argument (%s) at line %d of %s\n", 00730 do_exec ? "exec" : "include", 00731 do_exec ? "/path/to/executable" : "filename", 00732 lineno, 00733 configfile); 00734 } 00735 } 00736 else 00737 ast_log(LOG_WARNING, "Unknown directive '%s' at line %d of %s\n", cur, lineno, configfile); 00738 } else { 00739 /* Just a line (variable = value) */ 00740 if (!*cat) { 00741 ast_log(LOG_WARNING, 00742 "parse error: No category context for line %d of %s\n", lineno, configfile); 00743 return -1; 00744 } 00745 c = strchr(cur, '='); 00746 if (c) { 00747 *c = 0; 00748 c++; 00749 /* Ignore > in => */ 00750 if (*c== '>') { 00751 object = 1; 00752 c++; 00753 } else 00754 object = 0; 00755 if ((v = ast_variable_new(ast_strip(cur), ast_strip(c)))) { 00756 v->lineno = lineno; 00757 v->object = object; 00758 /* Put and reset comments */ 00759 v->blanklines = 0; 00760 ast_variable_append(*cat, v); 00761 /* add comments */ 00762 if (withcomments && comment_buffer && comment_buffer[0] ) { 00763 v->precomments = ALLOC_COMMENT(comment_buffer); 00764 } 00765 if (withcomments && lline_buffer && lline_buffer[0] ) { 00766 v->sameline = ALLOC_COMMENT(lline_buffer); 00767 } 00768 if ( withcomments ) 00769 CB_RESET(); 00770 00771 } else { 00772 return -1; 00773 } 00774 } else { 00775 ast_log(LOG_WARNING, "No '=' (equal sign) in line %d of %s\n", lineno, configfile); 00776 } 00777 } 00778 return 0; 00779 } 00780 00781 static struct ast_config *config_text_file_load(const char *database, const char *table, const char *filename, struct ast_config *cfg, int withcomments) 00782 { 00783 char fn[256]; 00784 char buf[8192]; 00785 char *new_buf, *comment_p, *process_buf; 00786 FILE *f; 00787 int lineno=0; 00788 int comment = 0, nest[MAX_NESTED_COMMENTS]; 00789 struct ast_category *cat = NULL; 00790 int count = 0; 00791 struct stat statbuf; 00792 00793 cat = ast_config_get_current_category(cfg); 00794 00795 if (filename[0] == '/') { 00796 ast_copy_string(fn, filename, sizeof(fn)); 00797 } else { 00798 snprintf(fn, sizeof(fn), "%s/%s", (char *)ast_config_AST_CONFIG_DIR, filename); 00799 } 00800 00801 if (withcomments) { 00802 CB_INIT(); 00803 if (!lline_buffer || !comment_buffer) { 00804 ast_log(LOG_ERROR, "Failed to initialize the comment buffer!\n"); 00805 return NULL; 00806 } 00807 } 00808 #ifdef AST_INCLUDE_GLOB 00809 { 00810 int glob_ret; 00811 glob_t globbuf; 00812 globbuf.gl_offs = 0; /* initialize it to silence gcc */ 00813 #ifdef SOLARIS 00814 glob_ret = glob(fn, GLOB_NOCHECK, NULL, &globbuf); 00815 #else 00816 glob_ret = glob(fn, GLOB_NOMAGIC|GLOB_BRACE, NULL, &globbuf); 00817 #endif 00818 if (glob_ret == GLOB_NOSPACE) 00819 ast_log(LOG_WARNING, 00820 "Glob Expansion of pattern '%s' failed: Not enough memory\n", fn); 00821 else if (glob_ret == GLOB_ABORTED) 00822 ast_log(LOG_WARNING, 00823 "Glob Expansion of pattern '%s' failed: Read error\n", fn); 00824 else { 00825 /* loop over expanded files */ 00826 int i; 00827 for (i=0; i<globbuf.gl_pathc; i++) { 00828 ast_copy_string(fn, globbuf.gl_pathv[i], sizeof(fn)); 00829 #endif 00830 do { 00831 if (stat(fn, &statbuf)) 00832 continue; 00833 00834 if (!S_ISREG(statbuf.st_mode)) { 00835 ast_log(LOG_WARNING, "'%s' is not a regular file, ignoring\n", fn); 00836 continue; 00837 } 00838 if (option_verbose > 1) { 00839 ast_verbose(VERBOSE_PREFIX_2 "Parsing '%s': ", fn); 00840 fflush(stdout); 00841 } 00842 if (!(f = fopen(fn, "r"))) { 00843 if (option_debug) 00844 ast_log(LOG_DEBUG, "No file to parse: %s\n", fn); 00845 if (option_verbose > 1) 00846 ast_verbose( "Not found (%s)\n", strerror(errno)); 00847 continue; 00848 } 00849 count++; 00850 if (option_debug) 00851 ast_log(LOG_DEBUG, "Parsing %s\n", fn); 00852 if (option_verbose > 1) 00853 ast_verbose("Found\n"); 00854 while (!feof(f)) { 00855 lineno++; 00856 if (fgets(buf, sizeof(buf), f)) { 00857 if ( withcomments ) { 00858 CB_ADD(lline_buffer); /* add the current lline buffer to the comment buffer */ 00859 lline_buffer[0] = 0; /* erase the lline buffer */ 00860 } 00861 00862 new_buf = buf; 00863 if (comment) 00864 process_buf = NULL; 00865 else 00866 process_buf = buf; 00867 00868 while ((comment_p = strchr(new_buf, COMMENT_META))) { 00869 if ((comment_p > new_buf) && (*(comment_p-1) == '\\')) { 00870 /* Yuck, gotta memmove */ 00871 memmove(comment_p - 1, comment_p, strlen(comment_p) + 1); 00872 new_buf = comment_p; 00873 } else if (comment_p[1] == COMMENT_TAG && comment_p[2] == COMMENT_TAG && (comment_p[3] != '-')) { 00874 /* Meta-Comment start detected ";--" */ 00875 if (comment < MAX_NESTED_COMMENTS) { 00876 *comment_p = '\0'; 00877 new_buf = comment_p + 3; 00878 comment++; 00879 nest[comment-1] = lineno; 00880 } else { 00881 ast_log(LOG_ERROR, "Maximum nest limit of %d reached.\n", MAX_NESTED_COMMENTS); 00882 } 00883 } else if ((comment_p >= new_buf + 2) && 00884 (*(comment_p - 1) == COMMENT_TAG) && 00885 (*(comment_p - 2) == COMMENT_TAG)) { 00886 /* Meta-Comment end detected */ 00887 comment--; 00888 new_buf = comment_p + 1; 00889 if (!comment) { 00890 /* Back to non-comment now */ 00891 if (process_buf) { 00892 /* Actually have to move what's left over the top, then continue */ 00893 char *oldptr; 00894 oldptr = process_buf + strlen(process_buf); 00895 if ( withcomments ) { 00896 CB_ADD(";"); 00897 CB_ADD_LEN(oldptr+1,new_buf-oldptr-1); 00898 } 00899 00900 memmove(oldptr, new_buf, strlen(new_buf) + 1); 00901 new_buf = oldptr; 00902 } else 00903 process_buf = new_buf; 00904 } 00905 } else { 00906 if (!comment) { 00907 /* If ; is found, and we are not nested in a comment, 00908 we immediately stop all comment processing */ 00909 if ( withcomments ) { 00910 LLB_ADD(comment_p); 00911 } 00912 *comment_p = '\0'; 00913 new_buf = comment_p; 00914 } else 00915 new_buf = comment_p + 1; 00916 } 00917 } 00918 if ( withcomments && comment && !process_buf ) 00919 { 00920 CB_ADD(buf); /* the whole line is a comment, store it */ 00921 } 00922 00923 if (process_buf) { 00924 char *buf = ast_strip(process_buf); 00925 if (!ast_strlen_zero(buf)) { 00926 if (process_text_line(cfg, &cat, buf, lineno, filename, withcomments)) { 00927 cfg = NULL; 00928 break; 00929 } 00930 } 00931 } 00932 } 00933 } 00934 fclose(f); 00935 } while (0); 00936 if (comment) { 00937 ast_log(LOG_WARNING,"Unterminated comment detected beginning on line %d\n", nest[comment]); 00938 } 00939 #ifdef AST_INCLUDE_GLOB 00940 if (!cfg) 00941 break; 00942 } 00943 globfree(&globbuf); 00944 } 00945 } 00946 #endif 00947 00948 if (cfg && cfg->include_level == 1 && withcomments && comment_buffer) { 00949 free(comment_buffer); 00950 free(lline_buffer); 00951 comment_buffer = NULL; 00952 lline_buffer = NULL; 00953 comment_buffer_size = 0; 00954 lline_buffer_size = 0; 00955 } 00956 00957 if (count == 0) 00958 return NULL; 00959 00960 return cfg; 00961 } 00962 00963 int config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator) 00964 { 00965 FILE *f; 00966 char fn[256]; 00967 char date[256]=""; 00968 time_t t; 00969 struct ast_variable *var; 00970 struct ast_category *cat; 00971 struct ast_comment *cmt; 00972 int blanklines = 0; 00973 00974 if (configfile[0] == '/') { 00975 ast_copy_string(fn, configfile, sizeof(fn)); 00976 } else { 00977 snprintf(fn, sizeof(fn), "%s/%s", ast_config_AST_CONFIG_DIR, configfile); 00978 } 00979 time(&t); 00980 ast_copy_string(date, ctime(&t), sizeof(date)); 00981 #ifdef __CYGWIN__ 00982 if ((f = fopen(fn, "w+"))) { 00983 #else 00984 if ((f = fopen(fn, "w"))) { 00985 #endif 00986 if (option_verbose > 1) 00987 ast_verbose(VERBOSE_PREFIX_2 "Saving '%s': ", fn); 00988 fprintf(f, ";!\n"); 00989 fprintf(f, ";! Automatically generated configuration file\n"); 00990 if (strcmp(configfile, fn)) 00991 fprintf(f, ";! Filename: %s (%s)\n", configfile, fn); 00992 else 00993 fprintf(f, ";! Filename: %s\n", configfile); 00994 fprintf(f, ";! Generator: %s\n", generator); 00995 fprintf(f, ";! Creation Date: %s", date); 00996 fprintf(f, ";!\n"); 00997 cat = cfg->root; 00998 while (cat) { 00999 /* Dump section with any appropriate comment */ 01000 for (cmt = cat->precomments; cmt; cmt=cmt->next) 01001 { 01002 if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!') 01003 fprintf(f,"%s", cmt->cmt); 01004 } 01005 if (!cat->precomments) 01006 fprintf(f,"\n"); 01007 fprintf(f, "[%s]", cat->name); 01008 for (cmt = cat->sameline; cmt; cmt=cmt->next) 01009 { 01010 fprintf(f,"%s", cmt->cmt); 01011 } 01012 if (!cat->sameline) 01013 fprintf(f,"\n"); 01014 var = cat->root; 01015 while (var) { 01016 for (cmt = var->precomments; cmt; cmt=cmt->next) 01017 { 01018 if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!') 01019 fprintf(f,"%s", cmt->cmt); 01020 } 01021 if (var->sameline) 01022 fprintf(f, "%s %s %s %s", var->name, (var->object ? "=>" : "="), var->value, var->sameline->cmt); 01023 else 01024 fprintf(f, "%s %s %s\n", var->name, (var->object ? "=>" : "="), var->value); 01025 if (var->blanklines) { 01026 blanklines = var->blanklines; 01027 while (blanklines--) 01028 fprintf(f, "\n"); 01029 } 01030 01031 var = var->next; 01032 } 01033 #if 0 01034 /* Put an empty line */ 01035 fprintf(f, "\n"); 01036 #endif 01037 cat = cat->next; 01038 } 01039 if ((option_verbose > 1) && !option_debug) 01040 ast_verbose("Saved\n"); 01041 } else { 01042 if (option_debug) 01043 ast_log(LOG_DEBUG, "Unable to open for writing: %s\n", fn); 01044 if (option_verbose > 1) 01045 ast_verbose(VERBOSE_PREFIX_2 "Unable to write (%s)", strerror(errno)); 01046 return -1; 01047 } 01048 fclose(f); 01049 return 0; 01050 } 01051 01052 static void clear_config_maps(void) 01053 { 01054 struct ast_config_map *map; 01055 01056 ast_mutex_lock(&config_lock); 01057 01058 while (config_maps) { 01059 map = config_maps; 01060 config_maps = config_maps->next; 01061 free(map); 01062 } 01063 01064 ast_mutex_unlock(&config_lock); 01065 } 01066 01067 static int append_mapping(char *name, char *driver, char *database, char *table) 01068 { 01069 struct ast_config_map *map; 01070 int length; 01071 01072 length = sizeof(*map); 01073 length += strlen(name) + 1; 01074 length += strlen(driver) + 1; 01075 length += strlen(database) + 1; 01076 if (table) 01077 length += strlen(table) + 1; 01078 01079 if (!(map = ast_calloc(1, length))) 01080 return -1; 01081 01082 map->name = map->stuff; 01083 strcpy(map->name, name); 01084 map->driver = map->name + strlen(map->name) + 1; 01085 strcpy(map->driver, driver); 01086 map->database = map->driver + strlen(map->driver) + 1; 01087 strcpy(map->database, database); 01088 if (table) { 01089 map->table = map->database + strlen(map->database) + 1; 01090 strcpy(map->table, table); 01091 } 01092 map->next = config_maps; 01093 01094 if (option_verbose > 1) 01095 ast_verbose(VERBOSE_PREFIX_2 "Binding %s to %s/%s/%s\n", 01096 map->name, map->driver, map->database, map->table ? map->table : map->name); 01097 01098 config_maps = map; 01099 return 0; 01100 } 01101 01102 int read_config_maps(void) 01103 { 01104 struct ast_config *config, *configtmp; 01105 struct ast_variable *v; 01106 char *driver, *table, *database, *stringp, *tmp; 01107 01108 clear_config_maps(); 01109 01110 configtmp = ast_config_new(); 01111 configtmp->max_include_level = 1; 01112 config = ast_config_internal_load(extconfig_conf, configtmp, 0); 01113 if (!config) { 01114 ast_config_destroy(configtmp); 01115 return 0; 01116 } 01117 01118 for (v = ast_variable_browse(config, "settings"); v; v = v->next) { 01119 stringp = v->value; 01120 driver = strsep(&stringp, ","); 01121 01122 if ((tmp = strchr(stringp, '\"'))) 01123 stringp = tmp; 01124 01125 /* check if the database text starts with a double quote */ 01126 if (*stringp == '"') { 01127 stringp++; 01128 database = strsep(&stringp, "\""); 01129 strsep(&stringp, ","); 01130 } else { 01131 /* apparently this text has no quotes */ 01132 database = strsep(&stringp, ","); 01133 } 01134 01135 table = strsep(&stringp, ","); 01136 01137 if (!strcmp(v->name, extconfig_conf)) { 01138 ast_log(LOG_WARNING, "Cannot bind '%s'!\n", extconfig_conf); 01139 continue; 01140 } 01141 01142 if (!strcmp(v->name, "asterisk.conf")) { 01143 ast_log(LOG_WARNING, "Cannot bind 'asterisk.conf'!\n"); 01144 continue; 01145 } 01146 01147 if (!strcmp(v->name, "logger.conf")) { 01148 ast_log(LOG_WARNING, "Cannot bind 'logger.conf'!\n"); 01149 continue; 01150 } 01151 01152 if (!driver || !database) 01153 continue; 01154 if (!strcasecmp(v->name, "sipfriends")) { 01155 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"); 01156 append_mapping("sipusers", driver, database, table ? table : "sipfriends"); 01157 append_mapping("sippeers", driver, database, table ? table : "sipfriends"); 01158 } else if (!strcasecmp(v->name, "iaxfriends")) { 01159 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"); 01160 append_mapping("iaxusers", driver, database, table ? table : "iaxfriends"); 01161 append_mapping("iaxpeers", driver, database, table ? table : "iaxfriends"); 01162 } else 01163 append_mapping(v->name, driver, database, table); 01164 } 01165 01166 ast_config_destroy(config); 01167 return 0; 01168 } 01169 01170 int ast_config_engine_register(struct ast_config_engine *new) 01171 { 01172 struct ast_config_engine *ptr; 01173 01174 ast_mutex_lock(&config_lock); 01175 01176 if (!config_engine_list) { 01177 config_engine_list = new; 01178 } else { 01179 for (ptr = config_engine_list; ptr->next; ptr=ptr->next); 01180 ptr->next = new; 01181 } 01182 01183 ast_mutex_unlock(&config_lock); 01184 ast_log(LOG_NOTICE,"Registered Config Engine %s\n", new->name); 01185 01186 return 1; 01187 } 01188 01189 int ast_config_engine_deregister(struct ast_config_engine *del) 01190 { 01191 struct ast_config_engine *ptr, *last=NULL; 01192 01193 ast_mutex_lock(&config_lock); 01194 01195 for (ptr = config_engine_list; ptr; ptr=ptr->next) { 01196 if (ptr == del) { 01197 if (last) 01198 last->next = ptr->next; 01199 else 01200 config_engine_list = ptr->next; 01201 break; 01202 } 01203 last = ptr; 01204 } 01205 01206 ast_mutex_unlock(&config_lock); 01207 01208 return 0; 01209 } 01210 01211 /*! \brief Find realtime engine for realtime family */ 01212 static struct ast_config_engine *find_engine(const char *family, char *database, int dbsiz, char *table, int tabsiz) 01213 { 01214 struct ast_config_engine *eng, *ret = NULL; 01215 struct ast_config_map *map; 01216 01217 ast_mutex_lock(&config_lock); 01218 01219 for (map = config_maps; map; map = map->next) { 01220 if (!strcasecmp(family, map->name)) { 01221 if (database) 01222 ast_copy_string(database, map->database, dbsiz); 01223 if (table) 01224 ast_copy_string(table, map->table ? map->table : family, tabsiz); 01225 break; 01226 } 01227 } 01228 01229 /* Check if the required driver (engine) exist */ 01230 if (map) { 01231 for (eng = config_engine_list; !ret && eng; eng = eng->next) { 01232 if (!strcasecmp(eng->name, map->driver)) 01233 ret = eng; 01234 } 01235 } 01236 01237 ast_mutex_unlock(&config_lock); 01238 01239 /* if we found a mapping, but the engine is not available, then issue a warning */ 01240 if (map && !ret) 01241 ast_log(LOG_WARNING, "Realtime mapping for '%s' found to engine '%s', but the engine is not available\n", map->name, map->driver); 01242 01243 return ret; 01244 } 01245 01246 static struct ast_config_engine text_file_engine = { 01247 .name = "text", 01248 .load_func = config_text_file_load, 01249 }; 01250 01251 struct ast_config *ast_config_internal_load(const char *filename, struct ast_config *cfg, int withcomments) 01252 { 01253 char db[256]; 01254 char table[256]; 01255 struct ast_config_engine *loader = &text_file_engine; 01256 struct ast_config *result; 01257 01258 if (cfg->include_level == cfg->max_include_level) { 01259 ast_log(LOG_WARNING, "Maximum Include level (%d) exceeded\n", cfg->max_include_level); 01260 return NULL; 01261 } 01262 01263 cfg->include_level++; 01264 01265 if (strcmp(filename, extconfig_conf) && strcmp(filename, "asterisk.conf") && config_engine_list) { 01266 struct ast_config_engine *eng; 01267 01268 eng = find_engine(filename, db, sizeof(db), table, sizeof(table)); 01269 01270 01271 if (eng && eng->load_func) { 01272 loader = eng; 01273 } else { 01274 eng = find_engine("global", db, sizeof(db), table, sizeof(table)); 01275 if (eng && eng->load_func) 01276 loader = eng; 01277 } 01278 } 01279 01280 result = loader->load_func(db, table, filename, cfg, withcomments); 01281 01282 if (result) 01283 result->include_level--; 01284 01285 return result; 01286 } 01287 01288 struct ast_config *ast_config_load(const char *filename) 01289 { 01290 struct ast_config *cfg; 01291 struct ast_config *result; 01292 01293 cfg = ast_config_new(); 01294 if (!cfg) 01295 return NULL; 01296 01297 result = ast_config_internal_load(filename, cfg, 0); 01298 if (!result) 01299 ast_config_destroy(cfg); 01300 01301 return result; 01302 } 01303 01304 struct ast_config *ast_config_load_with_comments(const char *filename) 01305 { 01306 struct ast_config *cfg; 01307 struct ast_config *result; 01308 01309 cfg = ast_config_new(); 01310 if (!cfg) 01311 return NULL; 01312 01313 result = ast_config_internal_load(filename, cfg, 1); 01314 if (!result) 01315 ast_config_destroy(cfg); 01316 01317 return result; 01318 } 01319 01320 static struct ast_variable *ast_load_realtime_helper(const char *family, va_list ap) 01321 { 01322 struct ast_config_engine *eng; 01323 char db[256]=""; 01324 char table[256]=""; 01325 struct ast_variable *res=NULL; 01326 01327 eng = find_engine(family, db, sizeof(db), table, sizeof(table)); 01328 if (eng && eng->realtime_func) 01329 res = eng->realtime_func(db, table, ap); 01330 01331 return res; 01332 } 01333 01334 struct ast_variable *ast_load_realtime_all(const char *family, ...) 01335 { 01336 struct ast_variable *res; 01337 va_list ap; 01338 01339 va_start(ap, family); 01340 res = ast_load_realtime_helper(family, ap); 01341 va_end(ap); 01342 01343 return res; 01344 } 01345 01346 struct ast_variable *ast_load_realtime(const char *family, ...) 01347 { 01348 struct ast_variable *res, *cur, *prev = NULL, *freeme = NULL; 01349 va_list ap; 01350 01351 va_start(ap, family); 01352 res = ast_load_realtime_helper(family, ap); 01353 va_end(ap); 01354 01355 /* Eliminate blank entries */ 01356 for (cur = res; cur; cur = cur->next) { 01357 if (freeme) { 01358 free(freeme); 01359 freeme = NULL; 01360 } 01361 01362 if (ast_strlen_zero(cur->value)) { 01363 if (prev) 01364 prev->next = cur->next; 01365 else 01366 res = cur->next; 01367 freeme = cur; 01368 } else { 01369 prev = cur; 01370 } 01371 } 01372 return res; 01373 } 01374 01375 /*! \brief Check if realtime engine is configured for family */ 01376 int ast_check_realtime(const char *family) 01377 { 01378 struct ast_config_engine *eng; 01379 01380 eng = find_engine(family, NULL, 0, NULL, 0); 01381 if (eng) 01382 return 1; 01383 return 0; 01384 01385 } 01386 01387 struct ast_config *ast_load_realtime_multientry(const char *family, ...) 01388 { 01389 struct ast_config_engine *eng; 01390 char db[256]=""; 01391 char table[256]=""; 01392 struct ast_config *res=NULL; 01393 va_list ap; 01394 01395 va_start(ap, family); 01396 eng = find_engine(family, db, sizeof(db), table, sizeof(table)); 01397 if (eng && eng->realtime_multi_func) 01398 res = eng->realtime_multi_func(db, table, ap); 01399 va_end(ap); 01400 01401 return res; 01402 } 01403 01404 int ast_update_realtime(const char *family, const char *keyfield, const char *lookup, ...) 01405 { 01406 struct ast_config_engine *eng; 01407 int res = -1; 01408 char db[256]=""; 01409 char table[256]=""; 01410 va_list ap; 01411 01412 va_start(ap, lookup); 01413 eng = find_engine(family, db, sizeof(db), table, sizeof(table)); 01414 if (eng && eng->update_func) 01415 res = eng->update_func(db, table, keyfield, lookup, ap); 01416 va_end(ap); 01417 01418 return res; 01419 } 01420 01421 static int config_command(int fd, int argc, char **argv) 01422 { 01423 struct ast_config_engine *eng; 01424 struct ast_config_map *map; 01425 01426 ast_mutex_lock(&config_lock); 01427 01428 ast_cli(fd, "\n\n"); 01429 for (eng = config_engine_list; eng; eng = eng->next) { 01430 ast_cli(fd, "\nConfig Engine: %s\n", eng->name); 01431 for (map = config_maps; map; map = map->next) 01432 if (!strcasecmp(map->driver, eng->name)) { 01433 ast_cli(fd, "===> %s (db=%s, table=%s)\n", map->name, map->database, 01434 map->table ? map->table : map->name); 01435 } 01436 } 01437 ast_cli(fd,"\n\n"); 01438 01439 ast_mutex_unlock(&config_lock); 01440 01441 return 0; 01442 } 01443 01444 static char show_config_help[] = 01445 "Usage: core show config mappings\n" 01446 " Shows the filenames to config engines.\n"; 01447 01448 static struct ast_cli_entry cli_config[] = { 01449 { { "core", "show", "config", "mappings", NULL }, 01450 config_command, "Display config mappings (file names to config engines)", 01451 show_config_help }, 01452 }; 01453 01454 int register_config_cli() 01455 { 01456 ast_cli_register_multiple(cli_config, sizeof(cli_config) / sizeof(struct ast_cli_entry)); 01457 return 0; 01458 }