6bed8a9b2b61483dabb1992af3efc95df5837f86
[asterisk/asterisk.git] / funcs / func_config.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2008, Digium, Inc.
5  *
6  * Russell Bryant <russell@digium.com>
7  * Tilghman Lesher <func_config__200803@the-tilghman.com>
8  *
9  * See http://www.asterisk.org for more information about
10  * the Asterisk project. Please do not directly contact
11  * any of the maintainers of this project for assistance;
12  * the project provides a web site, mailing lists and IRC
13  * channels for your use.
14  *
15  * This program is free software, distributed under the terms of
16  * the GNU General Public License Version 2. See the LICENSE file
17  * at the top of the source tree.
18  */
19
20 /*! \file
21  *
22  * \brief A function to retrieve variables from an Asterisk configuration file
23  *
24  * \author Russell Bryant <russell@digium.com>
25  * \author Tilghman Lesher <func_config__200803@the-tilghman.com>
26  * 
27  * \ingroup functions
28  */
29
30 #include "asterisk.h"
31
32 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
33
34 #include "asterisk/module.h"
35 #include "asterisk/channel.h"
36 #include "asterisk/pbx.h"
37 #include "asterisk/app.h"
38
39 struct config_item {
40         AST_RWLIST_ENTRY(config_item) entry;
41         struct ast_config *cfg;
42         char filename[0];
43 };
44
45 static AST_RWLIST_HEAD_STATIC(configs, config_item);
46
47 static int config_function_read(struct ast_channel *chan, const char *cmd, char *data, 
48         char *buf, size_t len) 
49 {
50         struct ast_config *cfg;
51         struct ast_flags cfg_flags = { CONFIG_FLAG_FILEUNCHANGED };
52         const char *val;
53         char *parse;
54         struct config_item *cur;
55         AST_DECLARE_APP_ARGS(args,
56                 AST_APP_ARG(filename);
57                 AST_APP_ARG(category);
58                 AST_APP_ARG(variable);
59                 AST_APP_ARG(index);
60         );
61
62         if (ast_strlen_zero(data)) {
63                 ast_log(LOG_ERROR, "AST_CONFIG() requires an argument\n");
64                 return -1;
65         }
66
67         parse = ast_strdupa(data);
68         AST_STANDARD_APP_ARGS(args, parse);
69
70         if (ast_strlen_zero(args.filename)) {
71                 ast_log(LOG_ERROR, "AST_CONFIG() requires a filename\n");
72                 return -1;
73         }
74
75         if (ast_strlen_zero(args.category)) {
76                 ast_log(LOG_ERROR, "AST_CONFIG() requires a category\n");
77                 return -1;
78         }
79         
80         if (ast_strlen_zero(args.variable)) {
81                 ast_log(LOG_ERROR, "AST_CONFIG() requires a variable\n");
82                 return -1;
83         }
84
85         if (!(cfg = ast_config_load(args.filename, cfg_flags))) {
86                 return -1;
87         }
88
89         if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
90                 /* Retrieve cfg from list */
91                 AST_RWLIST_RDLOCK(&configs);
92                 AST_RWLIST_TRAVERSE(&configs, cur, entry) {
93                         if (!strcmp(cur->filename, args.filename)) {
94                                 break;
95                         }
96                 }
97
98                 if (!cur) {
99                         /* At worst, we might leak an entry while upgrading locks */
100                         AST_RWLIST_UNLOCK(&configs);
101                         AST_RWLIST_WRLOCK(&configs);
102                         if (!(cur = ast_malloc(sizeof(*cur) + strlen(args.filename) + 1))) {
103                                 AST_RWLIST_UNLOCK(&configs);
104                                 return -1;
105                         }
106
107                         strcpy(cur->filename, args.filename);
108
109                         ast_clear_flag(&cfg_flags, CONFIG_FLAG_FILEUNCHANGED);
110                         if (!(cfg = ast_config_load(args.filename, cfg_flags))) {
111                                 ast_free(cur);
112                                 AST_RWLIST_UNLOCK(&configs);
113                                 return -1;
114                         }
115
116                         cur->cfg = cfg;
117                         AST_RWLIST_INSERT_TAIL(&configs, cur, entry);
118                 }
119
120                 cfg = cur->cfg;
121         } else {
122                 /* Replace cfg in list */
123                 AST_RWLIST_WRLOCK(&configs);
124                 AST_RWLIST_TRAVERSE(&configs, cur, entry) {
125                         if (!strcmp(cur->filename, args.filename)) {
126                                 break;
127                         }
128                 }
129
130                 if (!cur) {
131                         if (!(cur = ast_malloc(sizeof(*cur) + strlen(args.filename) + 1))) {
132                                 AST_RWLIST_UNLOCK(&configs);
133                                 return -1;
134                         }
135
136                         strcpy(cur->filename, args.filename);
137                         cur->cfg = cfg;
138
139                         AST_RWLIST_INSERT_TAIL(&configs, cur, entry);
140                 } else {
141                         ast_config_destroy(cur->cfg);
142                         cur->cfg = cfg;
143                 }
144         }
145
146         if (!(val = ast_variable_retrieve(cfg, args.category, args.variable))) {
147                 ast_log(LOG_ERROR, "'%s' not found in [%s] of '%s'\n", args.variable, 
148                         args.category, args.filename);
149                 AST_RWLIST_UNLOCK(&configs);
150                 return -1;
151         }
152
153         ast_copy_string(buf, val, len);
154
155         /* Unlock down here, so there's no chance the struct goes away while we're using it. */
156         AST_RWLIST_UNLOCK(&configs);
157
158         return 0;
159 }
160
161 static struct ast_custom_function config_function = {
162         .name = "AST_CONFIG",
163         .syntax = "AST_CONFIG(config_file,category,variable_name)",
164         .synopsis = "Retrieve a variable from a configuration file",
165         .desc = 
166         "   This function reads a variable from an Asterisk configuration file.\n"
167         "",
168         .read = config_function_read,
169 };
170
171 static int unload_module(void)
172 {
173         struct config_item *current;
174         int res = ast_custom_function_unregister(&config_function);
175
176         AST_RWLIST_WRLOCK(&configs);
177         while ((current = AST_RWLIST_REMOVE_HEAD(&configs, entry))) {
178                 ast_config_destroy(current->cfg);
179                 ast_free(current);
180         }
181         AST_RWLIST_UNLOCK(&configs);
182
183         return res;
184 }
185
186 static int load_module(void)
187 {
188         return ast_custom_function_register(&config_function);
189 }
190
191 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Asterisk configuration file variable access");