2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2005, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
21 * \brief Configuration File Parser
23 * \author Mark Spencer <markster@digium.com>
25 * Includes the Asterisk Realtime API - ARA
26 * See doc/realtime.txt and doc/extconfig.txt
36 #define AST_INCLUDE_GLOB 1
37 #ifdef AST_INCLUDE_GLOB
38 #if defined(__Darwin__) || defined(__CYGWIN__)
39 #define GLOB_ABORTED GLOB_ABEND
46 ASTERISK_FILE_VERSION(__FILE__
, "$Revision$")
48 #include "asterisk/config.h"
49 #include "asterisk/cli.h"
50 #include "asterisk/lock.h"
51 #include "asterisk/options.h"
52 #include "asterisk/logger.h"
53 #include "asterisk/utils.h"
54 #include "asterisk/channel.h"
55 #include "asterisk/app.h"
57 #define MAX_NESTED_COMMENTS 128
58 #define COMMENT_START ";--"
59 #define COMMENT_END "--;"
60 #define COMMENT_META ';'
61 #define COMMENT_TAG '-'
63 static char *extconfig_conf
= "extconfig.conf";
65 static struct ast_config_map
{
66 struct ast_config_map
*next
;
72 } *config_maps
= NULL
;
74 AST_MUTEX_DEFINE_STATIC(config_lock
);
75 static struct ast_config_engine
*config_engine_list
;
77 #define MAX_INCLUDE_LEVEL 10
80 struct ast_comment
*next
;
86 int ignored
; /* do not let user of the config see this category */
87 struct ast_variable
*root
;
88 struct ast_variable
*last
;
89 struct ast_category
*next
;
93 struct ast_category
*root
;
94 struct ast_category
*last
;
95 struct ast_category
*current
;
96 struct ast_category
*last_browse
; /* used to cache the last category supplied via category_browse */
98 int max_include_level
;
101 struct ast_variable
*ast_variable_new(const char *name
, const char *value
)
103 struct ast_variable
*variable
;
104 int name_len
= strlen(name
) + 1;
106 if ((variable
= ast_calloc(1, name_len
+ strlen(value
) + 1 + sizeof(*variable
)))) {
107 variable
->name
= variable
->stuff
;
108 variable
->value
= variable
->stuff
+ name_len
;
109 strcpy(variable
->name
,name
);
110 strcpy(variable
->value
,value
);
116 void ast_variable_append(struct ast_category
*category
, struct ast_variable
*variable
)
121 category
->last
->next
= variable
;
123 category
->root
= variable
;
124 category
->last
= variable
;
127 void ast_variables_destroy(struct ast_variable
*v
)
129 struct ast_variable
*vn
;
138 struct ast_variable
*ast_variable_browse(const struct ast_config
*config
, const char *category
)
140 struct ast_category
*cat
= NULL
;
142 if (category
&& config
->last_browse
&& (config
->last_browse
->name
== category
))
143 cat
= config
->last_browse
;
145 cat
= ast_category_get(config
, category
);
147 return (cat
) ? cat
->root
: NULL
;
150 char *ast_variable_retrieve(const struct ast_config
*config
, const char *category
, const char *variable
)
152 struct ast_variable
*v
;
155 for (v
= ast_variable_browse(config
, category
); v
; v
= v
->next
) {
156 if (!strcasecmp(variable
, v
->name
))
160 struct ast_category
*cat
;
162 for (cat
= config
->root
; cat
; cat
= cat
->next
)
163 for (v
= cat
->root
; v
; v
= v
->next
)
164 if (!strcasecmp(variable
, v
->name
))
171 static struct ast_variable
*variable_clone(const struct ast_variable
*old
)
173 struct ast_variable
*new = ast_variable_new(old
->name
, old
->value
);
176 new->lineno
= old
->lineno
;
177 new->object
= old
->object
;
178 new->blanklines
= old
->blanklines
;
179 /* TODO: clone comments? */
185 static void move_variables(struct ast_category
*old
, struct ast_category
*new)
187 struct ast_variable
*var
= old
->root
;
190 /* we can just move the entire list in a single op */
191 ast_variable_append(new, var
);
194 struct ast_variable
*next
= var
->next
;
196 ast_variable_append(new, var
);
202 struct ast_category
*ast_category_new(const char *name
)
204 struct ast_category
*category
;
206 if ((category
= ast_calloc(1, sizeof(*category
))))
207 ast_copy_string(category
->name
, name
, sizeof(category
->name
));
211 static struct ast_category
*category_get(const struct ast_config
*config
, const char *category_name
, int ignored
)
213 struct ast_category
*cat
;
215 /* try exact match first, then case-insensitive match */
216 for (cat
= config
->root
; cat
; cat
= cat
->next
) {
217 if (cat
->name
== category_name
&& (ignored
|| !cat
->ignored
))
221 for (cat
= config
->root
; cat
; cat
= cat
->next
) {
222 if (!strcasecmp(cat
->name
, category_name
) && (ignored
|| !cat
->ignored
))
229 struct ast_category
*ast_category_get(const struct ast_config
*config
, const char *category_name
)
231 return category_get(config
, category_name
, 0);
234 int ast_category_exist(const struct ast_config
*config
, const char *category_name
)
236 return !!ast_category_get(config
, category_name
);
239 void ast_category_append(struct ast_config
*config
, struct ast_category
*category
)
242 config
->last
->next
= category
;
244 config
->root
= category
;
245 config
->last
= category
;
246 config
->current
= category
;
249 void ast_category_destroy(struct ast_category
*cat
)
251 ast_variables_destroy(cat
->root
);
255 static struct ast_category
*next_available_category(struct ast_category
*cat
)
257 for (; cat
&& cat
->ignored
; cat
= cat
->next
);
262 char *ast_category_browse(struct ast_config
*config
, const char *prev
)
264 struct ast_category
*cat
= NULL
;
266 if (prev
&& config
->last_browse
&& (config
->last_browse
->name
== prev
))
267 cat
= config
->last_browse
->next
;
268 else if (!prev
&& config
->root
)
271 for (cat
= config
->root
; cat
; cat
= cat
->next
) {
272 if (cat
->name
== prev
) {
278 for (cat
= config
->root
; cat
; cat
= cat
->next
) {
279 if (!strcasecmp(cat
->name
, prev
)) {
288 cat
= next_available_category(cat
);
290 config
->last_browse
= cat
;
291 return (cat
) ? cat
->name
: NULL
;
294 struct ast_variable
*ast_category_detach_variables(struct ast_category
*cat
)
296 struct ast_variable
*v
;
304 void ast_category_rename(struct ast_category
*cat
, const char *name
)
306 ast_copy_string(cat
->name
, name
, sizeof(cat
->name
));
309 static void inherit_category(struct ast_category
*new, const struct ast_category
*base
)
311 struct ast_variable
*var
;
313 for (var
= base
->root
; var
; var
= var
->next
)
314 ast_variable_append(new, variable_clone(var
));
317 struct ast_config
*ast_config_new(void)
319 struct ast_config
*config
;
321 if ((config
= ast_calloc(1, sizeof(*config
))))
322 config
->max_include_level
= MAX_INCLUDE_LEVEL
;
326 void ast_config_destroy(struct ast_config
*cfg
)
328 struct ast_category
*cat
, *catn
;
335 ast_variables_destroy(cat
->root
);
343 struct ast_category
*ast_config_get_current_category(const struct ast_config
*cfg
)
348 void ast_config_set_current_category(struct ast_config
*cfg
, const struct ast_category
*cat
)
350 /* cast below is just to silence compiler warning about dropping "const" */
351 cfg
->current
= (struct ast_category
*) cat
;
354 static int process_text_line(struct ast_config
*cfg
, struct ast_category
**cat
, char *buf
, int lineno
, const char *configfile
)
358 struct ast_variable
*v
;
359 char cmd
[512], exec_file
[512];
360 int object
, do_exec
, do_include
;
362 /* Actually parse the entry */
364 struct ast_category
*newcat
= NULL
;
367 /* A category header */
368 c
= strchr(cur
, ']');
370 ast_log(LOG_WARNING
, "parse error: no closing ']', line %d of %s\n", lineno
, configfile
);
378 if (!(*cat
= newcat
= ast_category_new(catname
))) {
381 /* If there are options or categories to inherit from, process them now */
383 if (!(cur
= strchr(c
, ')'))) {
384 ast_log(LOG_WARNING
, "parse error: no closing ')', line %d of %s\n", lineno
, configfile
);
388 while ((cur
= strsep(&c
, ","))) {
389 if (!strcasecmp(cur
, "!")) {
391 } else if (!strcasecmp(cur
, "+")) {
392 *cat
= category_get(cfg
, catname
, 1);
394 ast_config_destroy(cfg
);
396 ast_category_destroy(newcat
);
397 ast_log(LOG_WARNING
, "Category addition requested, but category '%s' does not exist, line %d of %s\n", catname
, lineno
, configfile
);
401 move_variables(newcat
, *cat
);
402 ast_category_destroy(newcat
);
406 struct ast_category
*base
;
408 base
= category_get(cfg
, cur
, 1);
410 ast_log(LOG_WARNING
, "Inheritance requested, but category '%s' does not exist, line %d of %s\n", cur
, lineno
, configfile
);
413 inherit_category(*cat
, base
);
418 ast_category_append(cfg
, *cat
);
419 } else if (cur
[0] == '#') {
423 while(*c
&& (*c
> 32)) c
++;
426 /* Find real argument */
427 c
= ast_skip_blanks(c
+ 1);
432 do_include
= !strcasecmp(cur
, "include");
434 do_exec
= !strcasecmp(cur
, "exec");
437 if (do_exec
&& !ast_opt_exec_includes
) {
438 ast_log(LOG_WARNING
, "Cannot perform #exec unless execincludes option is enabled in asterisk.conf (options section)!\n");
441 if (do_include
|| do_exec
) {
443 /* Strip off leading and trailing "'s and <>'s */
444 while((*c
== '<') || (*c
== '>') || (*c
== '\"')) c
++;
445 /* Get rid of leading mess */
447 while (!ast_strlen_zero(cur
)) {
448 c
= cur
+ strlen(cur
) - 1;
449 if ((*c
== '>') || (*c
== '<') || (*c
== '\"'))
454 /* #exec </path/to/executable>
455 We create a tmp file, then we #include it, then we delete it. */
457 snprintf(exec_file
, sizeof(exec_file
), "/var/tmp/exec.%d.%ld", (int)time(NULL
), (long)pthread_self());
458 snprintf(cmd
, sizeof(cmd
), "%s > %s 2>&1", cur
, exec_file
);
459 ast_safe_system(cmd
);
464 do_include
= ast_config_internal_load(cur
, cfg
) ? 1 : 0;
465 if(!ast_strlen_zero(exec_file
))
471 ast_log(LOG_WARNING
, "Directive '#%s' needs an argument (%s) at line %d of %s\n",
472 do_exec
? "exec" : "include",
473 do_exec
? "/path/to/executable" : "filename",
479 ast_log(LOG_WARNING
, "Unknown directive '%s' at line %d of %s\n", cur
, lineno
, configfile
);
481 /* Just a line (variable = value) */
484 "parse error: No category context for line %d of %s\n", lineno
, configfile
);
487 c
= strchr(cur
, '=');
497 if ((v
= ast_variable_new(ast_strip(cur
), ast_strip(c
)))) {
500 /* Put and reset comments */
502 ast_variable_append(*cat
, v
);
507 ast_log(LOG_WARNING
, "No '=' (equal sign) in line %d of %s\n", lineno
, configfile
);
514 static struct ast_config
*config_text_file_load(const char *database
, const char *table
, const char *filename
, struct ast_config
*cfg
)
518 char *new_buf
, *comment_p
, *process_buf
;
521 int comment
= 0, nest
[MAX_NESTED_COMMENTS
];
522 struct ast_category
*cat
= NULL
;
526 cat
= ast_config_get_current_category(cfg
);
528 if (filename
[0] == '/') {
529 ast_copy_string(fn
, filename
, sizeof(fn
));
531 snprintf(fn
, sizeof(fn
), "%s/%s", (char *)ast_config_AST_CONFIG_DIR
, filename
);
534 #ifdef AST_INCLUDE_GLOB
538 globbuf
.gl_offs
= 0; /* initialize it to silence gcc */
540 glob_ret
= glob(fn
, GLOB_NOCHECK
, NULL
, &globbuf
);
542 glob_ret
= glob(fn
, GLOB_NOMAGIC
|GLOB_BRACE
, NULL
, &globbuf
);
544 if (glob_ret
== GLOB_NOSPACE
)
546 "Glob Expansion of pattern '%s' failed: Not enough memory\n", fn
);
547 else if (glob_ret
== GLOB_ABORTED
)
549 "Glob Expansion of pattern '%s' failed: Read error\n", fn
);
551 /* loop over expanded files */
553 for (i
=0; i
<globbuf
.gl_pathc
; i
++) {
554 ast_copy_string(fn
, globbuf
.gl_pathv
[i
], sizeof(fn
));
557 if (stat(fn
, &statbuf
))
560 if (!S_ISREG(statbuf
.st_mode
)) {
561 ast_log(LOG_WARNING
, "'%s' is not a regular file, ignoring\n", fn
);
564 if ((option_verbose
> 1) && !option_debug
) {
565 ast_verbose(VERBOSE_PREFIX_2
"Parsing '%s': ", fn
);
568 if (!(f
= fopen(fn
, "r"))) {
570 ast_log(LOG_DEBUG
, "No file to parse: %s\n", fn
);
571 else if (option_verbose
> 1)
572 ast_verbose( "Not found (%s)\n", strerror(errno
));
577 ast_log(LOG_DEBUG
, "Parsing %s\n", fn
);
578 else if (option_verbose
> 1)
579 ast_verbose("Found\n");
582 if (fgets(buf
, sizeof(buf
), f
)) {
588 while ((comment_p
= strchr(new_buf
, COMMENT_META
))) {
589 if ((comment_p
> new_buf
) && (*(comment_p
-1) == '\\')) {
590 /* Yuck, gotta memmove */
591 memmove(comment_p
- 1, comment_p
, strlen(comment_p
) + 1);
593 } else if(comment_p
[1] == COMMENT_TAG
&& comment_p
[2] == COMMENT_TAG
&& (comment_p
[3] != '-')) {
594 /* Meta-Comment start detected ";--" */
595 if (comment
< MAX_NESTED_COMMENTS
) {
597 new_buf
= comment_p
+ 3;
599 nest
[comment
-1] = lineno
;
601 ast_log(LOG_ERROR
, "Maximum nest limit of %d reached.\n", MAX_NESTED_COMMENTS
);
603 } else if ((comment_p
>= new_buf
+ 2) &&
604 (*(comment_p
- 1) == COMMENT_TAG
) &&
605 (*(comment_p
- 2) == COMMENT_TAG
)) {
606 /* Meta-Comment end detected */
608 new_buf
= comment_p
+ 1;
610 /* Back to non-comment now */
612 /* Actually have to move what's left over the top, then continue */
614 oldptr
= process_buf
+ strlen(process_buf
);
615 memmove(oldptr
, new_buf
, strlen(new_buf
) + 1);
618 process_buf
= new_buf
;
622 /* If ; is found, and we are not nested in a comment,
623 we immediately stop all comment processing */
627 new_buf
= comment_p
+ 1;
631 char *buf
= ast_strip(process_buf
);
632 if (!ast_strlen_zero(buf
)) {
633 if (process_text_line(cfg
, &cat
, buf
, lineno
, filename
)) {
644 ast_log(LOG_WARNING
,"Unterminated comment detected beginning on line %d\n", nest
[comment
]);
646 #ifdef AST_INCLUDE_GLOB
660 int config_text_file_save(const char *configfile
, const struct ast_config
*cfg
, const char *generator
)
666 struct ast_variable
*var
;
667 struct ast_category
*cat
;
670 if (configfile
[0] == '/') {
671 ast_copy_string(fn
, configfile
, sizeof(fn
));
673 snprintf(fn
, sizeof(fn
), "%s/%s", ast_config_AST_CONFIG_DIR
, configfile
);
676 ast_copy_string(date
, ctime(&t
), sizeof(date
));
678 if ((f
= fopen(fn
, "w+"))) {
680 if ((f
= fopen(fn
, "w"))) {
682 if ((option_verbose
> 1) && !option_debug
)
683 ast_verbose( VERBOSE_PREFIX_2
"Saving '%s': ", fn
);
685 fprintf(f
, ";! Automatically generated configuration file\n");
686 fprintf(f
, ";! Filename: %s (%s)\n", configfile
, fn
);
687 fprintf(f
, ";! Generator: %s\n", generator
);
688 fprintf(f
, ";! Creation Date: %s", date
);
692 /* Dump section with any appropriate comment */
693 fprintf(f
, "[%s]\n", cat
->name
);
697 fprintf(f
, "%s %s %s ; %s\n", var
->name
, (var
->object
? "=>" : "="), var
->value
, var
->sameline
->cmt
);
699 fprintf(f
, "%s %s %s\n", var
->name
, (var
->object
? "=>" : "="), var
->value
);
700 if (var
->blanklines
) {
701 blanklines
= var
->blanklines
;
709 /* Put an empty line */
716 printf("Unable to open for writing: %s\n", fn
);
717 else if (option_verbose
> 1)
718 printf( "Unable to write (%s)", strerror(errno
));
725 static void clear_config_maps(void)
727 struct ast_config_map
*map
;
729 ast_mutex_lock(&config_lock
);
731 while (config_maps
) {
733 config_maps
= config_maps
->next
;
737 ast_mutex_unlock(&config_lock
);
740 static int append_mapping(char *name
, char *driver
, char *database
, char *table
)
742 struct ast_config_map
*map
;
745 length
= sizeof(*map
);
746 length
+= strlen(name
) + 1;
747 length
+= strlen(driver
) + 1;
748 length
+= strlen(database
) + 1;
750 length
+= strlen(table
) + 1;
752 if (!(map
= ast_calloc(1, length
)))
755 map
->name
= map
->stuff
;
756 strcpy(map
->name
, name
);
757 map
->driver
= map
->name
+ strlen(map
->name
) + 1;
758 strcpy(map
->driver
, driver
);
759 map
->database
= map
->driver
+ strlen(map
->driver
) + 1;
760 strcpy(map
->database
, database
);
762 map
->table
= map
->database
+ strlen(map
->database
) + 1;
763 strcpy(map
->table
, table
);
765 map
->next
= config_maps
;
767 if (option_verbose
> 1)
768 ast_verbose(VERBOSE_PREFIX_2
"Binding %s to %s/%s/%s\n",
769 map
->name
, map
->driver
, map
->database
, map
->table
? map
->table
: map
->name
);
775 int read_config_maps(void)
777 struct ast_config
*config
, *configtmp
;
778 struct ast_variable
*v
;
779 char *driver
, *table
, *database
, *stringp
;
783 configtmp
= ast_config_new();
784 configtmp
->max_include_level
= 1;
785 config
= ast_config_internal_load(extconfig_conf
, configtmp
);
787 ast_config_destroy(configtmp
);
791 for (v
= ast_variable_browse(config
, "settings"); v
; v
= v
->next
) {
793 driver
= strsep(&stringp
, ",");
795 /* check if the database text starts with a double quote */
796 if (*stringp
== '"') {
798 database
= strsep(&stringp
, "\"");
799 strsep(&stringp
, ",");
801 /* apparently this text has no quotes */
802 database
= strsep(&stringp
, ",");
805 table
= strsep(&stringp
, ",");
807 if (!strcmp(v
->name
, extconfig_conf
)) {
808 ast_log(LOG_WARNING
, "Cannot bind '%s'!\n", extconfig_conf
);
812 if (!strcmp(v
->name
, "asterisk.conf")) {
813 ast_log(LOG_WARNING
, "Cannot bind 'asterisk.conf'!\n");
817 if (!strcmp(v
->name
, "logger.conf")) {
818 ast_log(LOG_WARNING
, "Cannot bind 'logger.conf'!\n");
822 if (!driver
|| !database
)
824 if (!strcasecmp(v
->name
, "sipfriends")) {
825 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");
826 append_mapping("sipusers", driver
, database
, table
? table
: "sipfriends");
827 append_mapping("sippeers", driver
, database
, table
? table
: "sipfriends");
828 } else if (!strcasecmp(v
->name
, "iaxfriends")) {
829 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");
830 append_mapping("iaxusers", driver
, database
, table
? table
: "iaxfriends");
831 append_mapping("iaxpeers", driver
, database
, table
? table
: "iaxfriends");
833 append_mapping(v
->name
, driver
, database
, table
);
836 ast_config_destroy(config
);
840 int ast_config_engine_register(struct ast_config_engine
*new)
842 struct ast_config_engine
*ptr
;
844 ast_mutex_lock(&config_lock
);
846 if (!config_engine_list
) {
847 config_engine_list
= new;
849 for (ptr
= config_engine_list
; ptr
->next
; ptr
=ptr
->next
);
853 ast_mutex_unlock(&config_lock
);
854 ast_log(LOG_NOTICE
,"Registered Config Engine %s\n", new->name
);
859 int ast_config_engine_deregister(struct ast_config_engine
*del
)
861 struct ast_config_engine
*ptr
, *last
=NULL
;
863 ast_mutex_lock(&config_lock
);
865 for (ptr
= config_engine_list
; ptr
; ptr
=ptr
->next
) {
868 last
->next
= ptr
->next
;
870 config_engine_list
= ptr
->next
;
876 ast_mutex_unlock(&config_lock
);
881 /*! \brief Find realtime engine for realtime family */
882 static struct ast_config_engine
*find_engine(const char *family
, char *database
, int dbsiz
, char *table
, int tabsiz
)
884 struct ast_config_engine
*eng
, *ret
= NULL
;
885 struct ast_config_map
*map
;
887 ast_mutex_lock(&config_lock
);
889 for (map
= config_maps
; map
; map
= map
->next
) {
890 if (!strcasecmp(family
, map
->name
)) {
892 ast_copy_string(database
, map
->database
, dbsiz
);
894 ast_copy_string(table
, map
->table
? map
->table
: family
, tabsiz
);
899 /* Check if the required driver (engine) exist */
901 for (eng
= config_engine_list
; !ret
&& eng
; eng
= eng
->next
) {
902 if (!strcasecmp(eng
->name
, map
->driver
))
907 ast_mutex_unlock(&config_lock
);
909 /* if we found a mapping, but the engine is not available, then issue a warning */
911 ast_log(LOG_WARNING
, "Realtime mapping for '%s' found to engine '%s', but the engine is not available\n", map
->name
, map
->driver
);
916 static struct ast_config_engine text_file_engine
= {
918 .load_func
= config_text_file_load
,
921 struct ast_config
*ast_config_internal_load(const char *filename
, struct ast_config
*cfg
)
925 struct ast_config_engine
*loader
= &text_file_engine
;
926 struct ast_config
*result
;
928 if (cfg
->include_level
== cfg
->max_include_level
) {
929 ast_log(LOG_WARNING
, "Maximum Include level (%d) exceeded\n", cfg
->max_include_level
);
933 cfg
->include_level
++;
935 if (strcmp(filename
, extconfig_conf
) && strcmp(filename
, "asterisk.conf") && config_engine_list
) {
936 struct ast_config_engine
*eng
;
938 eng
= find_engine(filename
, db
, sizeof(db
), table
, sizeof(table
));
941 if (eng
&& eng
->load_func
) {
944 eng
= find_engine("global", db
, sizeof(db
), table
, sizeof(table
));
945 if (eng
&& eng
->load_func
)
950 result
= loader
->load_func(db
, table
, filename
, cfg
);
953 result
->include_level
--;
958 struct ast_config
*ast_config_load(const char *filename
)
960 struct ast_config
*cfg
;
961 struct ast_config
*result
;
963 cfg
= ast_config_new();
967 result
= ast_config_internal_load(filename
, cfg
);
969 ast_config_destroy(cfg
);
974 struct ast_variable
*ast_load_realtime(const char *family
, ...)
976 struct ast_config_engine
*eng
;
979 struct ast_variable
*res
=NULL
;
982 va_start(ap
, family
);
983 eng
= find_engine(family
, db
, sizeof(db
), table
, sizeof(table
));
984 if (eng
&& eng
->realtime_func
)
985 res
= eng
->realtime_func(db
, table
, ap
);
991 /*! \brief Check if realtime engine is configured for family */
992 int ast_check_realtime(const char *family
)
994 struct ast_config_engine
*eng
;
996 eng
= find_engine(family
, NULL
, 0, NULL
, 0);
1003 struct ast_config
*ast_load_realtime_multientry(const char *family
, ...)
1005 struct ast_config_engine
*eng
;
1008 struct ast_config
*res
=NULL
;
1011 va_start(ap
, family
);
1012 eng
= find_engine(family
, db
, sizeof(db
), table
, sizeof(table
));
1013 if (eng
&& eng
->realtime_multi_func
)
1014 res
= eng
->realtime_multi_func(db
, table
, ap
);
1020 int ast_update_realtime(const char *family
, const char *keyfield
, const char *lookup
, ...)
1022 struct ast_config_engine
*eng
;
1028 va_start(ap
, lookup
);
1029 eng
= find_engine(family
, db
, sizeof(db
), table
, sizeof(table
));
1030 if (eng
&& eng
->update_func
)
1031 res
= eng
->update_func(db
, table
, keyfield
, lookup
, ap
);
1037 static int config_command(int fd
, int argc
, char **argv
)
1039 struct ast_config_engine
*eng
;
1040 struct ast_config_map
*map
;
1042 ast_mutex_lock(&config_lock
);
1044 ast_cli(fd
, "\n\n");
1045 for (eng
= config_engine_list
; eng
; eng
= eng
->next
) {
1046 ast_cli(fd
, "\nConfig Engine: %s\n", eng
->name
);
1047 for (map
= config_maps
; map
; map
= map
->next
)
1048 if (!strcasecmp(map
->driver
, eng
->name
)) {
1049 ast_cli(fd
, "===> %s (db=%s, table=%s)\n", map
->name
, map
->database
,
1050 map
->table
? map
->table
: map
->name
);
1055 ast_mutex_unlock(&config_lock
);
1060 static char show_config_help
[] =
1061 "Usage: show config mappings\n"
1062 " Shows the filenames to config engines.\n";
1064 static struct ast_cli_entry config_command_struct
= {
1065 { "show", "config", "mappings", NULL
}, config_command
, "Show Config mappings (file names to config engines)", show_config_help
, NULL
1068 int register_config_cli()
1070 return ast_cli_register(&config_command_struct
);