Update documentation for CHANNEL function
[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 /*** MODULEINFO
31         <support_level>core</support_level>
32  ***/
33
34 #include "asterisk.h"
35
36 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
37
38 #include "asterisk/module.h"
39 #include "asterisk/channel.h"
40 #include "asterisk/pbx.h"
41 #include "asterisk/app.h"
42
43 /*** DOCUMENTATION
44         <function name="AST_CONFIG" language="en_US">
45                 <synopsis>
46                         Retrieve a variable from a configuration file.
47                 </synopsis>
48                 <syntax>
49                         <parameter name="config_file" required="true" />
50                         <parameter name="category" required="true" />
51                         <parameter name="variable_name" required="true" />
52                 </syntax>
53                 <description>
54                         <para>This function reads a variable from an Asterisk configuration file.</para>
55                 </description>
56         </function>
57
58 ***/
59
60 struct config_item {
61         AST_RWLIST_ENTRY(config_item) entry;
62         struct ast_config *cfg;
63         char filename[0];
64 };
65
66 static AST_RWLIST_HEAD_STATIC(configs, config_item);
67
68 static int config_function_read(struct ast_channel *chan, const char *cmd, char *data, 
69         char *buf, size_t len) 
70 {
71         struct ast_config *cfg;
72         struct ast_flags cfg_flags = { CONFIG_FLAG_FILEUNCHANGED };
73         const char *val;
74         char *parse;
75         struct config_item *cur;
76         AST_DECLARE_APP_ARGS(args,
77                 AST_APP_ARG(filename);
78                 AST_APP_ARG(category);
79                 AST_APP_ARG(variable);
80                 AST_APP_ARG(index);
81         );
82
83         if (ast_strlen_zero(data)) {
84                 ast_log(LOG_ERROR, "AST_CONFIG() requires an argument\n");
85                 return -1;
86         }
87
88         parse = ast_strdupa(data);
89         AST_STANDARD_APP_ARGS(args, parse);
90
91         if (ast_strlen_zero(args.filename)) {
92                 ast_log(LOG_ERROR, "AST_CONFIG() requires a filename\n");
93                 return -1;
94         }
95
96         if (ast_strlen_zero(args.category)) {
97                 ast_log(LOG_ERROR, "AST_CONFIG() requires a category\n");
98                 return -1;
99         }
100         
101         if (ast_strlen_zero(args.variable)) {
102                 ast_log(LOG_ERROR, "AST_CONFIG() requires a variable\n");
103                 return -1;
104         }
105
106         if (!(cfg = ast_config_load(args.filename, cfg_flags)) || cfg == CONFIG_STATUS_FILEINVALID) {
107                 return -1;
108         }
109
110         if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
111                 /* Retrieve cfg from list */
112                 AST_RWLIST_RDLOCK(&configs);
113                 AST_RWLIST_TRAVERSE(&configs, cur, entry) {
114                         if (!strcmp(cur->filename, args.filename)) {
115                                 break;
116                         }
117                 }
118
119                 if (!cur) {
120                         /* At worst, we might leak an entry while upgrading locks */
121                         AST_RWLIST_UNLOCK(&configs);
122                         AST_RWLIST_WRLOCK(&configs);
123                         if (!(cur = ast_malloc(sizeof(*cur) + strlen(args.filename) + 1))) {
124                                 AST_RWLIST_UNLOCK(&configs);
125                                 return -1;
126                         }
127
128                         strcpy(cur->filename, args.filename);
129
130                         ast_clear_flag(&cfg_flags, CONFIG_FLAG_FILEUNCHANGED);
131                         if (!(cfg = ast_config_load(args.filename, cfg_flags)) || cfg == CONFIG_STATUS_FILEINVALID) {
132                                 ast_free(cur);
133                                 AST_RWLIST_UNLOCK(&configs);
134                                 return -1;
135                         }
136
137                         cur->cfg = cfg;
138                         AST_RWLIST_INSERT_TAIL(&configs, cur, entry);
139                 }
140
141                 cfg = cur->cfg;
142         } else {
143                 /* Replace cfg in list */
144                 AST_RWLIST_WRLOCK(&configs);
145                 AST_RWLIST_TRAVERSE(&configs, cur, entry) {
146                         if (!strcmp(cur->filename, args.filename)) {
147                                 break;
148                         }
149                 }
150
151                 if (!cur) {
152                         if (!(cur = ast_malloc(sizeof(*cur) + strlen(args.filename) + 1))) {
153                                 AST_RWLIST_UNLOCK(&configs);
154                                 return -1;
155                         }
156
157                         strcpy(cur->filename, args.filename);
158                         cur->cfg = cfg;
159
160                         AST_RWLIST_INSERT_TAIL(&configs, cur, entry);
161                 } else {
162                         ast_config_destroy(cur->cfg);
163                         cur->cfg = cfg;
164                 }
165         }
166
167         if (!(val = ast_variable_retrieve(cfg, args.category, args.variable))) {
168                 ast_log(LOG_ERROR, "'%s' not found in [%s] of '%s'\n", args.variable, 
169                         args.category, args.filename);
170                 AST_RWLIST_UNLOCK(&configs);
171                 return -1;
172         }
173
174         ast_copy_string(buf, val, len);
175
176         /* Unlock down here, so there's no chance the struct goes away while we're using it. */
177         AST_RWLIST_UNLOCK(&configs);
178
179         return 0;
180 }
181
182 static struct ast_custom_function config_function = {
183         .name = "AST_CONFIG",
184         .read = config_function_read,
185 };
186
187 static int unload_module(void)
188 {
189         struct config_item *current;
190         int res = ast_custom_function_unregister(&config_function);
191
192         AST_RWLIST_WRLOCK(&configs);
193         while ((current = AST_RWLIST_REMOVE_HEAD(&configs, entry))) {
194                 ast_config_destroy(current->cfg);
195                 ast_free(current);
196         }
197         AST_RWLIST_UNLOCK(&configs);
198
199         return res;
200 }
201
202 static int load_module(void)
203 {
204         return ast_custom_function_register(&config_function);
205 }
206
207 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Asterisk configuration file variable access");