Remove ASTERISK_REGISTER_FILE.
[asterisk/asterisk.git] / main / config.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2010, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
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.
13  *
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.
17  */
18
19 /*! \file
20  *
21  * \brief Configuration File Parser
22  *
23  * \author Mark Spencer <markster@digium.com>
24  *
25  * Includes the Asterisk Realtime API - ARA
26  * See http://wiki.asterisk.org
27  */
28
29 /*** MODULEINFO
30         <support_level>core</support_level>
31  ***/
32
33 #include "asterisk.h"
34
35 #include "asterisk/paths.h"     /* use ast_config_AST_CONFIG_DIR */
36 #include "asterisk/network.h"   /* we do some sockaddr manipulation here */
37
38 #include <string.h>
39 #include <libgen.h>
40 #include <time.h>
41 #include <sys/stat.h>
42
43 #include <math.h>       /* HUGE_VAL */
44 #include <regex.h>
45
46 #define AST_INCLUDE_GLOB 1
47
48 #include "asterisk/config.h"
49 #include "asterisk/cli.h"
50 #include "asterisk/lock.h"
51 #include "asterisk/utils.h"
52 #include "asterisk/channel.h"
53 #include "asterisk/app.h"
54 #include "asterisk/astobj2.h"
55 #include "asterisk/strings.h"   /* for the ast_str_*() API */
56 #include "asterisk/netsock2.h"
57
58 #define MAX_NESTED_COMMENTS 128
59 #define COMMENT_START ";--"
60 #define COMMENT_END "--;"
61 #define COMMENT_META ';'
62 #define COMMENT_TAG '-'
63
64 /*!
65  * Define the minimum filename space to reserve for each
66  * ast_variable in case the filename is renamed later by
67  * ast_include_rename().
68  */
69 #define MIN_VARIABLE_FNAME_SPACE        40
70
71 static char *extconfig_conf = "extconfig.conf";
72
73 static struct ao2_container *cfg_hooks;
74 static void config_hook_exec(const char *filename, const char *module, const struct ast_config *cfg);
75 static inline struct ast_variable *variable_list_switch(struct ast_variable *l1, struct ast_variable *l2);
76 static int does_category_match(struct ast_category *cat, const char *category_name,
77         const char *match, char sep);
78
79 /*! \brief Structure to keep comments for rewriting configuration files */
80 struct ast_comment {
81         struct ast_comment *next;
82         /*! Comment body allocated after struct. */
83         char cmt[0];
84 };
85
86 /*! \brief Hold the mtime for config files, so if we don't need to reread our config, don't. */
87 struct cache_file_include {
88         AST_LIST_ENTRY(cache_file_include) list;
89         /*! Filename or wildcard pattern as specified by the including file. */
90         char include[0];
91 };
92
93 struct cache_file_mtime {
94         AST_LIST_ENTRY(cache_file_mtime) list;
95         AST_LIST_HEAD_NOLOCK(includes, cache_file_include) includes;
96         unsigned int has_exec:1;
97         /*! stat() file size */
98         unsigned long stat_size;
99         /*! stat() file modtime nanoseconds */
100         unsigned long stat_mtime_nsec;
101         /*! stat() file modtime seconds since epoc */
102         time_t stat_mtime;
103
104         /*! String stuffed in filename[] after the filename string. */
105         const char *who_asked;
106         /*! Filename and who_asked stuffed after it. */
107         char filename[0];
108 };
109
110 /*! Cached file mtime list. */
111 static AST_LIST_HEAD_STATIC(cfmtime_head, cache_file_mtime);
112
113 static int init_appendbuf(void *data)
114 {
115         struct ast_str **str = data;
116         *str = ast_str_create(16);
117         return *str ? 0 : -1;
118 }
119
120 AST_THREADSTORAGE_CUSTOM(appendbuf, init_appendbuf, ast_free_ptr);
121
122 /* comment buffers are better implemented using the ast_str_*() API */
123 #define CB_SIZE 250     /* initial size of comment buffers */
124
125 static void  CB_ADD(struct ast_str **cb, const char *str)
126 {
127         ast_str_append(cb, 0, "%s", str);
128 }
129
130 static void  CB_ADD_LEN(struct ast_str **cb, const char *str, int len)
131 {
132         char *s = ast_alloca(len + 1);
133
134         memcpy(s, str, len);
135         s[len] = '\0';
136         ast_str_append(cb, 0, "%s", s);
137 }
138
139 static void CB_RESET(struct ast_str *cb, struct ast_str *llb)
140 {
141         if (cb) {
142                 ast_str_reset(cb);
143         }
144         if (llb) {
145                 ast_str_reset(llb);
146         }
147 }
148
149 static struct ast_comment *ALLOC_COMMENT(struct ast_str *buffer)
150 {
151         struct ast_comment *x = NULL;
152         if (!buffer || !ast_str_strlen(buffer)) {
153                 return NULL;
154         }
155         if ((x = ast_calloc(1, sizeof(*x) + ast_str_strlen(buffer) + 1))) {
156                 strcpy(x->cmt, ast_str_buffer(buffer)); /* SAFE */
157         }
158         return x;
159 }
160
161 /* I need to keep track of each config file, and all its inclusions,
162    so that we can track blank lines in each */
163
164 struct inclfile {
165         char *fname;
166         int lineno;
167 };
168
169 static int hash_string(const void *obj, const int flags)
170 {
171         char *str = ((struct inclfile *) obj)->fname;
172         int total;
173
174         for (total = 0; *str; str++) {
175                 unsigned int tmp = total;
176                 total <<= 1; /* multiply by 2 */
177                 total += tmp; /* multiply by 3 */
178                 total <<= 2; /* multiply by 12 */
179                 total += tmp; /* multiply by 13 */
180
181                 total += ((unsigned int) (*str));
182         }
183         if (total < 0) {
184                 total = -total;
185         }
186         return total;
187 }
188
189 static int hashtab_compare_strings(void *a, void *b, int flags)
190 {
191         const struct inclfile *ae = a, *be = b;
192         return !strcmp(ae->fname, be->fname) ? CMP_MATCH | CMP_STOP : 0;
193 }
194
195 static struct ast_config_map {
196         struct ast_config_map *next;
197         int priority;
198         /*! Stored in stuff[] at struct end. */
199         const char *name;
200         /*! Stored in stuff[] at struct end. */
201         const char *driver;
202         /*! Stored in stuff[] at struct end. */
203         const char *database;
204         /*! Stored in stuff[] at struct end. */
205         const char *table;
206         /*! Contents of name, driver, database, and table in that order stuffed here. */
207         char stuff[0];
208 } *config_maps = NULL;
209
210 AST_MUTEX_DEFINE_STATIC(config_lock);
211 static struct ast_config_engine *config_engine_list;
212
213 #define MAX_INCLUDE_LEVEL 10
214
215 struct ast_category_template_instance {
216         char name[80]; /* redundant? */
217         const struct ast_category *inst;
218         AST_LIST_ENTRY(ast_category_template_instance) next;
219 };
220
221 struct ast_category {
222         char name[80];
223         int ignored;                    /*!< do not let user of the config see this category -- set by (!) after the category decl; a template */
224         int include_level;
225         /*!
226          * \brief The file name from whence this declaration was read
227          * \note Will never be NULL
228          */
229         char *file;
230         int lineno;
231         AST_LIST_HEAD_NOLOCK(template_instance_list, ast_category_template_instance) template_instances;
232         struct ast_comment *precomments;
233         struct ast_comment *sameline;
234         struct ast_comment *trailing; /*!< the last object in the list will get assigned any trailing comments when EOF is hit */
235         /*! First category variable in the list. */
236         struct ast_variable *root;
237         /*! Last category variable in the list. */
238         struct ast_variable *last;
239         /*! Previous node in the list. */
240         struct ast_category *prev;
241         /*! Next node in the list. */
242         struct ast_category *next;
243 };
244
245 struct ast_config {
246         /*! First config category in the list. */
247         struct ast_category *root;
248         /*! Last config category in the list. */
249         struct ast_category *last;
250         struct ast_category *current;
251         struct ast_category *last_browse;     /*!< used to cache the last category supplied via category_browse */
252         int include_level;
253         int max_include_level;
254         struct ast_config_include *includes;  /*!< a list of inclusions, which should describe the entire tree */
255 };
256
257 struct ast_config_include {
258         /*!
259          * \brief file name in which the include occurs
260          * \note Will never be NULL
261          */
262         char *include_location_file;
263         int  include_location_lineno;    /*!< lineno where include occurred */
264         int  exec;                       /*!< set to non-zero if its a #exec statement */
265         /*!
266          * \brief if it's an exec, you'll have both the /var/tmp to read, and the original script
267          * \note Will never be NULL if exec is non-zero
268          */
269         char *exec_file;
270         /*!
271          * \brief file name included
272          * \note Will never be NULL
273          */
274         char *included_file;
275         int inclusion_count;             /*!< if the file is included more than once, a running count thereof -- but, worry not,
276                                               we explode the instances and will include those-- so all entries will be unique */
277         int output;                      /*!< a flag to indicate if the inclusion has been output */
278         struct ast_config_include *next; /*!< ptr to next inclusion in the list */
279 };
280
281 static void ast_variable_destroy(struct ast_variable *doomed);
282 static void ast_includes_destroy(struct ast_config_include *incls);
283
284 #ifdef __AST_DEBUG_MALLOC
285 struct ast_variable *_ast_variable_new(const char *name, const char *value, const char *filename, const char *file, const char *func, int lineno)
286 #else
287 struct ast_variable *ast_variable_new(const char *name, const char *value, const char *filename)
288 #endif
289 {
290         struct ast_variable *variable;
291         int name_len = strlen(name) + 1;
292         int val_len = strlen(value) + 1;
293         int fn_len = strlen(filename) + 1;
294
295         /* Ensure a minimum length in case the filename is changed later. */
296         if (fn_len < MIN_VARIABLE_FNAME_SPACE) {
297                 fn_len = MIN_VARIABLE_FNAME_SPACE;
298         }
299
300         if (
301 #ifdef __AST_DEBUG_MALLOC
302                 (variable = __ast_calloc(1, fn_len + name_len + val_len + sizeof(*variable), file, lineno, func))
303 #else
304                 (variable = ast_calloc(1, fn_len + name_len + val_len + sizeof(*variable)))
305 #endif
306                 ) {
307                 char *dst = variable->stuff;    /* writable space starts here */
308
309                 /* Put file first so ast_include_rename() can calculate space available. */
310                 variable->file = strcpy(dst, filename);
311                 dst += fn_len;
312                 variable->name = strcpy(dst, name);
313                 dst += name_len;
314                 variable->value = strcpy(dst, value);
315         }
316         return variable;
317 }
318
319 /*!
320  * \internal
321  * \brief Move the contents from the source to the destination variable.
322  *
323  * \param dst_var Destination variable node
324  * \param src_var Source variable node
325  *
326  * \return Nothing
327  */
328 static void ast_variable_move(struct ast_variable *dst_var, struct ast_variable *src_var)
329 {
330         dst_var->lineno = src_var->lineno;
331         dst_var->object = src_var->object;
332         dst_var->blanklines = src_var->blanklines;
333         dst_var->precomments = src_var->precomments;
334         src_var->precomments = NULL;
335         dst_var->sameline = src_var->sameline;
336         src_var->sameline = NULL;
337         dst_var->trailing = src_var->trailing;
338         src_var->trailing = NULL;
339 }
340
341 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)
342 {
343         /* a file should be included ONCE. Otherwise, if one of the instances is changed,
344          * then all be changed. -- how do we know to include it? -- Handling modified
345          * instances is possible, I'd have
346          * to create a new master for each instance. */
347         struct ast_config_include *inc;
348         struct stat statbuf;
349
350         inc = ast_include_find(conf, included_file);
351         if (inc) {
352                 do {
353                         inc->inclusion_count++;
354                         snprintf(real_included_file_name, real_included_file_name_size, "%s~~%d", included_file, inc->inclusion_count);
355                 } while (stat(real_included_file_name, &statbuf) == 0);
356                 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);
357         } else
358                 *real_included_file_name = 0;
359
360         inc = ast_calloc(1,sizeof(struct ast_config_include));
361         if (!inc) {
362                 return NULL;
363         }
364         inc->include_location_file = ast_strdup(from_file);
365         inc->include_location_lineno = from_lineno;
366         if (!ast_strlen_zero(real_included_file_name))
367                 inc->included_file = ast_strdup(real_included_file_name);
368         else
369                 inc->included_file = ast_strdup(included_file);
370
371         inc->exec = is_exec;
372         if (is_exec)
373                 inc->exec_file = ast_strdup(exec_file);
374
375         if (!inc->include_location_file
376                 || !inc->included_file
377                 || (is_exec && !inc->exec_file)) {
378                 ast_includes_destroy(inc);
379                 return NULL;
380         }
381
382         /* attach this new struct to the conf struct */
383         inc->next = conf->includes;
384         conf->includes = inc;
385
386         return inc;
387 }
388
389 void ast_include_rename(struct ast_config *conf, const char *from_file, const char *to_file)
390 {
391         struct ast_config_include *incl;
392         struct ast_category *cat;
393         char *str;
394
395         int from_len = strlen(from_file);
396         int to_len = strlen(to_file);
397
398         if (strcmp(from_file, to_file) == 0) /* no use wasting time if the name is the same */
399                 return;
400
401         /* the manager code allows you to read in one config file, then
402          * write it back out under a different name. But, the new arrangement
403          * ties output lines to the file name. So, before you try to write
404          * the config file to disk, better riffle thru the data and make sure
405          * the file names are changed.
406          */
407         /* file names are on categories, includes (of course), and on variables. So,
408          * traverse all this and swap names */
409
410         for (incl = conf->includes; incl; incl=incl->next) {
411                 if (strcmp(incl->include_location_file,from_file) == 0) {
412                         if (from_len >= to_len)
413                                 strcpy(incl->include_location_file, to_file);
414                         else {
415                                 /* Keep the old filename if the allocation fails. */
416                                 str = ast_strdup(to_file);
417                                 if (str) {
418                                         ast_free(incl->include_location_file);
419                                         incl->include_location_file = str;
420                                 }
421                         }
422                 }
423         }
424         for (cat = conf->root; cat; cat = cat->next) {
425                 struct ast_variable **prev;
426                 struct ast_variable *v;
427                 struct ast_variable *new_var;
428
429                 if (strcmp(cat->file,from_file) == 0) {
430                         if (from_len >= to_len)
431                                 strcpy(cat->file, to_file);
432                         else {
433                                 /* Keep the old filename if the allocation fails. */
434                                 str = ast_strdup(to_file);
435                                 if (str) {
436                                         ast_free(cat->file);
437                                         cat->file = str;
438                                 }
439                         }
440                 }
441                 for (prev = &cat->root, v = cat->root; v; prev = &v->next, v = v->next) {
442                         if (strcmp(v->file, from_file)) {
443                                 continue;
444                         }
445
446                         /*
447                          * Calculate actual space available.  The file string is
448                          * intentionally stuffed before the name string just so we can
449                          * do this.
450                          */
451                         if (to_len < v->name - v->file) {
452                                 /* The new name will fit in the available space. */
453                                 str = (char *) v->file;/* Stupid compiler complains about discarding qualifiers even though I used a cast. */
454                                 strcpy(str, to_file);/* SAFE */
455                                 continue;
456                         }
457
458                         /* Keep the old filename if the allocation fails. */
459                         new_var = ast_variable_new(v->name, v->value, to_file);
460                         if (!new_var) {
461                                 continue;
462                         }
463
464                         /* Move items from the old list node to the replacement node. */
465                         ast_variable_move(new_var, v);
466
467                         /* Replace the old node in the list with the new node. */
468                         new_var->next = v->next;
469                         if (cat->last == v) {
470                                 cat->last = new_var;
471                         }
472                         *prev = new_var;
473
474                         ast_variable_destroy(v);
475
476                         v = new_var;
477                 }
478         }
479 }
480
481 struct ast_config_include *ast_include_find(struct ast_config *conf, const char *included_file)
482 {
483         struct ast_config_include *x;
484         for (x=conf->includes;x;x=x->next) {
485                 if (strcmp(x->included_file,included_file) == 0)
486                         return x;
487         }
488         return 0;
489 }
490
491
492 void ast_variable_append(struct ast_category *category, struct ast_variable *variable)
493 {
494         if (!variable)
495                 return;
496         if (category->last)
497                 category->last->next = variable;
498         else
499                 category->root = variable;
500         category->last = variable;
501         while (category->last->next)
502                 category->last = category->last->next;
503 }
504
505 void ast_variable_insert(struct ast_category *category, struct ast_variable *variable, const char *line)
506 {
507         struct ast_variable *cur = category->root;
508         int lineno;
509         int insertline;
510
511         if (!variable || sscanf(line, "%30d", &insertline) != 1) {
512                 return;
513         }
514         if (!insertline) {
515                 variable->next = category->root;
516                 category->root = variable;
517         } else {
518                 for (lineno = 1; lineno < insertline; lineno++) {
519                         cur = cur->next;
520                         if (!cur->next) {
521                                 break;
522                         }
523                 }
524                 variable->next = cur->next;
525                 cur->next = variable;
526         }
527 }
528
529 static void ast_comment_destroy(struct ast_comment **comment)
530 {
531         struct ast_comment *n, *p;
532
533         for (p = *comment; p; p = n) {
534                 n = p->next;
535                 ast_free(p);
536         }
537
538         *comment = NULL;
539 }
540
541 static void ast_variable_destroy(struct ast_variable *doomed)
542 {
543         ast_comment_destroy(&doomed->precomments);
544         ast_comment_destroy(&doomed->sameline);
545         ast_comment_destroy(&doomed->trailing);
546         ast_free(doomed);
547 }
548
549 struct ast_variable *ast_variables_dup(struct ast_variable *var)
550 {
551         struct ast_variable *cloned;
552         struct ast_variable *tmp;
553
554         if (!(cloned = ast_variable_new(var->name, var->value, var->file))) {
555                 return NULL;
556         }
557
558         tmp = cloned;
559
560         while ((var = var->next)) {
561                 if (!(tmp->next = ast_variable_new(var->name, var->value, var->file))) {
562                         ast_variables_destroy(cloned);
563                         return NULL;
564                 }
565                 tmp = tmp->next;
566         }
567
568         return cloned;
569 }
570
571 struct ast_variable *ast_variables_reverse(struct ast_variable *var)
572 {
573         struct ast_variable *var1, *var2;
574
575         var1 = var;
576
577         if (!var1 || !var1->next) {
578                 return var1;
579         }
580
581         var2 = var1->next;
582         var1->next = NULL;
583
584         while (var2) {
585                 struct ast_variable *next = var2->next;
586
587                 var2->next = var1;
588                 var1 = var2;
589                 var2 = next;
590         }
591
592         return var1;
593 }
594
595 void ast_variables_destroy(struct ast_variable *v)
596 {
597         struct ast_variable *vn;
598
599         while (v) {
600                 vn = v;
601                 v = v->next;
602                 ast_variable_destroy(vn);
603         }
604 }
605
606 struct ast_variable *ast_variable_browse(const struct ast_config *config, const char *category)
607 {
608         struct ast_category *cat;
609
610         if (config->last_browse && (config->last_browse->name == category)) {
611                 cat = config->last_browse;
612         } else {
613                 cat = ast_category_get(config, category, NULL);
614         }
615
616         return (cat) ? cat->root : NULL;
617 }
618
619 static inline struct ast_variable *variable_list_switch(struct ast_variable *l1, struct ast_variable *l2)
620 {
621     l1->next = l2->next;
622     l2->next = l1;
623     return l2;
624 }
625
626 struct ast_variable *ast_variable_list_sort(struct ast_variable *start)
627 {
628         struct ast_variable *p, *q;
629         struct ast_variable top;
630         int changed = 1;
631         memset(&top, 0, sizeof(top));
632         top.next = start;
633         if (start != NULL && start->next != NULL) {
634                 while (changed) {
635                         changed = 0;
636                         q = &top;
637                         p = top.next;
638                         while (p->next != NULL) {
639                                 if (p->next != NULL && strcmp(p->name, p->next->name) > 0) {
640                                         q->next = variable_list_switch(p, p->next);
641                                         changed = 1;
642                                 }
643                                 q = p;
644                                 if (p->next != NULL)
645                                         p = p->next;
646                         }
647                 }
648         }
649         return top.next;
650 }
651
652 struct ast_variable *ast_variable_list_append_hint(struct ast_variable **head, struct ast_variable *search_hint, struct ast_variable *newvar)
653 {
654         struct ast_variable *curr;
655         ast_assert(head != NULL);
656
657         if (!*head) {
658                 *head = newvar;
659         } else {
660                 if (search_hint == NULL) {
661                         search_hint = *head;
662                 }
663                 for (curr = search_hint; curr->next; curr = curr->next);
664                 curr->next = newvar;
665         }
666
667         for (curr = newvar; curr->next; curr = curr->next);
668
669         return curr;
670 }
671
672 const char *ast_config_option(struct ast_config *cfg, const char *cat, const char *var)
673 {
674         const char *tmp;
675         tmp = ast_variable_retrieve(cfg, cat, var);
676         if (!tmp) {
677                 tmp = ast_variable_retrieve(cfg, "general", var);
678         }
679         return tmp;
680 }
681
682 const char *ast_variable_retrieve(struct ast_config *config, const char *category, const char *variable)
683 {
684         struct ast_variable *v;
685
686         if (category) {
687                 for (v = ast_variable_browse(config, category); v; v = v->next) {
688                         if (!strcasecmp(variable, v->name)) {
689                                 return v->value;
690                         }
691                 }
692         } else {
693                 struct ast_category *cat;
694
695                 for (cat = config->root; cat; cat = cat->next) {
696                         for (v = cat->root; v; v = v->next) {
697                                 if (!strcasecmp(variable, v->name)) {
698                                         return v->value;
699                                 }
700                         }
701                 }
702         }
703
704         return NULL;
705 }
706
707 const char *ast_variable_retrieve_filtered(struct ast_config *config,
708         const char *category, const char *variable, const char *filter)
709 {
710         struct ast_category *cat = NULL;
711         const char *value;
712
713         while ((cat = ast_category_browse_filtered(config, category, cat, filter))) {
714                 value = ast_variable_find(cat, variable);
715                 if (value) {
716                         return value;
717                 }
718         }
719
720         return NULL;
721 }
722
723 const char *ast_variable_find(const struct ast_category *category, const char *variable)
724 {
725         return ast_variable_find_in_list(category->root, variable);
726 }
727
728 const struct ast_variable *ast_variable_find_variable_in_list(const struct ast_variable *list, const char *variable_name)
729 {
730         const struct ast_variable *v;
731
732         for (v = list; v; v = v->next) {
733                 if (!strcasecmp(variable_name, v->name)) {
734                         return v;
735                 }
736         }
737         return NULL;
738 }
739
740 int ast_variables_match(const struct ast_variable *left, const struct ast_variable *right)
741 {
742         char *op;
743
744         if (left == right) {
745                 return 1;
746         }
747
748         if (!(left && right)) {
749                 return 0;
750         }
751
752         op = strrchr(right->name, ' ');
753         if (op) {
754                 op++;
755         }
756
757         return ast_strings_match(left->value, op ? ast_strdupa(op) : NULL, right->value);
758 }
759
760 int ast_variable_lists_match(const struct ast_variable *left, const struct ast_variable *right, int exact_match)
761 {
762         const struct ast_variable *field;
763         int right_count = 0;
764         int left_count = 0;
765
766         if (left == right) {
767                 return 1;
768         }
769
770         if (!(left && right)) {
771                 return 0;
772         }
773
774         for (field = right; field; field = field->next) {
775                 char *space = strrchr(field->name, ' ');
776                 const struct ast_variable *old;
777                 char * name = (char *)field->name;
778
779                 if (space) {
780                         name = ast_strdup(field->name);
781                         if (!name) {
782                                 return 0;
783                         }
784                         name[space - field->name] = '\0';
785                 }
786
787                 old = ast_variable_find_variable_in_list(left, name);
788                 if (name != field->name) {
789                         ast_free(name);
790                 }
791
792                 if (exact_match) {
793                         if (!old || strcmp(old->value, field->value)) {
794                                 return 0;
795                         }
796                 } else {
797                         if (!ast_variables_match(old, field)) {
798                                 return 0;
799                         }
800                 }
801
802                 right_count++;
803         }
804
805         if (exact_match) {
806                 for (field = left; field; field = field->next) {
807                         left_count++;
808                 }
809
810                 if (right_count != left_count) {
811                         return 0;
812                 }
813         }
814
815         return 1;
816 }
817
818 const char *ast_variable_find_in_list(const struct ast_variable *list, const char *variable)
819 {
820         const struct ast_variable *v;
821
822         for (v = list; v; v = v->next) {
823                 if (!strcasecmp(variable, v->name)) {
824                         return v->value;
825                 }
826         }
827         return NULL;
828 }
829
830 const char *ast_variable_find_last_in_list(const struct ast_variable *list, const char *variable)
831 {
832         const struct ast_variable *v;
833         const char *found = NULL;
834
835         for (v = list; v; v = v->next) {
836                 if (!strcasecmp(variable, v->name)) {
837                         found = v->value;
838                 }
839         }
840         return found;
841 }
842
843 static struct ast_variable *variable_clone(const struct ast_variable *old)
844 {
845         struct ast_variable *new = ast_variable_new(old->name, old->value, old->file);
846
847         if (new) {
848                 new->lineno = old->lineno;
849                 new->object = old->object;
850                 new->blanklines = old->blanklines;
851                 /* TODO: clone comments? */
852         }
853
854         return new;
855 }
856
857 static void move_variables(struct ast_category *old, struct ast_category *new)
858 {
859         struct ast_variable *var = old->root;
860
861         old->root = NULL;
862         /* we can just move the entire list in a single op */
863         ast_variable_append(new, var);
864 }
865
866 /*! \brief Returns true if ALL of the regex expressions and category name match.
867  * Both can be NULL (I.E. no predicate) which results in a true return;
868  */
869 static int does_category_match(struct ast_category *cat, const char *category_name,
870         const char *match, char sep)
871 {
872         char *dupmatch;
873         char *nvp = NULL;
874         int match_found = 0, match_expressions = 0;
875         int template_ok = 0;
876
877         /* Only match on category name if it's not a NULL or empty string */
878         if (!ast_strlen_zero(category_name) && strcasecmp(cat->name, category_name)) {
879                 return 0;
880         }
881
882         /* If match is NULL or empty, automatically match if not a template */
883         if (ast_strlen_zero(match)) {
884                 return !cat->ignored;
885         }
886
887         dupmatch = ast_strdupa(match);
888
889         while ((nvp = ast_strsep(&dupmatch, sep, AST_STRSEP_STRIP))) {
890                 struct ast_variable *v;
891                 char *match_name;
892                 char *match_value = NULL;
893                 char *regerr;
894                 int rc;
895                 regex_t r_name, r_value;
896
897                 match_expressions++;
898
899                 match_name = ast_strsep(&nvp, '=', AST_STRSEP_STRIP);
900                 match_value = ast_strsep(&nvp, '=', AST_STRSEP_STRIP);
901
902                 /* an empty match value is OK.  A NULL match value (no =) is NOT. */
903                 if (match_value == NULL) {
904                         break;
905                 }
906
907                 if (!strcmp("TEMPLATES", match_name)) {
908                         if (!strcasecmp("include", match_value)) {
909                                 if (cat->ignored) {
910                                         template_ok = 1;
911                                 }
912                                 match_found++;
913                         } else if (!strcasecmp("restrict", match_value)) {
914                                 if (cat->ignored) {
915                                         match_found++;
916                                         template_ok = 1;
917                                 } else {
918                                         break;
919                                 }
920                         }
921                         continue;
922                 }
923
924                 if ((rc = regcomp(&r_name, match_name, REG_EXTENDED | REG_NOSUB))) {
925                         regerr = ast_alloca(128);
926                         regerror(rc, &r_name, regerr, 128);
927                         ast_log(LOG_ERROR, "Regular expression '%s' failed to compile: %s\n",
928                                 match_name, regerr);
929                         regfree(&r_name);
930                         return 0;
931                 }
932                 if ((rc = regcomp(&r_value, match_value, REG_EXTENDED | REG_NOSUB))) {
933                         regerr = ast_alloca(128);
934                         regerror(rc, &r_value, regerr, 128);
935                         ast_log(LOG_ERROR, "Regular expression '%s' failed to compile: %s\n",
936                                 match_value, regerr);
937                         regfree(&r_name);
938                         regfree(&r_value);
939                         return 0;
940                 }
941
942                 for (v = cat->root; v; v = v->next) {
943                         if (!regexec(&r_name, v->name, 0, NULL, 0)
944                                 && !regexec(&r_value, v->value, 0, NULL, 0)) {
945                                 match_found++;
946                                 break;
947                         }
948                 }
949                 regfree(&r_name);
950                 regfree(&r_value);
951         }
952         if (match_found == match_expressions && (!cat->ignored || template_ok)) {
953                 return 1;
954         }
955         return 0;
956 }
957
958
959 static struct ast_category *new_category(const char *name, const char *in_file, int lineno, int template)
960 {
961         struct ast_category *category;
962
963         category = ast_calloc(1, sizeof(*category));
964         if (!category) {
965                 return NULL;
966         }
967         category->file = ast_strdup(in_file);
968         if (!category->file) {
969                 ast_category_destroy(category);
970                 return NULL;
971         }
972         ast_copy_string(category->name, name, sizeof(category->name));
973         category->lineno = lineno; /* if you don't know the lineno, set it to 999999 or something real big */
974         category->ignored = template;
975         return category;
976 }
977
978 struct ast_category *ast_category_new(const char *name, const char *in_file, int lineno)
979 {
980         return new_category(name, in_file, lineno, 0);
981 }
982
983 struct ast_category *ast_category_new_template(const char *name, const char *in_file, int lineno)
984 {
985         return new_category(name, in_file, lineno, 1);
986 }
987
988 static struct ast_category *category_get_sep(const struct ast_config *config,
989         const char *category_name, const char *filter, char sep)
990 {
991         struct ast_category *cat;
992
993         for (cat = config->root; cat; cat = cat->next) {
994                 if (cat->name == category_name && does_category_match(cat, category_name, filter, sep)) {
995                         return cat;
996                 }
997         }
998
999         for (cat = config->root; cat; cat = cat->next) {
1000                 if (does_category_match(cat, category_name, filter, sep)) {
1001                         return cat;
1002                 }
1003         }
1004
1005         return NULL;
1006 }
1007
1008 struct ast_category *ast_category_get(const struct ast_config *config,
1009         const char *category_name, const char *filter)
1010 {
1011         return category_get_sep(config, category_name, filter, ',');
1012 }
1013
1014 const char *ast_category_get_name(const struct ast_category *category)
1015 {
1016         return category->name;
1017 }
1018
1019 int ast_category_is_template(const struct ast_category *category)
1020 {
1021         return category->ignored;
1022 }
1023
1024 struct ast_str *ast_category_get_templates(const struct ast_category *category)
1025 {
1026         struct ast_category_template_instance *template;
1027         struct ast_str *str;
1028         int first = 1;
1029
1030         if (AST_LIST_EMPTY(&category->template_instances)) {
1031                 return NULL;
1032         }
1033
1034         str = ast_str_create(128);
1035         if (!str) {
1036                 return NULL;
1037         }
1038
1039         AST_LIST_TRAVERSE(&category->template_instances, template, next) {
1040                 ast_str_append(&str, 0, "%s%s", first ? "" : ",", template->name);
1041                 first = 0;
1042         }
1043
1044         return str;
1045 }
1046
1047 int ast_category_exist(const struct ast_config *config, const char *category_name,
1048         const char *filter)
1049 {
1050         return !!ast_category_get(config, category_name, filter);
1051 }
1052
1053 void ast_category_append(struct ast_config *config, struct ast_category *category)
1054 {
1055         if (config->last) {
1056                 config->last->next = category;
1057                 category->prev = config->last;
1058         } else {
1059                 config->root = category;
1060                 category->prev = NULL;
1061         }
1062         category->next = NULL;
1063         category->include_level = config->include_level;
1064
1065         config->last = category;
1066         config->current = category;
1067 }
1068
1069 int ast_category_insert(struct ast_config *config, struct ast_category *cat, const char *match)
1070 {
1071         struct ast_category *cur_category;
1072
1073         if (!config || !config->root || !cat || !match) {
1074                 return -1;
1075         }
1076
1077         if (!strcasecmp(config->root->name, match)) {
1078                 cat->next = config->root;
1079                 cat->prev = NULL;
1080                 config->root->prev = cat;
1081                 config->root = cat;
1082                 return 0;
1083         }
1084
1085         for (cur_category = config->root->next; cur_category; cur_category = cur_category->next) {
1086                 if (!strcasecmp(cur_category->name, match)) {
1087                         cat->prev = cur_category->prev;
1088                         cat->prev->next = cat;
1089
1090                         cat->next = cur_category;
1091                         cur_category->prev = cat;
1092
1093                         return 0;
1094                 }
1095         }
1096
1097         return -1;
1098 }
1099
1100 static void ast_destroy_template_list(struct ast_category *cat)
1101 {
1102         struct ast_category_template_instance *x;
1103
1104         while ((x = AST_LIST_REMOVE_HEAD(&cat->template_instances, next)))
1105                 ast_free(x);
1106 }
1107
1108 void ast_category_destroy(struct ast_category *cat)
1109 {
1110         ast_variables_destroy(cat->root);
1111         cat->root = NULL;
1112         cat->last = NULL;
1113         ast_comment_destroy(&cat->precomments);
1114         ast_comment_destroy(&cat->sameline);
1115         ast_comment_destroy(&cat->trailing);
1116         ast_destroy_template_list(cat);
1117         ast_free(cat->file);
1118         ast_free(cat);
1119 }
1120
1121 static void ast_includes_destroy(struct ast_config_include *incls)
1122 {
1123         struct ast_config_include *incl,*inclnext;
1124
1125         for (incl=incls; incl; incl = inclnext) {
1126                 inclnext = incl->next;
1127                 ast_free(incl->include_location_file);
1128                 ast_free(incl->exec_file);
1129                 ast_free(incl->included_file);
1130                 ast_free(incl);
1131         }
1132 }
1133
1134 static struct ast_category *next_available_category(struct ast_category *cat,
1135         const char *name, const char *filter)
1136 {
1137         for (; cat && !does_category_match(cat, name, filter, ','); cat = cat->next);
1138
1139         return cat;
1140 }
1141
1142 /*! return the first var of a category */
1143 struct ast_variable *ast_category_first(struct ast_category *cat)
1144 {
1145         return (cat) ? cat->root : NULL;
1146 }
1147
1148 struct ast_variable *ast_category_root(struct ast_config *config, char *cat)
1149 {
1150         struct ast_category *category = ast_category_get(config, cat, NULL);
1151
1152         if (category)
1153                 return category->root;
1154         return NULL;
1155 }
1156
1157 void ast_config_sort_categories(struct ast_config *config, int descending,
1158                                                                 int (*comparator)(struct ast_category *p, struct ast_category *q))
1159 {
1160         /*
1161          * The contents of this function are adapted from
1162          * an example of linked list merge sorting
1163          * copyright 2001 Simon Tatham.
1164          *
1165          * Permission is hereby granted, free of charge, to any person
1166          * obtaining a copy of this software and associated documentation
1167          * files (the "Software"), to deal in the Software without
1168          * restriction, including without limitation the rights to use,
1169          * copy, modify, merge, publish, distribute, sublicense, and/or
1170          * sell copies of the Software, and to permit persons to whom the
1171          * Software is furnished to do so, subject to the following
1172          * conditions:
1173          *
1174          * The above copyright notice and this permission notice shall be
1175          * included in all copies or substantial portions of the Software.
1176          *
1177          * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
1178          * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
1179          * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
1180          * NONINFRINGEMENT.  IN NO EVENT SHALL SIMON TATHAM BE LIABLE FOR
1181          * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
1182          * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
1183          * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
1184          * SOFTWARE.
1185          */
1186
1187         int insize = 1;
1188         struct ast_category *p, *q, *e, *tail;
1189         int nmerges, psize, qsize, i;
1190
1191         /* If the descending flag was sent, we'll apply inversion to the comparison function's return. */
1192         if (descending) {
1193                 descending = -1;
1194         } else {
1195                 descending = 1;
1196         }
1197
1198         if (!config->root) {
1199                 return;
1200         }
1201
1202         while (1) {
1203                 p = config->root;
1204                 config->root = NULL;
1205                 tail = NULL;
1206
1207                 nmerges = 0; /* count number of merges we do in this pass */
1208
1209                 while (p) {
1210                         nmerges++; /* there exists a merge to be done */
1211
1212                         /* step `insize' places along from p */
1213                         q = p;
1214                         psize = 0;
1215                         for (i = 0; i < insize; i++) {
1216                                 psize++;
1217                                 q = q->next;
1218                                 if (!q) {
1219                                         break;
1220                                 }
1221                         }
1222
1223                         /* if q hasn't fallen off end, we have two lists to merge */
1224                         qsize = insize;
1225
1226                         /* now we have two lists; merge them */
1227                         while (psize > 0 || (qsize > 0 && q)) {
1228                                 /* decide whether next element of merge comes from p or q */
1229                                 if (psize == 0) {
1230                                         /* p is empty; e must come from q. */
1231                                         e = q;
1232                                         q = q->next;
1233                                         qsize--;
1234                                 } else if (qsize == 0 || !q) {
1235                                         /* q is empty; e must come from p. */
1236                                         e = p; p = p->next; psize--;
1237                                 } else if ((comparator(p,q) * descending) <= 0) {
1238                                         /* First element of p is lower (or same) e must come from p. */
1239                                         e = p;
1240                                         p = p->next;
1241                                         psize--;
1242                                 } else {
1243                                         /* First element of q is lower; e must come from q. */
1244                                         e = q;
1245                                         q = q->next;
1246                                         qsize--;
1247                                 }
1248
1249                                 /* add the next element to the merged list */
1250                                 if (tail) {
1251                                         tail->next = e;
1252                                 } else {
1253                                         config->root = e;
1254                                 }
1255                                 tail = e;
1256                         }
1257
1258                         /* now p has stepped `insize' places along, and q has too */
1259                         p = q;
1260                 }
1261
1262                 tail->next = NULL;
1263
1264                 /* If we have done only one merge, we're finished. */
1265                 if (nmerges <= 1) { /* allow for nmerges==0, the empty list case */
1266                         return;
1267                 }
1268
1269                 /* Otherwise repeat, merging lists twice the size */
1270                 insize *= 2;
1271         }
1272
1273 }
1274
1275 char *ast_category_browse(struct ast_config *config, const char *prev)
1276 {
1277         struct ast_category *cat;
1278
1279         if (!prev) {
1280                 /* First time browse. */
1281                 cat = config->root;
1282         } else if (config->last_browse && (config->last_browse->name == prev)) {
1283                 /* Simple last browse found. */
1284                 cat = config->last_browse->next;
1285         } else {
1286                 /*
1287                  * Config changed since last browse.
1288                  *
1289                  * First try cheap last browse search. (Rebrowsing a different
1290                  * previous category?)
1291                  */
1292                 for (cat = config->root; cat; cat = cat->next) {
1293                         if (cat->name == prev) {
1294                                 /* Found it. */
1295                                 cat = cat->next;
1296                                 break;
1297                         }
1298                 }
1299                 if (!cat) {
1300                         /*
1301                          * Have to do it the hard way. (Last category was deleted and
1302                          * re-added?)
1303                          */
1304                         for (cat = config->root; cat; cat = cat->next) {
1305                                 if (!strcasecmp(cat->name, prev)) {
1306                                         /* Found it. */
1307                                         cat = cat->next;
1308                                         break;
1309                                 }
1310                         }
1311                 }
1312         }
1313
1314         if (cat)
1315                 cat = next_available_category(cat, NULL, NULL);
1316
1317         config->last_browse = cat;
1318         return (cat) ? cat->name : NULL;
1319 }
1320
1321 struct ast_category *ast_category_browse_filtered(struct ast_config *config,
1322         const char *category_name, struct ast_category *prev, const char *filter)
1323 {
1324         struct ast_category *cat;
1325
1326         if (!prev) {
1327                 prev = config->root;
1328         } else {
1329                 prev = prev->next;
1330         }
1331
1332         cat = next_available_category(prev, category_name, filter);
1333
1334         return cat;
1335 }
1336
1337 struct ast_variable *ast_category_detach_variables(struct ast_category *cat)
1338 {
1339         struct ast_variable *v;
1340
1341         v = cat->root;
1342         cat->root = NULL;
1343         cat->last = NULL;
1344
1345         return v;
1346 }
1347
1348 void ast_category_rename(struct ast_category *cat, const char *name)
1349 {
1350         ast_copy_string(cat->name, name, sizeof(cat->name));
1351 }
1352
1353 int ast_category_inherit(struct ast_category *new, const struct ast_category *base)
1354 {
1355         struct ast_variable *var;
1356         struct ast_category_template_instance *x;
1357
1358         x = ast_calloc(1, sizeof(*x));
1359         if (!x) {
1360                 return -1;
1361         }
1362         strcpy(x->name, base->name);
1363         x->inst = base;
1364         AST_LIST_INSERT_TAIL(&new->template_instances, x, next);
1365         for (var = base->root; var; var = var->next) {
1366                 struct ast_variable *cloned = variable_clone(var);
1367                 if (!cloned) {
1368                         return -1;
1369                 }
1370                 cloned->inherited = 1;
1371                 ast_variable_append(new, cloned);
1372         }
1373         return 0;
1374 }
1375
1376 struct ast_config *ast_config_new(void)
1377 {
1378         struct ast_config *config;
1379
1380         if ((config = ast_calloc(1, sizeof(*config))))
1381                 config->max_include_level = MAX_INCLUDE_LEVEL;
1382         return config;
1383 }
1384
1385 int ast_variable_delete(struct ast_category *category, const char *variable, const char *match, const char *line)
1386 {
1387         struct ast_variable *cur, *prev=NULL, *curn;
1388         int res = -1;
1389         int num_item = 0;
1390         int req_item;
1391
1392         req_item = -1;
1393         if (!ast_strlen_zero(line)) {
1394                 /* Requesting to delete by item number. */
1395                 if (sscanf(line, "%30d", &req_item) != 1
1396                         || req_item < 0) {
1397                         /* Invalid item number to delete. */
1398                         return -1;
1399                 }
1400         }
1401
1402         prev = NULL;
1403         cur = category->root;
1404         while (cur) {
1405                 curn = cur->next;
1406                 /* Delete by item number or by variable name with optional value. */
1407                 if ((0 <= req_item && num_item == req_item)
1408                         || (req_item < 0 && !strcasecmp(cur->name, variable)
1409                                 && (ast_strlen_zero(match) || !strcasecmp(cur->value, match)))) {
1410                         if (prev) {
1411                                 prev->next = cur->next;
1412                                 if (cur == category->last)
1413                                         category->last = prev;
1414                         } else {
1415                                 category->root = cur->next;
1416                                 if (cur == category->last)
1417                                         category->last = NULL;
1418                         }
1419                         ast_variable_destroy(cur);
1420                         res = 0;
1421                 } else
1422                         prev = cur;
1423
1424                 cur = curn;
1425                 ++num_item;
1426         }
1427         return res;
1428 }
1429
1430 int ast_variable_update(struct ast_category *category, const char *variable,
1431                                                 const char *value, const char *match, unsigned int object)
1432 {
1433         struct ast_variable *cur, *prev=NULL, *newer=NULL;
1434
1435         for (cur = category->root; cur; prev = cur, cur = cur->next) {
1436                 if (strcasecmp(cur->name, variable) ||
1437                         (!ast_strlen_zero(match) && strcasecmp(cur->value, match)))
1438                         continue;
1439
1440                 if (!(newer = ast_variable_new(variable, value, cur->file)))
1441                         return -1;
1442
1443                 ast_variable_move(newer, cur);
1444                 newer->object = newer->object || object;
1445
1446                 /* Replace the old node in the list with the new node. */
1447                 newer->next = cur->next;
1448                 if (prev)
1449                         prev->next = newer;
1450                 else
1451                         category->root = newer;
1452                 if (category->last == cur)
1453                         category->last = newer;
1454
1455                 ast_variable_destroy(cur);
1456
1457                 return 0;
1458         }
1459
1460         /* Could not find variable to update */
1461         return -1;
1462 }
1463
1464 struct ast_category *ast_category_delete(struct ast_config *config,
1465         struct ast_category *category)
1466 {
1467         struct ast_category *prev;
1468
1469         if (!config || !category) {
1470                 return NULL;
1471         }
1472
1473         if (category->prev) {
1474                 category->prev->next = category->next;
1475         } else {
1476                 config->root = category->next;
1477         }
1478
1479         if (category->next) {
1480                 category->next->prev = category->prev;
1481         } else {
1482                 config->last = category->prev;
1483         }
1484
1485         prev = category->prev;
1486
1487         if (config->last_browse == category) {
1488                 config->last_browse = prev;
1489         }
1490
1491         ast_category_destroy(category);
1492
1493         return prev;
1494 }
1495
1496 int ast_category_empty(struct ast_category *category)
1497 {
1498         if (!category) {
1499                 return -1;
1500         }
1501
1502         ast_variables_destroy(category->root);
1503         category->root = NULL;
1504         category->last = NULL;
1505
1506         return 0;
1507 }
1508
1509 void ast_config_destroy(struct ast_config *cfg)
1510 {
1511         struct ast_category *cat, *catn;
1512
1513         if (!cfg)
1514                 return;
1515
1516         ast_includes_destroy(cfg->includes);
1517
1518         cat = cfg->root;
1519         while (cat) {
1520                 catn = cat;
1521                 cat = cat->next;
1522                 ast_category_destroy(catn);
1523         }
1524         ast_free(cfg);
1525 }
1526
1527 struct ast_category *ast_config_get_current_category(const struct ast_config *cfg)
1528 {
1529         return cfg->current;
1530 }
1531
1532 void ast_config_set_current_category(struct ast_config *cfg, const struct ast_category *cat)
1533 {
1534         /* cast below is just to silence compiler warning about dropping "const" */
1535         cfg->current = (struct ast_category *) cat;
1536 }
1537
1538 /*!
1539  * \internal
1540  * \brief Create a new cfmtime list node.
1541  *
1542  * \param filename Config filename caching.
1543  * \param who_asked Who wanted to know.
1544  *
1545  * \retval cfmtime New node on success.
1546  * \retval NULL on error.
1547  */
1548 static struct cache_file_mtime *cfmtime_new(const char *filename, const char *who_asked)
1549 {
1550         struct cache_file_mtime *cfmtime;
1551         char *dst;
1552
1553         cfmtime = ast_calloc(1,
1554                 sizeof(*cfmtime) + strlen(filename) + 1 + strlen(who_asked) + 1);
1555         if (!cfmtime) {
1556                 return NULL;
1557         }
1558         dst = cfmtime->filename;        /* writable space starts here */
1559         strcpy(dst, filename); /* Safe */
1560         dst += strlen(dst) + 1;
1561         cfmtime->who_asked = strcpy(dst, who_asked); /* Safe */
1562
1563         return cfmtime;
1564 }
1565
1566 enum config_cache_attribute_enum {
1567         ATTRIBUTE_INCLUDE = 0,
1568         ATTRIBUTE_EXEC = 1,
1569 };
1570
1571 /*!
1572  * \internal
1573  * \brief Save the stat() data to the cached file modtime struct.
1574  *
1575  * \param cfmtime Cached file modtime.
1576  * \param statbuf Buffer filled in by stat().
1577  *
1578  * \return Nothing
1579  */
1580 static void cfmstat_save(struct cache_file_mtime *cfmtime, struct stat *statbuf)
1581 {
1582         cfmtime->stat_size = statbuf->st_size;
1583 #if defined(HAVE_STRUCT_STAT_ST_MTIM)
1584         cfmtime->stat_mtime_nsec = statbuf->st_mtim.tv_nsec;
1585 #elif defined(HAVE_STRUCT_STAT_ST_MTIMENSEC)
1586         cfmtime->stat_mtime_nsec = statbuf->st_mtimensec;
1587 #elif defined(HAVE_STRUCT_STAT_ST_MTIMESPEC)
1588         cfmtime->stat_mtime_nsec = statbuf->st_mtimespec.tv_nsec;
1589 #else
1590         cfmtime->stat_mtime_nsec = 0;
1591 #endif
1592         cfmtime->stat_mtime = statbuf->st_mtime;
1593 }
1594
1595 /*!
1596  * \internal
1597  * \brief Compare the stat() data with the cached file modtime struct.
1598  *
1599  * \param cfmtime Cached file modtime.
1600  * \param statbuf Buffer filled in by stat().
1601  *
1602  * \retval non-zero if different.
1603  */
1604 static int cfmstat_cmp(struct cache_file_mtime *cfmtime, struct stat *statbuf)
1605 {
1606         struct cache_file_mtime cfm_buf;
1607
1608         cfmstat_save(&cfm_buf, statbuf);
1609
1610         return cfmtime->stat_size != cfm_buf.stat_size
1611                 || cfmtime->stat_mtime != cfm_buf.stat_mtime
1612                 || cfmtime->stat_mtime_nsec != cfm_buf.stat_mtime_nsec;
1613 }
1614
1615 /*!
1616  * \internal
1617  * \brief Clear the cached file modtime include list.
1618  *
1619  * \param cfmtime Cached file modtime.
1620  *
1621  * \note cfmtime_head is assumed already locked.
1622  *
1623  * \return Nothing
1624  */
1625 static void config_cache_flush_includes(struct cache_file_mtime *cfmtime)
1626 {
1627         struct cache_file_include *cfinclude;
1628
1629         while ((cfinclude = AST_LIST_REMOVE_HEAD(&cfmtime->includes, list))) {
1630                 ast_free(cfinclude);
1631         }
1632 }
1633
1634 /*!
1635  * \internal
1636  * \brief Destroy the given cached file modtime entry.
1637  *
1638  * \param cfmtime Cached file modtime.
1639  *
1640  * \note cfmtime_head is assumed already locked.
1641  *
1642  * \return Nothing
1643  */
1644 static void config_cache_destroy_entry(struct cache_file_mtime *cfmtime)
1645 {
1646         config_cache_flush_includes(cfmtime);
1647         ast_free(cfmtime);
1648 }
1649
1650 /*!
1651  * \internal
1652  * \brief Remove and destroy the config cache entry for the filename and who_asked.
1653  *
1654  * \param filename Config filename.
1655  * \param who_asked Which module asked.
1656  *
1657  * \return Nothing
1658  */
1659 static void config_cache_remove(const char *filename, const char *who_asked)
1660 {
1661         struct cache_file_mtime *cfmtime;
1662
1663         AST_LIST_LOCK(&cfmtime_head);
1664         AST_LIST_TRAVERSE_SAFE_BEGIN(&cfmtime_head, cfmtime, list) {
1665                 if (!strcmp(cfmtime->filename, filename)
1666                         && !strcmp(cfmtime->who_asked, who_asked)) {
1667                         AST_LIST_REMOVE_CURRENT(list);
1668                         config_cache_destroy_entry(cfmtime);
1669                         break;
1670                 }
1671         }
1672         AST_LIST_TRAVERSE_SAFE_END;
1673         AST_LIST_UNLOCK(&cfmtime_head);
1674 }
1675
1676 static void config_cache_attribute(const char *configfile, enum config_cache_attribute_enum attrtype, const char *filename, const char *who_asked)
1677 {
1678         struct cache_file_mtime *cfmtime;
1679         struct cache_file_include *cfinclude;
1680
1681         /* Find our cached entry for this configuration file */
1682         AST_LIST_LOCK(&cfmtime_head);
1683         AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
1684                 if (!strcmp(cfmtime->filename, configfile) && !strcmp(cfmtime->who_asked, who_asked))
1685                         break;
1686         }
1687         if (!cfmtime) {
1688                 cfmtime = cfmtime_new(configfile, who_asked);
1689                 if (!cfmtime) {
1690                         AST_LIST_UNLOCK(&cfmtime_head);
1691                         return;
1692                 }
1693                 /* Note that the file mtime is initialized to 0, i.e. 1970 */
1694                 AST_LIST_INSERT_SORTALPHA(&cfmtime_head, cfmtime, list, filename);
1695         }
1696
1697         switch (attrtype) {
1698         case ATTRIBUTE_INCLUDE:
1699                 AST_LIST_TRAVERSE(&cfmtime->includes, cfinclude, list) {
1700                         if (!strcmp(cfinclude->include, filename)) {
1701                                 AST_LIST_UNLOCK(&cfmtime_head);
1702                                 return;
1703                         }
1704                 }
1705                 cfinclude = ast_calloc(1, sizeof(*cfinclude) + strlen(filename) + 1);
1706                 if (!cfinclude) {
1707                         AST_LIST_UNLOCK(&cfmtime_head);
1708                         return;
1709                 }
1710                 strcpy(cfinclude->include, filename); /* Safe */
1711                 AST_LIST_INSERT_TAIL(&cfmtime->includes, cfinclude, list);
1712                 break;
1713         case ATTRIBUTE_EXEC:
1714                 cfmtime->has_exec = 1;
1715                 break;
1716         }
1717         AST_LIST_UNLOCK(&cfmtime_head);
1718 }
1719
1720 /*! \brief parse one line in the configuration.
1721  * \verbatim
1722  * We can have a category header        [foo](...)
1723  * a directive                          #include / #exec
1724  * or a regular line                    name = value
1725  * \endverbatim
1726  */
1727 static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
1728         char *buf, int lineno, const char *configfile, struct ast_flags flags,
1729         struct ast_str *comment_buffer,
1730         struct ast_str *lline_buffer,
1731         const char *suggested_include_file,
1732         struct ast_category **last_cat, struct ast_variable **last_var, const char *who_asked)
1733 {
1734         char *c;
1735         char *cur = buf;
1736         struct ast_variable *v;
1737         char cmd[512], exec_file[512];
1738
1739         /* Actually parse the entry */
1740         if (cur[0] == '[') { /* A category header */
1741                 /* format is one of the following:
1742                  * [foo]        define a new category named 'foo'
1743                  * [foo](!)     define a new template category named 'foo'
1744                  * [foo](+)     append to category 'foo', error if foo does not exist.
1745                  * [foo](a)     define a new category and inherit from category or template a.
1746                  *              You can put a comma-separated list of categories and templates
1747                  *              and '!' and '+' between parentheses, with obvious meaning.
1748                  */
1749                 struct ast_category *newcat;
1750                 char *catname;
1751
1752                 c = strchr(cur, ']');
1753                 if (!c) {
1754                         ast_log(LOG_WARNING, "parse error: no closing ']', line %d of %s\n", lineno, configfile);
1755                         return -1;
1756                 }
1757                 *c++ = '\0';
1758                 cur++;
1759                 if (*c++ != '(')
1760                         c = NULL;
1761                 catname = cur;
1762                 *cat = newcat = ast_category_new(catname,
1763                         S_OR(suggested_include_file, cfg->include_level == 1 ? "" : configfile),
1764                         lineno);
1765                 if (!newcat) {
1766                         return -1;
1767                 }
1768                 (*cat)->lineno = lineno;
1769
1770                 /* add comments */
1771                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1772                         newcat->precomments = ALLOC_COMMENT(comment_buffer);
1773                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1774                         newcat->sameline = ALLOC_COMMENT(lline_buffer);
1775                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1776                         CB_RESET(comment_buffer, lline_buffer);
1777
1778                 /* If there are options or categories to inherit from, process them now */
1779                 if (c) {
1780                         if (!(cur = strchr(c, ')'))) {
1781                                 ast_category_destroy(newcat);
1782                                 ast_log(LOG_WARNING, "parse error: no closing ')', line %d of %s\n", lineno, configfile);
1783                                 return -1;
1784                         }
1785                         *cur = '\0';
1786                         while ((cur = strsep(&c, ","))) {
1787                                 if (!strcasecmp(cur, "!")) {
1788                                         (*cat)->ignored = 1;
1789                                 } else if (cur[0] == '+') {
1790                                         char *filter = NULL;
1791
1792                                         if (cur[1] != ',') {
1793                                                 filter = &cur[1];
1794                                         }
1795                                         *cat = category_get_sep(cfg, catname, filter, '&');
1796                                         if (!(*cat)) {
1797                                                 if (newcat) {
1798                                                         ast_category_destroy(newcat);
1799                                                 }
1800                                                 ast_log(LOG_WARNING, "Category addition requested, but category '%s' does not exist, line %d of %s\n", catname, lineno, configfile);
1801                                                 return -1;
1802                                         }
1803                                         if (newcat) {
1804                                                 ast_config_set_current_category(cfg, *cat);
1805                                                 (*cat)->ignored |= newcat->ignored;
1806                                                 move_variables(newcat, *cat);
1807                                                 ast_category_destroy(newcat);
1808                                                 newcat = NULL;
1809                                         }
1810                                 } else {
1811                                         struct ast_category *base;
1812
1813                                         base = ast_category_get(cfg, cur, "TEMPLATES=include");
1814                                         if (!base) {
1815                                                 if (newcat) {
1816                                                         ast_category_destroy(newcat);
1817                                                 }
1818                                                 ast_log(LOG_WARNING, "Inheritance requested, but category '%s' does not exist, line %d of %s\n", cur, lineno, configfile);
1819                                                 return -1;
1820                                         }
1821                                         if (ast_category_inherit(*cat, base)) {
1822                                                 if (newcat) {
1823                                                         ast_category_destroy(newcat);
1824                                                 }
1825                                                 ast_log(LOG_ERROR, "Inheritence requested, but allocation failed\n");
1826                                                 return -1;
1827                                         }
1828                                 }
1829                         }
1830                 }
1831
1832                 /*
1833                  * We need to set *last_cat to newcat here regardless.  If the
1834                  * category is being appended to we have no place for trailing
1835                  * comments on the appended category.  The appended category
1836                  * may be in another file or it already has trailing comments
1837                  * that we would then leak.
1838                  */
1839                 *last_var = NULL;
1840                 *last_cat = newcat;
1841                 if (newcat) {
1842                         ast_category_append(cfg, newcat);
1843                 }
1844         } else if (cur[0] == '#') { /* A directive - #include or #exec */
1845                 char *cur2;
1846                 char real_inclusion_name[256];
1847                 int do_include = 0;     /* otherwise, it is exec */
1848                 int try_include = 0;
1849
1850                 cur++;
1851                 c = cur;
1852                 while (*c && (*c > 32)) {
1853                         c++;
1854                 }
1855
1856                 if (*c) {
1857                         *c = '\0';
1858                         /* Find real argument */
1859                         c = ast_strip(c + 1);
1860                         if (!(*c)) {
1861                                 c = NULL;
1862                         }
1863                 } else {
1864                         c = NULL;
1865                 }
1866                 if (!strcasecmp(cur, "include")) {
1867                         do_include = 1;
1868                 } else if (!strcasecmp(cur, "tryinclude")) {
1869                         do_include = 1;
1870                         try_include = 1;
1871                 } else if (!strcasecmp(cur, "exec")) {
1872                         if (!ast_opt_exec_includes) {
1873                                 ast_log(LOG_WARNING, "Cannot perform #exec unless execincludes option is enabled in asterisk.conf (options section)!\n");
1874                                 return 0;       /* XXX is this correct ? or we should return -1 ? */
1875                         }
1876                 } else {
1877                         ast_log(LOG_WARNING, "Unknown directive '#%s' at line %d of %s\n", cur, lineno, configfile);
1878                         return 0;       /* XXX is this correct ? or we should return -1 ? */
1879                 }
1880
1881                 if (c == NULL) {
1882                         ast_log(LOG_WARNING, "Directive '#%s' needs an argument (%s) at line %d of %s\n",
1883                                         do_include ? "include / tryinclude" : "exec",
1884                                         do_include ? "filename" : "/path/to/executable",
1885                                         lineno,
1886                                         configfile);
1887                         return 0;       /* XXX is this correct ? or we should return -1 ? */
1888                 }
1889
1890                 cur = c;
1891                 /* Strip off leading and trailing "'s and <>'s */
1892                 /* Dequote */
1893                 if ((*c == '"') || (*c == '<')) {
1894                         char quote_char = *c;
1895                         if (quote_char == '<') {
1896                                 quote_char = '>';
1897                         }
1898
1899                         if (*(c + strlen(c) - 1) == quote_char) {
1900                                 cur++;
1901                                 *(c + strlen(c) - 1) = '\0';
1902                         }
1903                 }
1904                 cur2 = cur;
1905
1906                 /* #exec </path/to/executable>
1907                    We create a tmp file, then we #include it, then we delete it. */
1908                 if (!do_include) {
1909                         struct timeval now = ast_tvnow();
1910                         if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
1911                                 config_cache_attribute(configfile, ATTRIBUTE_EXEC, NULL, who_asked);
1912                         snprintf(exec_file, sizeof(exec_file), "/var/tmp/exec.%d%d.%ld", (int)now.tv_sec, (int)now.tv_usec, (long)pthread_self());
1913                         snprintf(cmd, sizeof(cmd), "%s > %s 2>&1", cur, exec_file);
1914                         ast_safe_system(cmd);
1915                         cur = exec_file;
1916                 } else {
1917                         if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
1918                                 config_cache_attribute(configfile, ATTRIBUTE_INCLUDE, cur, who_asked);
1919                         exec_file[0] = '\0';
1920                 }
1921                 /* A #include */
1922                 /* record this inclusion */
1923                 ast_include_new(cfg, cfg->include_level == 1 ? "" : configfile, cur, !do_include, cur2, lineno, real_inclusion_name, sizeof(real_inclusion_name));
1924
1925                 do_include = ast_config_internal_load(cur, cfg, flags, real_inclusion_name, who_asked) ? 1 : 0;
1926                 if (!ast_strlen_zero(exec_file))
1927                         unlink(exec_file);
1928                 if (!do_include && !try_include) {
1929                         ast_log(LOG_ERROR, "The file '%s' was listed as a #include but it does not exist.\n", cur);
1930                         return -1;
1931                 }
1932                 /* XXX otherwise what ? the default return is 0 anyways */
1933
1934         } else {
1935                 /* Just a line (variable = value) */
1936                 int object = 0;
1937                 int is_escaped;
1938
1939                 if (!(*cat)) {
1940                         ast_log(LOG_WARNING,
1941                                 "parse error: No category context for line %d of %s\n", lineno, configfile);
1942                         return -1;
1943                 }
1944
1945                 is_escaped = cur[0] == '\\';
1946                 if (is_escaped) {
1947                         /* First character is escaped. */
1948                         ++cur;
1949                         if (cur[0] < 33) {
1950                                 ast_log(LOG_ERROR, "Invalid escape in line %d of %s\n", lineno, configfile);
1951                                 return -1;
1952                         }
1953                 }
1954                 c = strchr(cur + is_escaped, '=');
1955
1956                 if (c && c > cur + is_escaped && (*(c - 1) == '+')) {
1957                         struct ast_variable *var, *replace = NULL;
1958                         struct ast_str **str = ast_threadstorage_get(&appendbuf, sizeof(*str));
1959
1960                         if (!str || !*str) {
1961                                 return -1;
1962                         }
1963
1964                         *(c - 1) = '\0';
1965                         c++;
1966                         cur = ast_strip(cur);
1967
1968                         /* Must iterate through category until we find last variable of same name (since there could be multiple) */
1969                         for (var = ast_category_first(*cat); var; var = var->next) {
1970                                 if (!strcmp(var->name, cur)) {
1971                                         replace = var;
1972                                 }
1973                         }
1974
1975                         if (!replace) {
1976                                 /* Nothing to replace; just set a variable normally. */
1977                                 goto set_new_variable;
1978                         }
1979
1980                         ast_str_set(str, 0, "%s", replace->value);
1981                         ast_str_append(str, 0, "%s", c);
1982                         ast_str_trim_blanks(*str);
1983                         ast_variable_update(*cat, replace->name, ast_skip_blanks(ast_str_buffer(*str)), replace->value, object);
1984                 } else if (c) {
1985                         *c = 0;
1986                         c++;
1987                         /* Ignore > in => */
1988                         if (*c== '>') {
1989                                 object = 1;
1990                                 c++;
1991                         }
1992                         cur = ast_strip(cur);
1993 set_new_variable:
1994                         if (ast_strlen_zero(cur)) {
1995                                 ast_log(LOG_WARNING, "No variable name in line %d of %s\n", lineno, configfile);
1996                         } else if ((v = ast_variable_new(cur, ast_strip(c), S_OR(suggested_include_file, cfg->include_level == 1 ? "" : configfile)))) {
1997                                 v->lineno = lineno;
1998                                 v->object = object;
1999                                 *last_cat = NULL;
2000                                 *last_var = v;
2001                                 /* Put and reset comments */
2002                                 v->blanklines = 0;
2003                                 ast_variable_append(*cat, v);
2004                                 /* add comments */
2005                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
2006                                         v->precomments = ALLOC_COMMENT(comment_buffer);
2007                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
2008                                         v->sameline = ALLOC_COMMENT(lline_buffer);
2009                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
2010                                         CB_RESET(comment_buffer, lline_buffer);
2011
2012                         } else {
2013                                 return -1;
2014                         }
2015                 } else {
2016                         ast_log(LOG_WARNING, "No '=' (equal sign) in line %d of %s\n", lineno, configfile);
2017                 }
2018         }
2019         return 0;
2020 }
2021
2022 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)
2023 {
2024         char fn[256];
2025 #if defined(LOW_MEMORY)
2026         char buf[512];
2027 #else
2028         char buf[8192];
2029 #endif
2030         char *new_buf, *comment_p, *process_buf;
2031         FILE *f;
2032         int lineno=0;
2033         int comment = 0, nest[MAX_NESTED_COMMENTS];
2034         struct ast_category *cat = NULL;
2035         int count = 0;
2036         struct stat statbuf;
2037         struct cache_file_mtime *cfmtime = NULL;
2038         struct cache_file_include *cfinclude;
2039         struct ast_variable *last_var = NULL;
2040         struct ast_category *last_cat = NULL;
2041         /*! Growable string buffer */
2042         struct ast_str *comment_buffer = NULL;  /*!< this will be a comment collector.*/
2043         struct ast_str *lline_buffer = NULL;    /*!< A buffer for stuff behind the ; */
2044 #ifdef AST_INCLUDE_GLOB
2045         int glob_ret;
2046         glob_t globbuf;
2047 #endif
2048
2049         if (cfg) {
2050                 cat = ast_config_get_current_category(cfg);
2051         }
2052
2053         if (filename[0] == '/') {
2054                 ast_copy_string(fn, filename, sizeof(fn));
2055         } else {
2056                 snprintf(fn, sizeof(fn), "%s/%s", ast_config_AST_CONFIG_DIR, filename);
2057         }
2058
2059         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
2060                 comment_buffer = ast_str_create(CB_SIZE);
2061                 if (comment_buffer) {
2062                         lline_buffer = ast_str_create(CB_SIZE);
2063                 }
2064                 if (!lline_buffer) {
2065                         ast_free(comment_buffer);
2066                         ast_log(LOG_ERROR, "Failed to initialize the comment buffer!\n");
2067                         return NULL;
2068                 }
2069         }
2070 #ifdef AST_INCLUDE_GLOB
2071         globbuf.gl_offs = 0;    /* initialize it to silence gcc */
2072         glob_ret = glob(fn, MY_GLOB_FLAGS, NULL, &globbuf);
2073         if (glob_ret == GLOB_NOSPACE) {
2074                 ast_log(LOG_WARNING,
2075                         "Glob Expansion of pattern '%s' failed: Not enough memory\n", fn);
2076         } else if (glob_ret  == GLOB_ABORTED) {
2077                 ast_log(LOG_WARNING,
2078                         "Glob Expansion of pattern '%s' failed: Read error\n", fn);
2079         } else {
2080                 /* loop over expanded files */
2081                 int i;
2082
2083                 if (!cfg && (globbuf.gl_pathc != 1 || strcmp(fn, globbuf.gl_pathv[0]))) {
2084                         /*
2085                          * We just want a file changed answer and since we cannot
2086                          * tell if a file was deleted with wildcard matching we will
2087                          * assume that something has always changed.  Also without
2088                          * a lot of refactoring we couldn't check more than one file
2089                          * for changes in the glob loop anyway.
2090                          */
2091                         globfree(&globbuf);
2092                         ast_free(comment_buffer);
2093                         ast_free(lline_buffer);
2094                         return NULL;
2095                 }
2096                 for (i=0; i<globbuf.gl_pathc; i++) {
2097                         ast_copy_string(fn, globbuf.gl_pathv[i], sizeof(fn));
2098 #endif
2099                         /*
2100                          * The following is not a loop, but just a convenient way to define a block
2101                          * (using do { } while(0) ), and be able to exit from it with 'continue'
2102                          * or 'break' in case of errors. Nice trick.
2103                          */
2104                         do {
2105                                 if (stat(fn, &statbuf)) {
2106                                         if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE)) {
2107                                                 config_cache_remove(fn, who_asked);
2108                                         }
2109                                         continue;
2110                                 }
2111
2112                                 if (!S_ISREG(statbuf.st_mode)) {
2113                                         ast_log(LOG_WARNING, "'%s' is not a regular file, ignoring\n", fn);
2114                                         if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE)) {
2115                                                 config_cache_remove(fn, who_asked);
2116                                         }
2117                                         continue;
2118                                 }
2119
2120                                 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE)) {
2121                                         /* Find our cached entry for this configuration file */
2122                                         AST_LIST_LOCK(&cfmtime_head);
2123                                         AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
2124                                                 if (!strcmp(cfmtime->filename, fn) && !strcmp(cfmtime->who_asked, who_asked)) {
2125                                                         break;
2126                                                 }
2127                                         }
2128                                         if (!cfmtime) {
2129                                                 cfmtime = cfmtime_new(fn, who_asked);
2130                                                 if (!cfmtime) {
2131                                                         AST_LIST_UNLOCK(&cfmtime_head);
2132                                                         continue;
2133                                                 }
2134                                                 /* Note that the file mtime is initialized to 0, i.e. 1970 */
2135                                                 AST_LIST_INSERT_SORTALPHA(&cfmtime_head, cfmtime, list, filename);
2136                                         }
2137                                 }
2138
2139                                 if (cfmtime
2140                                         && !cfmtime->has_exec
2141                                         && !cfmstat_cmp(cfmtime, &statbuf)
2142                                         && ast_test_flag(&flags, CONFIG_FLAG_FILEUNCHANGED)) {
2143                                         int unchanged = 1;
2144
2145                                         /* File is unchanged, what about the (cached) includes (if any)? */
2146                                         AST_LIST_TRAVERSE(&cfmtime->includes, cfinclude, list) {
2147                                                 if (!config_text_file_load(NULL, NULL, cfinclude->include,
2148                                                         NULL, flags, "", who_asked)) {
2149                                                         /* One change is enough to short-circuit and reload the whole shebang */
2150                                                         unchanged = 0;
2151                                                         break;
2152                                                 }
2153                                         }
2154
2155                                         if (unchanged) {
2156                                                 AST_LIST_UNLOCK(&cfmtime_head);
2157 #ifdef AST_INCLUDE_GLOB
2158                                                 globfree(&globbuf);
2159 #endif
2160                                                 ast_free(comment_buffer);
2161                                                 ast_free(lline_buffer);
2162                                                 return CONFIG_STATUS_FILEUNCHANGED;
2163                                         }
2164                                 }
2165
2166                                 /* If cfg is NULL, then we just want a file changed answer. */
2167                                 if (cfg == NULL) {
2168                                         if (cfmtime) {
2169                                                 AST_LIST_UNLOCK(&cfmtime_head);
2170                                         }
2171                                         continue;
2172                                 }
2173
2174                                 if (cfmtime) {
2175                                         /* Forget about what we thought we knew about this file's includes. */
2176                                         cfmtime->has_exec = 0;
2177                                         config_cache_flush_includes(cfmtime);
2178
2179                                         cfmstat_save(cfmtime, &statbuf);
2180                                         AST_LIST_UNLOCK(&cfmtime_head);
2181                                 }
2182
2183                                 if (!(f = fopen(fn, "r"))) {
2184                                         ast_debug(1, "No file to parse: %s\n", fn);
2185                                         ast_verb(2, "Parsing '%s': Not found (%s)\n", fn, strerror(errno));
2186                                         continue;
2187                                 }
2188                                 count++;
2189                                 /* If we get to this point, then we're loading regardless */
2190                                 ast_clear_flag(&flags, CONFIG_FLAG_FILEUNCHANGED);
2191                                 ast_debug(1, "Parsing %s\n", fn);
2192                                 ast_verb(2, "Parsing '%s': Found\n", fn);
2193                                 while (!feof(f)) {
2194                                         lineno++;
2195                                         if (fgets(buf, sizeof(buf), f)) {
2196                                                 /* Skip lines that are too long */
2197                                                 if (strlen(buf) == sizeof(buf) - 1 && buf[sizeof(buf) - 1] != '\n') {
2198                                                         ast_log(LOG_WARNING, "Line %d too long, skipping. It begins with: %.32s...\n", lineno, buf);
2199                                                         while (fgets(buf, sizeof(buf), f)) {
2200                                                                 if (strlen(buf) != sizeof(buf) - 1 || buf[sizeof(buf) - 1] == '\n') {
2201                                                                         break;
2202                                                                 }
2203                                                         }
2204                                                         continue;
2205                                                 }
2206
2207                                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)
2208                                                         && lline_buffer
2209                                                         && ast_str_strlen(lline_buffer)) {
2210                                                         CB_ADD(&comment_buffer, ast_str_buffer(lline_buffer)); /* add the current lline buffer to the comment buffer */
2211                                                         ast_str_reset(lline_buffer);        /* erase the lline buffer */
2212                                                 }
2213
2214                                                 new_buf = buf;
2215                                                 if (comment) {
2216                                                         process_buf = NULL;
2217                                                 } else {
2218                                                         process_buf = buf;
2219                                                 }
2220
2221                                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)
2222                                                         && comment_buffer
2223                                                         && ast_str_strlen(comment_buffer)
2224                                                         && (ast_strlen_zero(buf) || strlen(buf) == strspn(buf," \t\n\r"))) {
2225                                                         /* blank line? really? Can we add it to an existing comment and maybe preserve inter- and post- comment spacing? */
2226                                                         CB_ADD(&comment_buffer, "\n"); /* add a newline to the comment buffer */
2227                                                         continue; /* go get a new line, then */
2228                                                 }
2229
2230                                                 while ((comment_p = strchr(new_buf, COMMENT_META))) {
2231                                                         if ((comment_p > new_buf) && (*(comment_p - 1) == '\\')) {
2232                                                                 /* Escaped semicolons aren't comments. */
2233                                                                 new_buf = comment_p;
2234                                                                 /* write over the \ and bring the null terminator with us */
2235                                                                 memmove(comment_p - 1, comment_p, strlen(comment_p) + 1);
2236                                                         } else if (comment_p[1] == COMMENT_TAG && comment_p[2] == COMMENT_TAG && (comment_p[3] != '-')) {
2237                                                                 /* Meta-Comment start detected ";--" */
2238                                                                 if (comment < MAX_NESTED_COMMENTS) {
2239                                                                         *comment_p = '\0';
2240                                                                         new_buf = comment_p + 3;
2241                                                                         comment++;
2242                                                                         nest[comment-1] = lineno;
2243                                                                 } else {
2244                                                                         ast_log(LOG_ERROR, "Maximum nest limit of %d reached.\n", MAX_NESTED_COMMENTS);
2245                                                                 }
2246                                                         } else if ((comment_p >= new_buf + 2) &&
2247                                                                    (*(comment_p - 1) == COMMENT_TAG) &&
2248                                                                    (*(comment_p - 2) == COMMENT_TAG)) {
2249                                                                 /* Meta-Comment end detected "--;" */
2250                                                                 comment--;
2251                                                                 new_buf = comment_p + 1;
2252                                                                 if (!comment) {
2253                                                                         /* Back to non-comment now */
2254                                                                         if (process_buf) {
2255                                                                                 /* Actually have to move what's left over the top, then continue */
2256                                                                                 char *oldptr;
2257
2258                                                                                 oldptr = process_buf + strlen(process_buf);
2259                                                                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
2260                                                                                         CB_ADD(&comment_buffer, ";");
2261                                                                                         CB_ADD_LEN(&comment_buffer, oldptr+1, new_buf-oldptr-1);
2262                                                                                 }
2263
2264                                                                                 memmove(oldptr, new_buf, strlen(new_buf) + 1);
2265                                                                                 new_buf = oldptr;
2266                                                                         } else {
2267                                                                                 process_buf = new_buf;
2268                                                                         }
2269                                                                 }
2270                                                         } else {
2271                                                                 if (!comment) {
2272                                                                         /* If ; is found, and we are not nested in a comment,
2273                                                                            we immediately stop all comment processing */
2274                                                                         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
2275                                                                                 CB_ADD(&lline_buffer, comment_p);
2276                                                                         }
2277                                                                         *comment_p = '\0';
2278                                                                         new_buf = comment_p;
2279                                                                 } else {
2280                                                                         new_buf = comment_p + 1;
2281                                                                 }
2282                                                         }
2283                                                 }
2284                                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment && !process_buf ) {
2285                                                         CB_ADD(&comment_buffer, buf); /* the whole line is a comment, store it */
2286                                                 }
2287
2288                                                 if (process_buf) {
2289                                                         char *buffer = ast_strip(process_buf);
2290
2291                                                         if (!ast_strlen_zero(buffer)) {
2292                                                                 if (process_text_line(cfg, &cat, buffer, lineno, fn,
2293                                                                         flags, comment_buffer, lline_buffer,
2294                                                                         suggested_include_file, &last_cat, &last_var,
2295                                                                         who_asked)) {
2296                                                                         cfg = CONFIG_STATUS_FILEINVALID;
2297                                                                         break;
2298                                                                 }
2299                                                         }
2300                                                 }
2301                                         }
2302                                 }
2303                                 /* end of file-- anything in a comment buffer? */
2304                                 if (last_cat) {
2305                                         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && ast_str_strlen(comment_buffer)) {
2306                                                 if (lline_buffer && ast_str_strlen(lline_buffer)) {
2307                                                         CB_ADD(&comment_buffer, ast_str_buffer(lline_buffer)); /* add the current lline buffer to the comment buffer */
2308                                                         ast_str_reset(lline_buffer); /* erase the lline buffer */
2309                                                 }
2310                                                 last_cat->trailing = ALLOC_COMMENT(comment_buffer);
2311                                         }
2312                                 } else if (last_var) {
2313                                         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && ast_str_strlen(comment_buffer)) {
2314                                                 if (lline_buffer && ast_str_strlen(lline_buffer)) {
2315                                                         CB_ADD(&comment_buffer, ast_str_buffer(lline_buffer)); /* add the current lline buffer to the comment buffer */
2316                                                         ast_str_reset(lline_buffer); /* erase the lline buffer */
2317                                                 }
2318                                                 last_var->trailing = ALLOC_COMMENT(comment_buffer);
2319                                         }
2320                                 } else {
2321                                         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && ast_str_strlen(comment_buffer)) {
2322                                                 ast_debug(1, "Nothing to attach comments to, discarded: %s\n", ast_str_buffer(comment_buffer));
2323                                         }
2324                                 }
2325                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
2326                                         CB_RESET(comment_buffer, lline_buffer);
2327                                 }
2328
2329                                 fclose(f);
2330                         } while (0);
2331                         if (comment) {
2332                                 ast_log(LOG_WARNING,"Unterminated comment detected beginning on line %d\n", nest[comment - 1]);
2333                         }
2334 #ifdef AST_INCLUDE_GLOB
2335                         if (cfg == NULL || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) {
2336                                 break;
2337                         }
2338                 }
2339                 globfree(&globbuf);
2340         }
2341 #endif
2342
2343         ast_free(comment_buffer);
2344         ast_free(lline_buffer);
2345
2346         if (count == 0) {
2347                 return NULL;
2348         }
2349
2350         return cfg;
2351 }
2352
2353
2354 /* NOTE: categories and variables each have a file and lineno attribute. On a save operation, these are used to determine
2355    which file and line number to write out to. Thus, an entire hierarchy of config files (via #include statements) can be
2356    recreated. BUT, care must be taken to make sure that every cat and var has the proper file name stored, or you may
2357    be shocked and mystified as to why things are not showing up in the files!
2358
2359    Also, All #include/#exec statements are recorded in the "includes" LL in the ast_config structure. The file name
2360    and line number are stored for each include, plus the name of the file included, so that these statements may be
2361    included in the output files on a file_save operation.
2362
2363    The lineno's are really just for relative placement in the file. There is no attempt to make sure that blank lines
2364    are included to keep the lineno's the same between input and output. The lineno fields are used mainly to determine
2365    the position of the #include and #exec directives. So, blank lines tend to disappear from a read/rewrite operation,
2366    and a header gets added.
2367
2368    vars and category heads are output in the order they are stored in the config file. So, if the software
2369    shuffles these at all, then the placement of #include directives might get a little mixed up, because the
2370    file/lineno data probably won't get changed.
2371
2372 */
2373
2374 static void gen_header(FILE *f1, const char *configfile, const char *fn, const char *generator)
2375 {
2376         char date[256]="";
2377         time_t t;
2378
2379         time(&t);
2380         ast_copy_string(date, ctime(&t), sizeof(date));
2381
2382         fprintf(f1, ";!\n");
2383         fprintf(f1, ";! Automatically generated configuration file\n");
2384         if (strcmp(configfile, fn))
2385                 fprintf(f1, ";! Filename: %s (%s)\n", configfile, fn);
2386         else
2387                 fprintf(f1, ";! Filename: %s\n", configfile);
2388         fprintf(f1, ";! Generator: %s\n", generator);
2389         fprintf(f1, ";! Creation Date: %s", date);
2390         fprintf(f1, ";!\n");
2391 }
2392
2393 static void inclfile_destroy(void *obj)
2394 {
2395         const struct inclfile *o = obj;
2396
2397         ast_free(o->fname);
2398 }
2399
2400 static void make_fn(char *fn, size_t fn_size, const char *file, const char *configfile)
2401 {
2402         if (ast_strlen_zero(file)) {
2403                 if (configfile[0] == '/') {
2404                         ast_copy_string(fn, configfile, fn_size);
2405                 } else {
2406                         snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, configfile);
2407                 }
2408         } else if (file[0] == '/') {
2409                 ast_copy_string(fn, file, fn_size);
2410         } else {
2411                 snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, file);
2412         }
2413 }
2414
2415 static struct inclfile *set_fn(char *fn, size_t fn_size, const char *file, const char *configfile, struct ao2_container *fileset)
2416 {
2417         struct inclfile lookup;
2418         struct inclfile *fi;
2419
2420         make_fn(fn, fn_size, file, configfile);
2421         lookup.fname = fn;
2422         fi = ao2_find(fileset, &lookup, OBJ_POINTER);
2423         if (fi) {
2424                 /* Found existing include file scratch pad. */
2425                 return fi;
2426         }
2427
2428         /* set up a file scratch pad */
2429         fi = ao2_alloc(sizeof(struct inclfile), inclfile_destroy);
2430         if (!fi) {
2431                 /* Scratch pad creation failed. */
2432                 return NULL;
2433         }
2434         fi->fname = ast_strdup(fn);
2435         if (!fi->fname) {
2436                 /* Scratch pad creation failed. */
2437                 ao2_ref(fi, -1);
2438                 return NULL;
2439         }
2440         fi->lineno = 1;
2441
2442         ao2_link(fileset, fi);
2443
2444         return fi;
2445 }
2446
2447 static int count_linefeeds(char *str)
2448 {
2449         int count = 0;
2450
2451         while (*str) {
2452                 if (*str =='\n')
2453                         count++;
2454                 str++;
2455         }
2456         return count;
2457 }
2458
2459 static int count_linefeeds_in_comments(struct ast_comment *x)
2460 {
2461         int count = 0;
2462
2463         while (x) {
2464                 count += count_linefeeds(x->cmt);
2465                 x = x->next;
2466         }
2467         return count;
2468 }
2469
2470 static void insert_leading_blank_lines(FILE *fp, struct inclfile *fi, struct ast_comment *precomments, int lineno)
2471 {
2472         int precomment_lines;
2473         int i;
2474
2475         if (!fi) {
2476                 /* No file scratch pad object so insert no blank lines. */
2477                 return;
2478         }
2479
2480         precomment_lines = count_linefeeds_in_comments(precomments);
2481
2482         /* I don't have to worry about those ;! comments, they are
2483            stored in the precomments, but not printed back out.
2484            I did have to make sure that comments following
2485            the ;! header comments were not also deleted in the process */
2486         if (lineno - precomment_lines - fi->lineno < 0) { /* insertions can mess up the line numbering and produce negative numbers that mess things up */
2487                 return;
2488         } else if (lineno == 0) {
2489                 /* Line replacements also mess things up */
2490                 return;
2491         } else if (lineno - precomment_lines - fi->lineno < 5) {
2492                 /* Only insert less than 5 blank lines; if anything more occurs,
2493                  * it's probably due to context deletion. */
2494                 for (i = fi->lineno; i < lineno - precomment_lines; i++) {
2495                         fprintf(fp, "\n");
2496                 }
2497         } else {
2498                 /* Deletion occurred - insert a single blank line, for separation of
2499                  * contexts. */
2500                 fprintf(fp, "\n");
2501         }
2502
2503         fi->lineno = lineno + 1; /* Advance the file lineno */
2504 }
2505
2506 int config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator)
2507 {
2508         return ast_config_text_file_save2(configfile, cfg, generator, CONFIG_SAVE_FLAG_PRESERVE_EFFECTIVE_CONTEXT);
2509 }
2510
2511 int ast_config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator)
2512 {
2513         return ast_config_text_file_save2(configfile, cfg, generator, CONFIG_SAVE_FLAG_PRESERVE_EFFECTIVE_CONTEXT);
2514 }
2515
2516 static int is_writable(const char *fn)
2517 {
2518         if (access(fn, F_OK)) {
2519                 char *dn = dirname(ast_strdupa(fn));
2520
2521                 if (access(dn, R_OK | W_OK)) {
2522                         ast_log(LOG_ERROR, "Unable to write to directory %s (%s)\n", dn, strerror(errno));
2523                         return 0;
2524                 }
2525         } else {
2526                 if (access(fn, R_OK | W_OK)) {
2527                         ast_log(LOG_ERROR, "Unable to write %s (%s)\n", fn, strerror(errno));
2528                         return 0;
2529                 }
2530         }
2531
2532         return 1;
2533 }
2534
2535 int ast_config_text_file_save2(const char *configfile, const struct ast_config *cfg, const char *generator, uint32_t flags)
2536 {
2537         FILE *f;
2538         char fn[PATH_MAX];
2539         struct ast_variable *var;
2540         struct ast_category *cat;
2541         struct ast_comment *cmt;
2542         struct ast_config_include *incl;
2543         int blanklines = 0;
2544         struct ao2_container *fileset;
2545         struct inclfile *fi;
2546
2547         fileset = ao2_container_alloc(1023, hash_string, hashtab_compare_strings);
2548         if (!fileset) {
2549                 /* Container creation failed. */
2550                 return -1;
2551         }
2552
2553         /* Check all the files for write access before attempting to modify any of them */
2554         for (incl = cfg->includes; incl; incl = incl->next) {
2555                 /* reset all the output flags in case this isn't our first time saving this data */
2556                 incl->output = 0;
2557
2558                 if (!incl->exec) {
2559                         /* now make sure we have write access to the include file or its parent directory */
2560                         make_fn(fn, sizeof(fn), incl->included_file, configfile);
2561                         /* If the file itself doesn't exist, make sure we have write access to the directory */
2562                         if (!is_writable(fn)) {
2563                                 return -1;
2564                         }
2565                 }
2566         }
2567
2568         /* now make sure we have write access to the main config file or its parent directory */
2569         make_fn(fn, sizeof(fn), 0, configfile);
2570         if (!is_writable(fn)) {
2571                 return -1;
2572         }
2573
2574         /* Now that we know we have write access to all files, it's safe to start truncating them */
2575
2576         /* go thru all the inclusions and make sure all the files involved (configfile plus all its inclusions)
2577            are all truncated to zero bytes and have that nice header*/
2578         for (incl = cfg->includes; incl; incl = incl->next) {
2579                 if (!incl->exec) { /* leave the execs alone -- we'll write out the #exec directives, but won't zero out the include files or exec files*/
2580                         /* normally, fn is just set to incl->included_file, prepended with config dir if relative */
2581                         fi = set_fn(fn, sizeof(fn), incl->included_file, configfile, fileset);
2582                         f = fopen(fn, "w");
2583                         if (f) {
2584                                 gen_header(f, configfile, fn, generator);
2585                                 fclose(f); /* this should zero out the file */
2586                         } else {
2587                                 ast_log(LOG_ERROR, "Unable to write %s (%s)\n", fn, strerror(errno));
2588                         }
2589                         if (fi) {
2590                                 ao2_ref(fi, -1);
2591                         }
2592                 }
2593         }
2594
2595         /* just set fn to absolute ver of configfile */
2596         fi = set_fn(fn, sizeof(fn), 0, configfile, fileset);
2597         if (
2598 #ifdef __CYGWIN__
2599                 (f = fopen(fn, "w+"))
2600 #else
2601                 (f = fopen(fn, "w"))
2602 #endif
2603                 ) {
2604                 ast_verb(2, "Saving '%s'\n", fn);
2605                 gen_header(f, configfile, fn, generator);
2606                 cat = cfg->root;
2607                 fclose(f);
2608                 if (fi) {
2609                         ao2_ref(fi, -1);
2610                 }
2611
2612                 /* from here out, we open each involved file and concat the stuff we need to add to the end and immediately close... */
2613                 /* since each var, cat, and associated comments can come from any file, we have to be
2614                    mobile, and open each file, print, and close it on an entry-by-entry basis */
2615
2616                 while (cat) {
2617                         fi = set_fn(fn, sizeof(fn), cat->file, configfile, fileset);
2618                         f = fopen(fn, "a");
2619                         if (!f) {
2620                                 ast_log(LOG_ERROR, "Unable to write %s (%s)\n", fn, strerror(errno));
2621                                 if (fi) {
2622                                         ao2_ref(fi, -1);
2623                                 }
2624                                 ao2_ref(fileset, -1);
2625                                 return -1;
2626                         }
2627
2628                         /* dump any includes that happen before this category header */
2629                         for (incl=cfg->includes; incl; incl = incl->next) {
2630                                 if (strcmp(incl->include_location_file, cat->file) == 0){
2631                                         if (cat->lineno > incl->include_location_lineno && !incl->output) {
2632                                                 if (incl->exec)
2633                                                         fprintf(f,"#exec \"%s\"\n", incl->exec_file);
2634                                                 else
2635                                                         fprintf(f,"#include \"%s\"\n", incl->included_file);
2636                                                 incl->output = 1;
2637                                         }
2638                                 }
2639                         }
2640
2641                         insert_leading_blank_lines(f, fi, cat->precomments, cat->lineno);
2642                         /* Dump section with any appropriate comment */
2643                         for (cmt = cat->precomments; cmt; cmt=cmt->next) {
2644                                 char *cmtp = cmt->cmt;
2645                                 while (cmtp && *cmtp == ';' && *(cmtp+1) == '!') {
2646                                         char *cmtp2 = strchr(cmtp+1, '\n');
2647                                         if (cmtp2)
2648                                                 cmtp = cmtp2+1;
2649                                         else cmtp = 0;
2650                                 }
2651                                 if (cmtp)
2652                                         fprintf(f,"%s", cmtp);
2653                         }
2654                         fprintf(f, "[%s]", cat->name);
2655                         if (cat->ignored || !AST_LIST_EMPTY(&cat->template_instances)) {
2656                                 fprintf(f, "(");
2657                                 if (cat->ignored) {
2658                                         fprintf(f, "!");
2659                                 }
2660                                 if (cat->ignored && !AST_LIST_EMPTY(&cat->template_instances)) {
2661                                         fprintf(f, ",");
2662                                 }
2663                                 if (!AST_LIST_EMPTY(&cat->template_instances)) {
2664                                         struct ast_category_template_instance *x;
2665                                         AST_LIST_TRAVERSE(&cat->template_instances, x, next) {
2666                                                 fprintf(f,"%s",x->name);
2667                                                 if (x != AST_LIST_LAST(&cat->template_instances))
2668                                                         fprintf(f,",");
2669                                         }
2670                                 }
2671                                 fprintf(f, ")");
2672                         }
2673                         for(cmt = cat->sameline; cmt; cmt=cmt->next)
2674                         {
2675                                 fprintf(f,"%s", cmt->cmt);
2676                         }
2677                         if (!cat->sameline)
2678                                 fprintf(f,"\n");
2679                         for (cmt = cat->trailing; cmt; cmt=cmt->next) {
2680                                 if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
2681                                         fprintf(f,"%s", cmt->cmt);
2682                         }
2683                         fclose(f);
2684                         if (fi) {
2685                                 ao2_ref(fi, -1);
2686                         }
2687
2688                         var = cat->root;
2689                         while (var) {
2690                                 struct ast_category_template_instance *x;
2691                                 int found = 0;
2692
2693                                 AST_LIST_TRAVERSE(&cat->template_instances, x, next) {
2694                                         struct ast_variable *v;
2695                                         for (v = x->inst->root; v; v = v->next) {
2696
2697                                                 if (flags & CONFIG_SAVE_FLAG_PRESERVE_EFFECTIVE_CONTEXT) {
2698                                                         if (!strcasecmp(var->name, v->name) && !strcmp(var->value, v->value)) {
2699                                                                 found = 1;
2700                                                                 break;
2701                                                         }
2702                                                 } else {
2703                                                         if (var->inherited) {
2704                                                                 found = 1;
2705                                                                 break;
2706                                                         } else {
2707                                                                 if (!strcasecmp(var->name, v->name) && !strcmp(var->value, v->value)) {
2708                                                                         found = 1;
2709                                                                         break;
2710                                                                 }
2711                                                         }
2712                                                 }
2713                                         }
2714                                         if (found) {
2715                                                 break;
2716                                         }
2717                                 }
2718                                 if (found) {
2719                                         var = var->next;
2720                                         continue;
2721                                 }
2722                                 fi = set_fn(fn, sizeof(fn), var->file, configfile, fileset);
2723                                 f = fopen(fn, "a");
2724                                 if (!f) {
2725                                         ast_debug(1, "Unable to open for writing: %s\n", fn);
2726                                         ast_verb(2, "Unable to write %s (%s)\n", fn, strerror(errno));
2727                                         if (fi) {
2728                                                 ao2_ref(fi, -1);
2729                                         }
2730                                         ao2_ref(fileset, -1);
2731                                         return -1;
2732                                 }
2733
2734                                 /* dump any includes that happen before this category header */
2735                                 for (incl=cfg->includes; incl; incl = incl->next) {
2736                                         if (strcmp(incl->include_location_file, var->file) == 0){
2737                                                 if (var->lineno > incl->include_location_lineno && !incl->output) {
2738                                                         if (incl->exec)
2739                                                                 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
2740                                                         else
2741                                                                 fprintf(f,"#include \"%s\"\n", incl->included_file);
2742                                                         incl->output = 1;
2743                                                 }
2744                                         }
2745                                 }
2746
2747                                 insert_leading_blank_lines(f, fi, var->precomments, var->lineno);
2748                                 for (cmt = var->precomments; cmt; cmt=cmt->next) {
2749                                         if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
2750                                                 fprintf(f,"%s", cmt->cmt);
2751                                 }
2752
2753                                 { /* Block for 'escaped' scope */
2754                                         int escaped_len = 2 * strlen(var->value) + 1;
2755                                         char escaped[escaped_len];
2756
2757                                         ast_escape_semicolons(var->value, escaped, escaped_len);
2758
2759                                         if (var->sameline) {
2760                                                 fprintf(f, "%s %s %s  %s", var->name, (var->object ? "=>" : "="),
2761                                                         escaped, var->sameline->cmt);
2762                                         } else {
2763                                                 fprintf(f, "%s %s %s\n", var->name, (var->object ? "=>" : "="),
2764                                                         escaped);
2765                                         }
2766                                 }
2767
2768                                 for (cmt = var->trailing; cmt; cmt=cmt->next) {
2769                                         if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
2770                                                 fprintf(f,"%s", cmt->cmt);
2771                                 }
2772                                 if (var->blanklines) {
2773                                         blanklines = var->blanklines;
2774                                         while (blanklines--)
2775                                                 fprintf(f, "\n");
2776                                 }
2777
2778                                 fclose(f);
2779                                 if (fi) {
2780                                         ao2_ref(fi, -1);
2781                                 }
2782
2783                                 var = var->next;
2784                         }
2785                         cat = cat->next;
2786                 }
2787                 if (!option_debug) {
2788                         ast_verb(2, "Saving '%s': saved\n", fn);
2789                 }
2790         } else {
2791                 ast_debug(1, "Unable to open for writing: %s\n", fn);
2792                 ast_verb(2, "Unable to write '%s' (%s)\n", fn, strerror(errno));
2793                 if (fi) {
2794                         ao2_ref(fi, -1);
2795                 }
2796                 ao2_ref(fileset, -1);
2797                 return -1;
2798         }
2799
2800         /* Now, for files with trailing #include/#exec statements,
2801            we have to make sure every entry is output */
2802         for (incl=cfg->includes; incl; incl = incl->next) {
2803                 if (!incl->output) {
2804                         /* open the respective file */
2805                         fi = set_fn(fn, sizeof(fn), incl->include_location_file, configfile, fileset);
2806                         f = fopen(fn, "a");
2807                         if (!f) {
2808                                 ast_debug(1, "Unable to open for writing: %s\n", fn);
2809                                 ast_verb(2, "Unable to write %s (%s)\n", fn, strerror(errno));
2810                                 if (fi) {
2811                                         ao2_ref(fi, -1);
2812                                 }
2813                                 ao2_ref(fileset, -1);
2814                                 return -1;
2815                         }
2816
2817                         /* output the respective include */
2818                         if (incl->exec)
2819                                 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
2820                         else
2821                                 fprintf(f,"#include \"%s\"\n", incl->included_file);
2822                         fclose(f);
2823                         incl->output = 1;
2824                         if (fi) {
2825                                 ao2_ref(fi, -1);
2826                         }
2827                 }
2828         }
2829         ao2_ref(fileset, -1); /* this should destroy the hash container */
2830
2831         /* pass new configuration to any config hooks */
2832         config_hook_exec(configfile, generator, cfg);
2833
2834         return 0;
2835 }
2836
2837 static void clear_config_maps(void)
2838 {
2839         struct ast_config_map *map;
2840
2841         SCOPED_MUTEX(lock, &config_lock);
2842
2843         while (config_maps) {
2844                 map = config_maps;
2845                 config_maps = config_maps->next;
2846                 ast_free(map);
2847         }
2848 }
2849
2850 #ifdef TEST_FRAMEWORK
2851 int ast_realtime_append_mapping(const char *name, const char *driver, const char *database, const char *table, int priority)
2852 #else
2853 static int ast_realtime_append_mapping(const char *name, const char *driver, const char *database, const char *table, int priority)
2854 #endif
2855 {
2856         struct ast_config_map *map;
2857         char *dst;
2858         int length;
2859
2860         length = sizeof(*map);
2861         length += strlen(name) + 1;
2862         length += strlen(driver) + 1;
2863         length += strlen(database) + 1;
2864         if (table)
2865                 length += strlen(table) + 1;
2866
2867         if (!(map = ast_calloc(1, length)))
2868                 return -1;
2869
2870         dst = map->stuff;       /* writable space starts here */
2871         map->name = strcpy(dst, name);
2872         dst += strlen(dst) + 1;
2873         map->driver = strcpy(dst, driver);
2874         dst += strlen(dst) + 1;
2875         map->database = strcpy(dst, database);
2876         if (table) {
2877                 dst += strlen(dst) + 1;
2878                 map->table = strcpy(dst, table);
2879         }
2880         map->priority = priority;
2881         map->next = config_maps;
2882         config_maps = map;
2883
2884         ast_verb(2, "Binding %s to %s/%s/%s\n", map->name, map->driver, map->database, map->table ? map->table : map->name);
2885
2886         return 0;
2887 }
2888
2889 int read_config_maps(void)
2890 {
2891         struct ast_config *config, *configtmp;
2892         struct ast_variable *v;
2893         char *driver, *table, *database, *textpri, *stringp, *tmp;
2894         struct ast_flags flags = { CONFIG_FLAG_NOREALTIME };
2895         int pri;
2896
2897         clear_config_maps();
2898
2899         configtmp = ast_config_new();
2900         if (!configtmp) {
2901                 ast_log(LOG_ERROR, "Unable to allocate memory for new config\n");
2902                 return -1;
2903         }
2904         config = ast_config_internal_load(extconfig_conf, configtmp, flags, "", "extconfig");
2905         if (config == CONFIG_STATUS_FILEINVALID) {
2906                 return -1;
2907         } else if (!config) {
2908                 ast_config_destroy(configtmp);
2909                 return 0;
2910         }
2911
2912         for (v = ast_variable_browse(config, "settings"); v; v = v->next) {
2913                 char buf[512];
2914                 ast_copy_string(buf, v->value, sizeof(buf));
2915                 stringp = buf;
2916                 driver = strsep(&stringp, ",");
2917
2918                 if ((tmp = strchr(stringp, '\"')))
2919                         stringp = tmp;
2920
2921                 /* check if the database text starts with a double quote */
2922                 if (*stringp == '"') {
2923                         stringp++;
2924                         database = strsep(&stringp, "\"");
2925                         strsep(&stringp, ",");
2926                 } else {
2927                         /* apparently this text has no quotes */
2928                         database = strsep(&stringp, ",");
2929                 }
2930
2931                 table = strsep(&stringp, ",");
2932                 textpri = strsep(&stringp, ",");
2933                 if (!textpri || !(pri = atoi(textpri))) {
2934                         pri = 1;
2935                 }
2936
2937                 if (!strcmp(v->name, extconfig_conf)) {
2938                         ast_log(LOG_WARNING, "Cannot bind '%s'!\n", extconfig_conf);
2939                         continue;
2940                 }
2941
2942                 if (!strcmp(v->name, "asterisk.conf")) {
2943                         ast_log(LOG_WARNING, "Cannot bind 'asterisk.conf'!\n");
2944                         continue;
2945                 }
2946
2947                 if (!strcmp(v->name, "logger.conf")) {
2948                         ast_log(LOG_WARNING, "Cannot bind 'logger.conf'!\n");
2949                         continue;
2950                 }
2951
2952                 if (!driver || !database)
2953                         continue;
2954                 if (!strcasecmp(v->name, "sipfriends")) {
2955                         ast_log(LOG_WARNING, "The 'sipfriends' table is obsolete, update your config to use sippeers instead.\n");
2956                         ast_realtime_append_mapping("sippeers", driver, database, table ? table : "sipfriends", pri);
2957                 } else if (!strcasecmp(v->name, "iaxfriends")) {
2958                         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");
2959                         ast_realtime_append_mapping("iaxusers", driver, database, table ? table : "iaxfriends", pri);
2960                         ast_realtime_append_mapping("iaxpeers", driver, database, table ? table : "iaxfriends", pri);
2961                 } else
2962                         ast_realtime_append_mapping(v->name, driver, database, table, pri);
2963         }
2964
2965         ast_config_destroy(config);
2966         return 0;
2967 }
2968
2969 int ast_config_engine_register(struct ast_config_engine *new)
2970 {
2971         struct ast_config_engine *ptr;
2972
2973         SCOPED_MUTEX(lock, &config_lock);
2974
2975         if (!config_engine_list) {
2976                 config_engine_list = new;
2977         } else {
2978                 for (ptr = config_engine_list; ptr->next; ptr=ptr->next);
2979                 ptr->next = new;
2980         }
2981
2982         return 1;
2983 }
2984
2985 int ast_config_engine_deregister(struct ast_config_engine *del)
2986 {
2987         struct ast_config_engine *ptr, *last=NULL;
2988
2989         SCOPED_MUTEX(lock, &config_lock);
2990
2991         for (ptr = config_engine_list; ptr; ptr=ptr->next) {
2992                 if (ptr == del) {
2993                         if (last)
2994                                 last->next = ptr->next;
2995                         else
2996                                 config_engine_list = ptr->next;
2997                         break;
2998                 }
2999                 last = ptr;
3000         }
3001
3002         return 0;
3003 }
3004
3005 int ast_realtime_is_mapping_defined(const char *family)
3006 {
3007         struct ast_config_map *map;
3008         SCOPED_MUTEX(lock, &config_lock);
3009
3010         for (map = config_maps; map; map = map->next) {
3011                 if (!strcasecmp(family, map->name)) {
3012                         return 1;
3013                 }
3014         }
3015         ast_debug(5, "Failed to find a realtime mapping for %s\n", family);
3016
3017         return 0;
3018 }
3019
3020 /*! \brief Find realtime engine for realtime family */
3021 static struct ast_config_engine *find_engine(const char *family, int priority, char *database, int dbsiz, char *table, int tabsiz)
3022 {
3023         struct ast_config_engine *eng, *ret = NULL;
3024         struct ast_config_map *map;
3025
3026         SCOPED_MUTEX(lock, &config_lock);
3027
3028         for (map = config_maps; map; map = map->next) {
3029                 if (!strcasecmp(family, map->name) && (priority == map->priority)) {
3030                         if (database)
3031                                 ast_copy_string(database, map->database, dbsiz);
3032                         if (table)
3033                                 ast_copy_string(table, map->table ? map->table : family, tabsiz);
3034                         break;
3035                 }
3036         }
3037
3038         /* Check if the required driver (engine) exist */
3039         if (map) {
3040                 for (eng = config_engine_list; !ret && eng; eng = eng->next) {
3041                         if (!strcasecmp(eng->name, map->driver))
3042                                 ret = eng;
3043                 }
3044         }
3045
3046         /* if we found a mapping, but the engine is not available, then issue a warning */
3047         if (map && !ret)
3048                 ast_log(LOG_WARNING, "Realtime mapping for '%s' found to engine '%s', but the engine is not available\n", map->name, map->driver);
3049
3050         return ret;
3051 }
3052
3053 static struct ast_config_engine text_file_engine = {
3054         .name = "text",
3055         .load_func = config_text_file_load,
3056 };
3057
3058 struct ast_config *ast_config_copy(const struct ast_config *old)
3059 {
3060         struct ast_config *new_config = ast_config_new();
3061         struct ast_category *cat_iter;
3062
3063         if (!new_config) {
3064                 return NULL;
3065         }
3066
3067         for (cat_iter = old->root; cat_iter; cat_iter = cat_iter->next) {
3068                 struct ast_category *new_cat =
3069                         ast_category_new(cat_iter->name, cat_iter->file, cat_iter->lineno);
3070                 if (!new_cat) {
3071                         goto fail;
3072                 }
3073                 ast_category_append(new_config, new_cat);
3074                 if (cat_iter->root) {
3075                         new_cat->root = ast_variables_dup(cat_iter->root);
3076                         if (!new_cat->root) {
3077                                 goto fail;
3078                         }
3079                         new_cat->last = cat_iter->last;
3080                 }
3081         }
3082
3083         return new_config;
3084
3085 fail:
3086         ast_config_destroy(new_config);
3087         return NULL;
3088 }
3089
3090
3091 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)
3092 {
3093         char db[256];
3094         char table[256];
3095         struct ast_config_engine *loader = &text_file_engine;
3096         struct ast_config *result;
3097
3098         /* The config file itself bumps include_level by 1 */
3099         if (cfg->max_include_level > 0 && cfg->include_level == cfg->max_include_level + 1) {
3100                 ast_log(LOG_WARNING, "Maximum Include level (%d) exceeded\n", cfg->max_include_level);
3101                 return NULL;
3102         }
3103
3104         cfg->include_level++;
3105
3106         if (!ast_test_flag(&flags, CONFIG_FLAG_NOREALTIME) && config_engine_list) {
3107                 struct ast_config_engine *eng;
3108
3109                 eng = find_engine(filename, 1, db, sizeof(db), table, sizeof(table));
3110
3111
3112                 if (eng && eng->load_func) {
3113                         loader = eng;
3114                 } else {
3115                         eng = find_engine("global", 1, db, sizeof(db), table, sizeof(table));
3116                         if (eng && eng->load_func)
3117                                 loader = eng;
3118                 }
3119         }
3120
3121         result = loader->load_func(db, table, filename, cfg, flags, suggested_include_file, who_asked);
3122
3123         if (result && result != CONFIG_STATUS_FILEINVALID && result != CONFIG_STATUS_FILEUNCHANGED) {
3124                 result->include_level--;
3125                 config_hook_exec(filename, who_asked, result);
3126         } else if (result != CONFIG_STATUS_FILEINVALID) {
3127                 cfg->include_level--;
3128         }
3129
3130         return result;
3131 }
3132
3133 struct ast_config *ast_config_load2(const char *filename, const char *who_asked, struct ast_flags flags)
3134 {
3135         struct ast_config *cfg;
3136         struct ast_config *result;
3137
3138         cfg = ast_config_new();
3139         if (!cfg)
3140                 return NULL;
3141
3142         result = ast_config_internal_load(filename, cfg, flags, "", who_asked);
3143         if (!result || result == CONFIG_STATUS_FILEUNCHANGED || result == CONFIG_STATUS_FILEINVALID)
3144                 ast_config_destroy(cfg);
3145
3146         return result;
3147 }
3148
3149 #define realtime_arguments_to_fields(ap, result) realtime_arguments_to_fields2(ap, 0, result)
3150
3151 /*!
3152  * \internal
3153  * \brief
3154  *
3155  * \param ap list of variable arguments
3156  * \param skip Skip argument pairs for this number of variables
3157  * \param result Address of a variables pointer to store the results
3158  *               May be NULL if no arguments are parsed
3159  *               Will be NULL on failure.
3160  *
3161  * \retval 0 on success or empty ap list
3162  * \retval -1 on failure
3163  */
3164 static int realtime_arguments_to_fields2(va_list ap, int skip, struct ast_variable **result)
3165 {
3166         struct ast_variable *first, *fields = NULL;
3167         const char *newparam;
3168         const char *newval;
3169
3170         /*
3171          * Previously we would do:
3172          *
3173          *     va_start(ap, last);
3174          *     x = realtime_arguments_to_fields(ap);
3175          *     y = realtime_arguments_to_fields(ap);
3176          *     va_end(ap);
3177          *
3178          * While this works on generic amd64 machines (2014), it doesn't on the
3179          * raspberry PI. The va_arg() manpage says:
3180          *
3181          *     If ap is passed to a function that uses va_arg(ap,type) then
3182          *     the value of ap is undefined after the return of that function.
3183          *
3184          * On the raspberry, ap seems to get reset after the call: the contents
3185          * of y would be equal to the contents of x.
3186          *
3187          * So, instead we allow the caller to skip past earlier argument sets
3188          * using the skip parameter:
3189          *
3190          *     va_start(ap, last);
3191          *     if (realtime_arguments_to_fields(ap, &x)) {
3192          *         // FAILURE CONDITIONS
3193          *     }
3194          *     va_end(ap);
3195          *     va_start(ap, last);
3196          *     if (realtime_arguments_to_fields2(ap, 1, &y)) {
3197          *         // FAILURE CONDITIONS
3198          *     }
3199          *     va_end(ap);
3200          */
3201         while (skip--) {
3202                 /* There must be at least one argument. */
3203                 newparam = va_arg(ap, const char *);
3204                 newval = va_arg(ap, const char *);
3205                 while ((newparam = va_arg(ap, const char *))) {
3206                         newval = va_arg(ap, const char *);
3207                 }
3208         }
3209
3210         /* Load up the first vars. */
3211         newparam = va_arg(ap, const char *);
3212         if (!newparam) {
3213                 *result = NULL;
3214                 return 0;
3215         }
3216         newval = va_arg(ap, const char *);
3217
3218         if (!(first = ast_variable_new(newparam, newval, ""))) {
3219                 *result = NULL;
3220                 return -1;
3221         }
3222
3223         while ((newparam = va_arg(ap, const char *))) {
3224                 struct ast_variable *field;
3225
3226                 newval = va_arg(ap, const char *);
3227                 if (!(field = ast_variable_new(newparam, newval, ""))) {
3228                         ast_variables_destroy(fields);
3229                         ast_variables_destroy(first);
3230                         *result = NULL;
3231                         return -1;
3232                 }
3233
3234                 field->next = fields;
3235                 fields = field;
3236         }
3237
3238         first->next = fields;
3239         fields = first;
3240
3241         *result = fields;
3242         return 0;
3243 }
3244
3245 struct ast_variable *ast_load_realtime_all_fields(const char *family, const struct ast_variable *fields)
3246 {
3247         struct ast_config_engine *eng;
3248         char db[256];
3249         char table[256];
3250         struct ast_variable *res=NULL;
3251         int i;
3252
3253         for (i = 1; ; i++) {
3254                 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
3255                         if (eng->realtime_func && (res = eng->realtime_func(db, table, fields))) {
3256                                 return res;
3257                         }
3258                 } else {
3259                         return NULL;
3260                 }
3261         }
3262
3263         return res;
3264 }
3265
3266 struct ast_variable *ast_load_realtime_all(const char *family, ...)
3267 {
3268         RAII_VAR(struct ast_variable *, fields, NULL, ast_variables_destroy);
3269         struct ast_variable *res = NULL;
3270         va_list ap;
3271
3272         va_start(ap, family);
3273         realtime_arguments_to_fields(ap, &fields);
3274         va_end(ap);
3275
3276         if (fields) {
3277                 res = ast_load_realtime_all_fields(family, fields);
3278         }
3279
3280         return res;
3281 }
3282
3283 struct ast_variable *ast_load_realtime_fields(const char *family, const struct ast_variable *fields)
3284 {
3285         struct ast_variable *res;
3286         struct ast_variable *cur;
3287         struct ast_variable **prev;
3288
3289         res = ast_load_realtime_all_fields(family, fields);
3290
3291         /* Filter the list. */
3292         prev = &res;
3293         cur = res;
3294         while (cur) {
3295                 if (ast_strlen_zero(cur->value)) {
3296                         /* Eliminate empty entries */
3297                         struct ast_variable *next;
3298
3299                         next = cur->next;
3300                         *prev = next;
3301                         ast_variable_destroy(cur);
3302                         cur = next;
3303                 } else {
3304                         /* Make blank entries empty and keep them. */
3305                         if (cur->value[0] == ' ' && cur->value[1] == '\0') {
3306                                 char *vptr = (char *) cur->value;
3307
3308                                 vptr[0] = '\0';
3309                         }
3310
3311                         prev = &cur->next;
3312                         cur = cur->next;
3313                 }
3314         }
3315         return res;
3316 }
3317
3318 struct ast_variable *ast_load_realtime(const char *family, ...)
3319 {
3320         RAII_VAR(struct ast_variable *, fields, NULL, ast_variables_destroy);
3321         int field_res = 0;
3322         va_list ap;
3323
3324         va_start(ap, family);
3325         if (realtime_arguments_to_fields(ap, &fields)) {
3326                 field_res = -1;
3327         }
3328         va_end(ap);
3329
3330         if (field_res) {
3331                 return NULL;
3332         }
3333
3334         if (!fields) {
3335                 return NULL;
3336         }
3337
3338         return ast_load_realtime_fields(family, fields);
3339 }
3340
3341 /*! \brief Check if realtime engine is configured for family */
3342 int ast_check_realtime(const char *family)
3343 {
3344         struct ast_config_engine *eng;
3345         if (!ast_realtime_enabled()) {
3346                 return 0;       /* There are no engines at all so fail early */
3347         }
3348
3349         eng = find_engine(family, 1, NULL, 0, NULL, 0);
3350         if (eng)
3351                 return 1;
3352         return 0;
3353 }
3354
3355 /*! \brief Check if there's any realtime engines loaded */
3356 int ast_realtime_enabled(void)
3357 {
3358         return config_maps ? 1 : 0;
3359 }
3360
3361 int ast_realtime_require_field(const char *family, ...)
3362 {
3363         struct ast_config_engine *eng;
3364         char db[256];
3365         char table[256];
3366         va_list ap;
3367         int res = -1, i;
3368
3369         va_start(ap, family);
3370         for (i = 1; ; i++) {
3371                 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
3372                         /* If the require succeeds, it returns 0. */
3373                         if (eng->require_func && !(res = eng->require_func(db, table, ap))) {
3374                                 break;
3375                         }
3376                 } else {
3377                         break;
3378                 }
3379         }
3380         va_end(ap);
3381
3382         return res;
3383 }
3384
3385 int ast_unload_realtime(const char *family)
3386 {
3387         struct ast_config_engine *eng;
3388         char db[256];
3389         char table[256];
3390         int res = -1, i;
3391
3392         for (i = 1; ; i++) {
3393                 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
3394                         if (eng->unload_func) {
3395                                 /* Do this for ALL engines */
3396                                 res = eng->unload_func(db, table);
3397                         }
3398                 } else {
3399                         break;
3400                 }
3401         }
3402         return res;
3403 }
3404
3405 struct ast_config *ast_load_realtime_multientry_fields(const char *family, const struct ast_variable *fields)
3406 {
3407         struct ast_config_engine *eng;
3408         char db[256];
3409         char table[256];
3410         struct ast_config *res = NULL;
3411         int i;
3412
3413         for (i = 1; ; i++) {
3414                 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
3415                         if (eng->realtime_multi_func && (res = eng->realtime_multi_func(db, table, fields))) {
3416                                 /* If we were returned an empty cfg, destroy it and return NULL */
3417                                 if (!res->root) {
3418                                         ast_config_destroy(res);
3419                                         res = NULL;
3420                                 }
3421                                 break;
3422                         }
3423                 } else {
3424                         break;
3425                 }
3426         }
3427
3428         return res;
3429 }
3430
3431 struct ast_config *ast_load_realtime_multientry(const char *family, ...)
3432 {
3433         RAII_VAR(struct ast_variable *, fields, NULL, ast_variables_destroy);
3434         va_list ap;
3435
3436         va_start(ap, family);
3437         realtime_arguments_to_fields(ap, &fields);
3438         va_end(ap);
3439
3440         if (!fields) {
3441                 return NULL;
3442         }
3443
3444         return ast_load_realtime_multientry_fields(family, fields);
3445 }
3446
3447 int ast_update_realtime_fields(const char *family, const char *keyfield, const char *lookup, const struct ast_variable *fields)
3448 {
3449         struct ast_config_engine *eng;
3450         int res = -1, i;
3451         char db[256];
3452         char table[256];
3453
3454         for (i = 1; ; i++) {
3455                 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
3456                         /* If the update succeeds, it returns >= 0. */
3457                         if (eng->update_func && ((res = eng->update_func(db, table, keyfield, lookup, fields)) >= 0)) {
3458                                 break;
3459                         }
3460                 } else {
3461                         break;
3462                 }
3463         }
3464
3465         return res;
3466 }
3467
3468 int ast_update_realtime(const char *family, const char *keyfield, const char *lookup, ...)
3469 {
3470         RAII_VAR(struct ast_variable *, fields, NULL, ast_variables_destroy);
3471         va_list ap;
3472
3473         va_start(ap, lookup);
3474         realtime_arguments_to_fields(ap, &fields);
3475         va_end(ap);
3476
3477         if (!fields) {
3478                 return -1;
3479         }
3480
3481         return ast_update_realtime_fields(family, keyfield, lookup, fields);
3482 }
3483
3484 int ast_update2_realtime_fields(const char *family, const struct ast_variable *lookup_fields, const struct ast_variable *update_fields)
3485 {
3486         struct ast_config_engine *eng;
3487         int res = -1, i;
3488         char db[256];
3489         char table[256];
3490
3491         for (i = 1; ; i++) {
3492                 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
3493                         if (eng->update2_func && !(res = eng->update2_func(db, table, lookup_fields, update_fields))) {
3494                                 break;
3495                         }