/src/kamailio/src/core/cfg/cfg.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (C) 2007 iptelorg GmbH |
3 | | * |
4 | | * This file is part of Kamailio, a free SIP server. |
5 | | * |
6 | | * Kamailio is free software; you can redistribute it and/or modify |
7 | | * it under the terms of the GNU General Public License as published by |
8 | | * the Free Software Foundation; either version 2 of the License, or |
9 | | * (at your option) any later version |
10 | | * |
11 | | * Kamailio is distributed in the hope that it will be useful, |
12 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | | * GNU General Public License for more details. |
15 | | * |
16 | | * You should have received a copy of the GNU General Public License |
17 | | * along with this program; if not, write to the Free Software |
18 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
19 | | * |
20 | | */ |
21 | | |
22 | | #include <string.h> |
23 | | |
24 | | #include "../ut.h" |
25 | | #include "../mem/mem.h" |
26 | | #include "cfg_struct.h" |
27 | | #include "cfg_ctx.h" |
28 | | #include "cfg_script.h" |
29 | | #include "cfg.h" |
30 | | |
31 | | /*! \brief declares a new cfg group |
32 | | * |
33 | | * handler is set to the memory area where the variables are stored |
34 | | * \return value is -1 on error |
35 | | */ |
36 | | int cfg_declare(char *group_name, cfg_def_t *def, void *values, int def_size, |
37 | | void **handle) |
38 | 0 | { |
39 | 0 | int i, num, size, group_name_len; |
40 | 0 | cfg_mapping_t *mapping = NULL; |
41 | 0 | cfg_group_t *group; |
42 | 0 | int types; |
43 | |
|
44 | 0 | if(def == NULL || def[0].name == NULL) |
45 | 0 | return -1; |
46 | | |
47 | | /* check the number of the variables */ |
48 | 0 | for(num = 0; def[num].name; num++) |
49 | 0 | ; |
50 | |
|
51 | 0 | mapping = (cfg_mapping_t *)pkg_malloc(sizeof(cfg_mapping_t) * num); |
52 | 0 | if(!mapping) { |
53 | 0 | PKG_MEM_ERROR; |
54 | 0 | goto error; |
55 | 0 | } |
56 | 0 | memset(mapping, 0, sizeof(cfg_mapping_t) * num); |
57 | 0 | types = 0; |
58 | | /* calculate the size of the memory block that has to |
59 | | be allocated for the cfg variables, and set the content of the |
60 | | cfg_mapping array the same time */ |
61 | 0 | for(i = 0, size = 0; i < num; i++) { |
62 | 0 | mapping[i].def = &(def[i]); |
63 | 0 | mapping[i].name_len = strlen(def[i].name); |
64 | 0 | mapping[i].pos = i; |
65 | | /* record all the types for sanity checks */ |
66 | 0 | types |= 1 << CFG_VAR_MASK(def[i].type); |
67 | | |
68 | | /* padding depends on the type of the next variable */ |
69 | 0 | switch(CFG_VAR_MASK(def[i].type)) { |
70 | | |
71 | 0 | case CFG_VAR_INT: |
72 | 0 | size = ROUND_INT(size); |
73 | 0 | mapping[i].offset = size; |
74 | 0 | size += sizeof(int); |
75 | 0 | break; |
76 | | |
77 | 0 | case CFG_VAR_STRING: |
78 | 0 | case CFG_VAR_POINTER: |
79 | 0 | size = ROUND_POINTER(size); |
80 | 0 | mapping[i].offset = size; |
81 | 0 | size += sizeof(char *); |
82 | 0 | break; |
83 | | |
84 | 0 | case CFG_VAR_STR: |
85 | 0 | size = ROUND_POINTER(size); |
86 | 0 | mapping[i].offset = size; |
87 | 0 | size += sizeof(str); |
88 | 0 | break; |
89 | | |
90 | 0 | default: |
91 | 0 | LM_ERR("%s.%s: unsupported variable type\n", group_name, |
92 | 0 | def[i].name); |
93 | 0 | goto error; |
94 | 0 | } |
95 | | |
96 | | /* verify the type of the input */ |
97 | 0 | if(CFG_INPUT_MASK(def[i].type) == 0) { |
98 | 0 | def[i].type |= CFG_VAR_MASK(def[i].type) << CFG_INPUT_SHIFT; |
99 | 0 | } else { |
100 | 0 | if((CFG_INPUT_MASK(def[i].type) |
101 | 0 | != CFG_VAR_MASK(def[i].type) << CFG_INPUT_SHIFT) |
102 | 0 | && (def[i].on_change_cb == 0)) { |
103 | 0 | LM_ERR("%s.%s: variable and input types are " |
104 | 0 | "different, but no callback is defined for conversion\n", |
105 | 0 | group_name, def[i].name); |
106 | 0 | goto error; |
107 | 0 | } |
108 | 0 | } |
109 | | |
110 | 0 | if(CFG_INPUT_MASK(def[i].type) > CFG_INPUT_STR) { |
111 | 0 | LM_ERR("%s.%s: unsupported input type\n", group_name, def[i].name); |
112 | 0 | goto error; |
113 | 0 | } |
114 | | |
115 | 0 | if(def[i].type & CFG_ATOMIC) { |
116 | 0 | if(CFG_VAR_MASK(def[i].type) != CFG_VAR_INT) { |
117 | 0 | LM_ERR("%s.%s: atomic change is allowed " |
118 | 0 | "only for integer types\n", |
119 | 0 | group_name, def[i].name); |
120 | 0 | goto error; |
121 | 0 | } |
122 | 0 | if(def[i].on_set_child_cb) { |
123 | 0 | LM_ERR("%s.%s: per-child process callback " |
124 | 0 | "does not work together with atomic change\n", |
125 | 0 | group_name, def[i].name); |
126 | 0 | goto error; |
127 | 0 | } |
128 | 0 | } |
129 | 0 | } |
130 | | |
131 | | /* fix the computed size (char*, str or pointer members will force |
132 | | structure padding to multiple of sizeof(pointer)) */ |
133 | 0 | if(types |
134 | 0 | & ((1 << CFG_VAR_STRING) | (1 << CFG_VAR_STR) |
135 | 0 | | (1 << CFG_VAR_POINTER))) |
136 | 0 | size = ROUND_POINTER(size); |
137 | | /* minor validation */ |
138 | 0 | if(size != def_size) { |
139 | 0 | LM_ERR("the specified size (%i) of the config " |
140 | 0 | "structure does not equal with the calculated size (%i), check " |
141 | 0 | "whether " |
142 | 0 | "the variable types are correctly defined!\n", |
143 | 0 | def_size, size); |
144 | 0 | goto error; |
145 | 0 | } |
146 | | |
147 | | /* The cfg variables are ready to use, let us set the handle |
148 | | before passing the new definitions to the drivers. |
149 | | We make the interface usable for the fixup functions |
150 | | at this step |
151 | | cfg_set_group() and cfg_new_group() need the handle to be set because |
152 | | they save its original value. */ |
153 | 0 | *handle = values; |
154 | |
|
155 | 0 | group_name_len = strlen(group_name); |
156 | | /* check for duplicates */ |
157 | 0 | if((group = cfg_lookup_group(group_name, group_name_len))) { |
158 | 0 | if(group->dynamic != CFG_GROUP_UNKNOWN) { |
159 | | /* conflict with another module/core group, or with a dynamic group */ |
160 | 0 | LM_ERR("configuration group has been already declared: %s\n", |
161 | 0 | group_name); |
162 | 0 | goto error; |
163 | 0 | } |
164 | | /* An empty group is found which does not have any variable yet */ |
165 | 0 | cfg_set_group(group, num, mapping, values, size, handle); |
166 | 0 | } else { |
167 | | /* create a new group |
168 | | I will allocate memory in shm mem for the variables later in a single block, |
169 | | when we know the size of all the registered groups. */ |
170 | 0 | if(!(group = cfg_new_group(group_name, group_name_len, num, mapping, |
171 | 0 | values, size, handle))) |
172 | 0 | goto error; |
173 | 0 | } |
174 | 0 | group->dynamic = CFG_GROUP_STATIC; |
175 | | |
176 | | /* notify the drivers about the new config definition */ |
177 | 0 | cfg_notify_drivers(group_name, group_name_len, def); |
178 | |
|
179 | 0 | LM_DBG("new config group has been registered: '%s' (num=%d, size=%d)\n", |
180 | 0 | group_name, num, size); |
181 | |
|
182 | 0 | return 0; |
183 | | |
184 | 0 | error: |
185 | 0 | if(mapping) |
186 | 0 | pkg_free(mapping); |
187 | 0 | LM_ERR("failed to register the config group: %s\n", group_name); |
188 | |
|
189 | 0 | return -1; |
190 | 0 | } |
191 | | |
192 | | /* declares a single variable with integer type */ |
193 | | int cfg_declare_int(char *group_name, char *var_name, int val, int min, int max, |
194 | | char *descr) |
195 | 0 | { |
196 | 0 | cfg_script_var_t *var; |
197 | |
|
198 | 0 | if((var = new_cfg_script_var(group_name, var_name, CFG_VAR_INT, descr)) |
199 | 0 | == NULL) |
200 | 0 | return -1; |
201 | | |
202 | 0 | var->val.i = val; |
203 | 0 | var->min = min; |
204 | 0 | var->max = max; |
205 | |
|
206 | 0 | return 0; |
207 | 0 | } |
208 | | |
209 | | /* declares a single variable with str type */ |
210 | | int cfg_declare_str(char *group_name, char *var_name, char *val, char *descr) |
211 | 0 | { |
212 | 0 | cfg_script_var_t *var; |
213 | 0 | int len; |
214 | |
|
215 | 0 | if((var = new_cfg_script_var(group_name, var_name, CFG_VAR_STR, descr)) |
216 | 0 | == NULL) |
217 | 0 | return -1; |
218 | | |
219 | 0 | if(val) { |
220 | 0 | len = strlen(val); |
221 | 0 | var->val.s.s = (char *)pkg_malloc(sizeof(char) * (len + 1)); |
222 | 0 | if(!var->val.s.s) { |
223 | 0 | PKG_MEM_ERROR; |
224 | 0 | return -1; |
225 | 0 | } |
226 | 0 | memcpy(var->val.s.s, val, len + 1); |
227 | 0 | var->val.s.len = len; |
228 | 0 | } else { |
229 | 0 | var->val.s.s = NULL; |
230 | 0 | var->val.s.len = 0; |
231 | 0 | } |
232 | | |
233 | 0 | return 0; |
234 | 0 | } |
235 | | |
236 | | /* Add a variable to a group instance with integer type. |
237 | | * The group instance is created if it does not exist. |
238 | | * wrapper function for new_add_var() |
239 | | */ |
240 | | int cfg_ginst_var_int( |
241 | | char *group_name, unsigned int group_id, char *var_name, int val) |
242 | 0 | { |
243 | 0 | str gname, vname; |
244 | |
|
245 | 0 | gname.s = group_name; |
246 | 0 | gname.len = strlen(group_name); |
247 | 0 | vname.s = var_name; |
248 | 0 | vname.len = strlen(var_name); |
249 | |
|
250 | 0 | return new_add_var( |
251 | 0 | &gname, group_id, &vname, (void *)(long)val, CFG_VAR_INT); |
252 | 0 | } |
253 | | |
254 | | /* Add a variable to a group instance with string type. |
255 | | * The group instance is created if it does not exist. |
256 | | * wrapper function for new_add_var() |
257 | | */ |
258 | | int cfg_ginst_var_string( |
259 | | char *group_name, unsigned int group_id, char *var_name, char *val) |
260 | 0 | { |
261 | 0 | str gname, vname; |
262 | |
|
263 | 0 | gname.s = group_name; |
264 | 0 | gname.len = strlen(group_name); |
265 | 0 | vname.s = var_name; |
266 | 0 | vname.len = strlen(var_name); |
267 | |
|
268 | 0 | return new_add_var(&gname, group_id, &vname, (void *)val, CFG_VAR_STRING); |
269 | 0 | } |
270 | | |
271 | | /* Create a new group instance. |
272 | | * wrapper function for new_add_var() |
273 | | */ |
274 | | int cfg_new_ginst(char *group_name, unsigned int group_id) |
275 | 0 | { |
276 | 0 | str gname; |
277 | |
|
278 | 0 | gname.s = group_name; |
279 | 0 | gname.len = strlen(group_name); |
280 | |
|
281 | 0 | return new_add_var( |
282 | 0 | &gname, group_id, NULL /* var */, NULL /* val */, 0 /* type */); |
283 | 0 | } |
284 | | |
285 | | /* returns the handle of a cfg group */ |
286 | | void **cfg_get_handle(char *gname) |
287 | 0 | { |
288 | 0 | cfg_group_t *group; |
289 | |
|
290 | 0 | group = cfg_lookup_group(gname, strlen(gname)); |
291 | 0 | if(!group || (group->dynamic != CFG_GROUP_STATIC)) |
292 | 0 | return NULL; |
293 | | |
294 | 0 | return group->handle; |
295 | 0 | } |
296 | | |
297 | | |
298 | | /* Set the group_id pointer based on the group string. |
299 | | * The string is either "group_name", or "group_name[group_id]" |
300 | | * *group_id is set to null in the former case. |
301 | | * Warning: changes the group string |
302 | | */ |
303 | | int cfg_get_group_id(str *group, unsigned int **group_id) |
304 | 0 | { |
305 | 0 | static unsigned int id; |
306 | 0 | str s; |
307 | |
|
308 | 0 | if(!group->s || (group->s[group->len - 1] != ']')) { |
309 | 0 | *group_id = NULL; |
310 | 0 | return 0; |
311 | 0 | } |
312 | | |
313 | 0 | s.s = group->s + group->len - 2; |
314 | 0 | s.len = 0; |
315 | 0 | while((s.s > group->s) && (*s.s != '[')) { |
316 | 0 | s.s--; |
317 | 0 | s.len++; |
318 | 0 | } |
319 | 0 | if(s.s == group->s) /* '[' not found */ |
320 | 0 | return -1; |
321 | 0 | group->len = s.s - group->s; |
322 | 0 | s.s++; |
323 | 0 | if(!group->len || !s.len) |
324 | 0 | return -1; |
325 | 0 | if(str2int(&s, &id)) |
326 | 0 | return -1; |
327 | | |
328 | 0 | *group_id = &id; |
329 | 0 | return 0; |
330 | 0 | } |