/src/kamailio/src/core/cfg/cfg_ctx.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 | | #include <stdio.h> |
24 | | #include <stdlib.h> |
25 | | |
26 | | #include "../ut.h" |
27 | | #include "cfg_struct.h" |
28 | | #include "cfg_script.h" |
29 | | #include "cfg_ctx.h" |
30 | | |
31 | | |
32 | | /* linked list of all the registered cfg contexts */ |
33 | | static cfg_ctx_t *cfg_ctx_list = NULL; |
34 | | |
35 | | /* creates a new config context that is an interface to the |
36 | | * cfg variables with write permission |
37 | | */ |
38 | | int cfg_register_ctx(cfg_ctx_t **handle, cfg_on_declare on_declare_cb) |
39 | 0 | { |
40 | 0 | cfg_ctx_t *ctx; |
41 | 0 | cfg_group_t *group; |
42 | 0 | str gname; |
43 | | |
44 | | /* allocate memory for the new context |
45 | | * Better to use shm mem, because 'changed' and 'lock' |
46 | | * must be in shm mem anyway */ |
47 | 0 | ctx = (cfg_ctx_t *)shm_malloc(sizeof(cfg_ctx_t)); |
48 | 0 | if(!ctx) { |
49 | 0 | SHM_MEM_ERROR; |
50 | 0 | return -1; |
51 | 0 | } |
52 | 0 | memset(ctx, 0, sizeof(cfg_ctx_t)); |
53 | 0 | if(lock_init(&ctx->lock) == 0) { |
54 | 0 | LM_ERR("failed to init lock\n"); |
55 | 0 | shm_free(ctx); |
56 | 0 | return -1; |
57 | 0 | } |
58 | | |
59 | | /* add the new ctx to the beginning of the list */ |
60 | 0 | ctx->next = cfg_ctx_list; |
61 | 0 | cfg_ctx_list = ctx; |
62 | | |
63 | | /* let the driver know about the already registered groups |
64 | | * The handle of the context must be set before calling the |
65 | | * on_declare callbacks. */ |
66 | 0 | *handle = ctx; |
67 | 0 | if(on_declare_cb) { |
68 | 0 | ctx->on_declare_cb = on_declare_cb; |
69 | |
|
70 | 0 | for(group = cfg_group; group; group = group->next) { |
71 | | /* dynamic groups are not ready, the callback |
72 | | * will be called later when the group is fixed-up */ |
73 | 0 | if(group->dynamic != CFG_GROUP_STATIC) |
74 | 0 | continue; |
75 | | |
76 | 0 | gname.s = group->name; |
77 | 0 | gname.len = group->name_len; |
78 | 0 | on_declare_cb(&gname, group->mapping->def); |
79 | 0 | } |
80 | 0 | } |
81 | |
|
82 | 0 | return 0; |
83 | 0 | } |
84 | | |
85 | | /* free the memory allocated for the contexts */ |
86 | | void cfg_ctx_destroy(void) |
87 | 0 | { |
88 | 0 | cfg_ctx_t *ctx, *ctx2; |
89 | |
|
90 | 0 | for(ctx = cfg_ctx_list; ctx; ctx = ctx2) { |
91 | 0 | ctx2 = ctx->next; |
92 | 0 | shm_free(ctx); |
93 | 0 | } |
94 | 0 | cfg_ctx_list = NULL; |
95 | 0 | } |
96 | | |
97 | | /* notify the drivers about the new config definition */ |
98 | | void cfg_notify_drivers(char *group_name, int group_name_len, cfg_def_t *def) |
99 | 0 | { |
100 | 0 | cfg_ctx_t *ctx; |
101 | 0 | str gname; |
102 | |
|
103 | 0 | gname.s = group_name; |
104 | 0 | gname.len = group_name_len; |
105 | |
|
106 | 0 | for(ctx = cfg_ctx_list; ctx; ctx = ctx->next) |
107 | 0 | if(ctx->on_declare_cb) |
108 | 0 | ctx->on_declare_cb(&gname, def); |
109 | 0 | } |
110 | | |
111 | | /* placeholder for a temporary string */ |
112 | | static char *temp_string = NULL; |
113 | | |
114 | | /* convert the value to the requested type */ |
115 | | int convert_val( |
116 | | unsigned int val_type, void *val, unsigned int var_type, void **new_val) |
117 | 0 | { |
118 | 0 | static str s; |
119 | 0 | char *end; |
120 | 0 | int i; |
121 | 0 | static char buf[INT2STR_MAX_LEN]; |
122 | | |
123 | | /* we have to convert from val_type to var_type */ |
124 | 0 | switch(CFG_INPUT_MASK(var_type)) { |
125 | 0 | case CFG_INPUT_INT: |
126 | 0 | if(val_type == CFG_VAR_INT) { |
127 | 0 | *new_val = val; |
128 | 0 | break; |
129 | |
|
130 | 0 | } else if(val_type == CFG_VAR_STRING) { |
131 | 0 | if(!val || (((char *)val)[0] == '\0')) { |
132 | 0 | LM_ERR("cannot convert NULL string value to integer\n"); |
133 | 0 | return -1; |
134 | 0 | } |
135 | 0 | *new_val = (void *)(long)strtol((char *)val, &end, 10); |
136 | 0 | if(*end != '\0') { |
137 | 0 | LM_ERR("cannot convert string to integer '%s'\n", |
138 | 0 | (char *)val); |
139 | 0 | return -1; |
140 | 0 | } |
141 | 0 | break; |
142 | |
|
143 | 0 | } else if(val_type == CFG_VAR_STR) { |
144 | 0 | if(!((str *)val)->len || !((str *)val)->s) { |
145 | 0 | LM_ERR("cannot convert NULL str value to integer\n"); |
146 | 0 | return -1; |
147 | 0 | } |
148 | 0 | if(str2sint((str *)val, &i)) { |
149 | 0 | LM_ERR("cannot convert string to integer '%.*s'\n", |
150 | 0 | ((str *)val)->len, ((str *)val)->s); |
151 | 0 | return -1; |
152 | 0 | } |
153 | 0 | *new_val = (void *)(long)i; |
154 | 0 | break; |
155 | 0 | } |
156 | 0 | goto error; |
157 | | |
158 | 0 | case CFG_INPUT_STRING: |
159 | 0 | if(val_type == CFG_VAR_INT) { |
160 | 0 | buf[snprintf(buf, sizeof(buf) - 1, "%ld", (long)val)] = '\0'; |
161 | 0 | *new_val = buf; |
162 | 0 | break; |
163 | |
|
164 | 0 | } else if(val_type == CFG_VAR_STRING) { |
165 | 0 | *new_val = val; |
166 | 0 | break; |
167 | |
|
168 | 0 | } else if(val_type == CFG_VAR_STR) { |
169 | 0 | if(!((str *)val)->s) { |
170 | 0 | *new_val = NULL; |
171 | 0 | break; |
172 | 0 | } |
173 | | /* the value may not be zero-terminated, thus, |
174 | | * a new variable has to be allocated with larger memory space */ |
175 | 0 | if(temp_string) |
176 | 0 | pkg_free(temp_string); |
177 | 0 | temp_string = (char *)pkg_malloc( |
178 | 0 | sizeof(char) * (((str *)val)->len + 1)); |
179 | 0 | if(!temp_string) { |
180 | 0 | PKG_MEM_ERROR; |
181 | 0 | return -1; |
182 | 0 | } |
183 | 0 | memcpy(temp_string, ((str *)val)->s, ((str *)val)->len); |
184 | 0 | temp_string[((str *)val)->len] = '\0'; |
185 | 0 | *new_val = (void *)temp_string; |
186 | 0 | break; |
187 | 0 | } |
188 | 0 | goto error; |
189 | | |
190 | 0 | case CFG_INPUT_STR: |
191 | 0 | if(val_type == CFG_VAR_INT) { |
192 | 0 | s.len = snprintf(buf, sizeof(buf) - 1, "%ld", (long)val); |
193 | 0 | buf[s.len] = '\0'; |
194 | 0 | s.s = buf; |
195 | 0 | *new_val = (void *)&s; |
196 | 0 | break; |
197 | |
|
198 | 0 | } else if(val_type == CFG_VAR_STRING) { |
199 | 0 | s.s = (char *)val; |
200 | 0 | s.len = (s.s) ? strlen(s.s) : 0; |
201 | 0 | *new_val = (void *)&s; |
202 | 0 | break; |
203 | |
|
204 | 0 | } else if(val_type == CFG_VAR_STR) { |
205 | 0 | *new_val = val; |
206 | 0 | break; |
207 | 0 | } |
208 | 0 | goto error; |
209 | 0 | } |
210 | | |
211 | 0 | return 0; |
212 | | |
213 | 0 | error: |
214 | 0 | LM_ERR("got a value with type %u, but expected %u\n", val_type, |
215 | 0 | CFG_INPUT_MASK(var_type)); |
216 | 0 | return -1; |
217 | 0 | } |
218 | | |
219 | | void convert_val_cleanup(void) |
220 | 0 | { |
221 | 0 | if(temp_string) { |
222 | 0 | pkg_free(temp_string); |
223 | 0 | temp_string = NULL; |
224 | 0 | } |
225 | 0 | } |
226 | | |
227 | | /* returns the size of the variable */ |
228 | | static int cfg_var_size(cfg_mapping_t *var) |
229 | 0 | { |
230 | 0 | switch(CFG_VAR_TYPE(var)) { |
231 | | |
232 | 0 | case CFG_VAR_INT: |
233 | 0 | return sizeof(int); |
234 | | |
235 | 0 | case CFG_VAR_STRING: |
236 | 0 | return sizeof(char *); |
237 | | |
238 | 0 | case CFG_VAR_STR: |
239 | 0 | return sizeof(str); |
240 | | |
241 | 0 | case CFG_VAR_POINTER: |
242 | 0 | return sizeof(void *); |
243 | | |
244 | 0 | default: |
245 | 0 | LM_CRIT("unknown type: %u\n", CFG_VAR_TYPE(var)); |
246 | 0 | return 0; |
247 | 0 | } |
248 | 0 | } |
249 | | |
250 | | /* Update the variables of the array within the meta structure |
251 | | * with the new default value. |
252 | | * The array is cloned before a change if clone is set to 1. |
253 | | */ |
254 | | static int cfg_update_defaults(cfg_group_meta_t *meta, cfg_group_t *group, |
255 | | cfg_mapping_t *var, char *new_val, int clone) |
256 | 0 | { |
257 | 0 | int i, clone_done = 0; |
258 | 0 | cfg_group_inst_t *array, *ginst; |
259 | |
|
260 | 0 | if(!(array = meta->array)) |
261 | 0 | return 0; |
262 | 0 | for(i = 0; i < meta->num; i++) { |
263 | 0 | ginst = (cfg_group_inst_t *)((char *)array |
264 | 0 | + (sizeof(cfg_group_inst_t) + group->size |
265 | 0 | - 1) |
266 | 0 | * i); |
267 | |
|
268 | 0 | if(!CFG_VAR_TEST(ginst, var)) { |
269 | | /* The variable uses the default value, it needs to be rewritten. */ |
270 | 0 | if(clone && !clone_done) { |
271 | | /* The array needs to be cloned before the modification */ |
272 | 0 | if(!(array = cfg_clone_array(meta, group))) |
273 | 0 | return -1; |
274 | 0 | ginst = (cfg_group_inst_t *)translate_pointer( |
275 | 0 | (char *)array, (char *)meta->array, (char *)ginst); |
276 | | /* re-link the array to the meta-data */ |
277 | 0 | meta->array = array; |
278 | 0 | clone_done = 1; |
279 | 0 | } |
280 | 0 | if((unsigned long)ginst->vars + var->offset) { |
281 | 0 | memcpy(ginst->vars + var->offset, new_val, cfg_var_size(var)); |
282 | 0 | } else { |
283 | 0 | LM_ERR("invalid variable offset\n"); |
284 | 0 | } |
285 | 0 | } |
286 | 0 | } |
287 | 0 | return 0; |
288 | 0 | } |
289 | | |
290 | | /* sets the value of a variable without the need of commit |
291 | | * |
292 | | * return value: |
293 | | * 0: success |
294 | | * -1: error |
295 | | * 1: variable has not been found |
296 | | */ |
297 | | int cfg_set_now(cfg_ctx_t *ctx, str *group_name, unsigned int *group_id, |
298 | | str *var_name, void *val, unsigned int val_type) |
299 | 0 | { |
300 | 0 | int i; |
301 | 0 | cfg_group_t *group; |
302 | 0 | cfg_mapping_t *var; |
303 | 0 | void *p, *v; |
304 | 0 | cfg_block_t *block = NULL; |
305 | 0 | str s, s2; |
306 | 0 | char *old_string = NULL; |
307 | 0 | void **replaced = NULL; |
308 | 0 | cfg_child_cb_t *child_cb = NULL; |
309 | 0 | cfg_group_inst_t *group_inst = NULL, *new_array = NULL; |
310 | 0 | unsigned char *var_block; |
311 | | |
312 | | /* verify the context even if we do not need it now |
313 | | * to make sure that a cfg driver has called the function |
314 | | * (very very weak security) */ |
315 | 0 | if(!ctx) { |
316 | 0 | LM_ERR("context is undefined\n"); |
317 | 0 | return -1; |
318 | 0 | } |
319 | | |
320 | 0 | if((val_type == CFG_VAR_UNSET) && !group_id) { |
321 | 0 | LM_ERR("Only group instance values can be deleted\n"); |
322 | 0 | return -1; |
323 | 0 | } |
324 | | |
325 | 0 | if(group_id && !cfg_shmized) { |
326 | | /* The config group has not been shmized yet, but |
327 | | * an additional instance of a variable needs to be added to the group. |
328 | | * Add this instance to the linked list of variables, they |
329 | | * will be fixed later. */ |
330 | 0 | return new_add_var(group_name, *group_id, var_name, val, val_type); |
331 | 0 | } |
332 | | |
333 | | /* look-up the group and the variable */ |
334 | 0 | if(cfg_lookup_var(group_name, var_name, &group, &var)) { |
335 | 0 | if(!cfg_shmized) { |
336 | | /* The group may be dynamic which is not yet ready |
337 | | * before forking */ |
338 | 0 | if((group = cfg_lookup_group(group_name->s, group_name->len)) |
339 | 0 | && (group->dynamic == CFG_GROUP_DYNAMIC)) |
340 | 0 | return cfg_set_script_var(group, var_name, val, val_type); |
341 | 0 | } |
342 | 0 | return 1; |
343 | 0 | } |
344 | | |
345 | | /* check whether the variable is read-only */ |
346 | 0 | if(var->def->type & CFG_READONLY) { |
347 | 0 | LM_ERR("variable is read-only\n"); |
348 | 0 | goto error0; |
349 | 0 | } |
350 | | |
351 | | /* The additional variable instances having per-child process callback |
352 | | * with CFG_CB_ONLY_ONCE flag cannot be rewritten. |
353 | | * The reason is that such variables typically set global parameters |
354 | | * as opposed to per-process variables. Hence, it is not possible to set |
355 | | * the group handle temporary to another block, and then reset it back later. */ |
356 | 0 | if(group_id && var->def->on_set_child_cb |
357 | 0 | && var->def->type & CFG_CB_ONLY_ONCE) { |
358 | 0 | LM_ERR("This variable does not support multiple values.\n"); |
359 | 0 | goto error0; |
360 | 0 | } |
361 | | |
362 | | /* check whether we have to convert the type */ |
363 | 0 | if((val_type != CFG_VAR_UNSET) |
364 | 0 | && convert_val(val_type, val, CFG_INPUT_TYPE(var), &v)) |
365 | 0 | goto error0; |
366 | | |
367 | 0 | if((val_type != CFG_VAR_UNSET) && (CFG_INPUT_TYPE(var) == CFG_INPUT_INT) |
368 | 0 | && (var->def->min || var->def->max)) { |
369 | | /* perform a simple min-max check for integers */ |
370 | 0 | if(((int)(long)v < var->def->min) || ((int)(long)v > var->def->max)) { |
371 | 0 | LM_ERR("integer value is out of range\n"); |
372 | 0 | goto error0; |
373 | 0 | } |
374 | 0 | } |
375 | | |
376 | 0 | if((val_type != CFG_VAR_UNSET) && var->def->on_change_cb) { |
377 | | /* Call the fixup function. |
378 | | * There is no need to set a temporary cfg handle, |
379 | | * because a single variable is changed */ |
380 | 0 | if(!group_id) { |
381 | 0 | var_block = *(group->handle); |
382 | 0 | } else { |
383 | 0 | if(!cfg_local) { |
384 | 0 | LM_ERR("Local configuration is missing\n"); |
385 | 0 | goto error0; |
386 | 0 | } |
387 | 0 | group_inst = cfg_find_group( |
388 | 0 | CFG_GROUP_META(cfg_local, group), group->size, *group_id); |
389 | 0 | if(!group_inst) { |
390 | 0 | LM_ERR("local group instance %.*s[%u] is not found\n", |
391 | 0 | group_name->len, group_name->s, *group_id); |
392 | 0 | goto error0; |
393 | 0 | } |
394 | 0 | var_block = group_inst->vars; |
395 | 0 | } |
396 | | |
397 | 0 | if(var->def->on_change_cb(var_block, group_name, var_name, &v) < 0) { |
398 | 0 | LM_ERR("fixup failed\n"); |
399 | 0 | goto error0; |
400 | 0 | } |
401 | 0 | } |
402 | | |
403 | | /* Set the per-child process callback only if the default value is changed. |
404 | | * The callback of other instances will be called when the config is |
405 | | * switched to that instance. */ |
406 | 0 | if(!group_id && var->def->on_set_child_cb) { |
407 | | /* get the name of the variable from the internal struct, |
408 | | * because var_name may be freed before the callback needs it */ |
409 | 0 | s.s = group->name; |
410 | 0 | s.len = group->name_len; |
411 | 0 | s2.s = var->def->name; |
412 | 0 | s2.len = var->name_len; |
413 | 0 | child_cb = cfg_child_cb_new( |
414 | 0 | &s, &s2, var->def->on_set_child_cb, var->def->type); |
415 | 0 | if(!child_cb) { |
416 | 0 | SHM_MEM_ERROR; |
417 | 0 | goto error0; |
418 | 0 | } |
419 | 0 | } |
420 | | |
421 | 0 | if(cfg_shmized) { |
422 | | /* make sure that nobody else replaces the global config |
423 | | * while the new one is prepared */ |
424 | 0 | CFG_WRITER_LOCK(); |
425 | |
|
426 | 0 | if(group_id) { |
427 | 0 | group_inst = cfg_find_group( |
428 | 0 | CFG_GROUP_META(*cfg_global, group), group->size, *group_id); |
429 | 0 | if(!group_inst) { |
430 | 0 | LM_ERR("global group instance %.*s[%u] is not found\n", |
431 | 0 | group_name->len, group_name->s, *group_id); |
432 | 0 | goto error; |
433 | 0 | } |
434 | 0 | if((val_type == CFG_VAR_UNSET) && !CFG_VAR_TEST(group_inst, var)) { |
435 | | /* nothing to do, the variable is not set in the group instance */ |
436 | 0 | CFG_WRITER_UNLOCK(); |
437 | 0 | LM_DBG("The variable is not set\n"); |
438 | 0 | return 0; |
439 | 0 | } |
440 | 0 | var_block = group_inst->vars; |
441 | 0 | } else { |
442 | 0 | group_inst = NULL; |
443 | 0 | var_block = CFG_GROUP_DATA(*cfg_global, group); |
444 | 0 | } |
445 | | |
446 | 0 | if(var->def->type & CFG_ATOMIC) { |
447 | | /* atomic change is allowed, we can rewrite the value |
448 | | * directly in the global config */ |
449 | 0 | p = var_block + var->offset; |
450 | |
|
451 | 0 | } else { |
452 | | /* clone the memory block, and prepare the modification */ |
453 | 0 | if(!(block = cfg_clone_global())) |
454 | 0 | goto error; |
455 | | |
456 | 0 | if(group_inst) { |
457 | | /* The additional array of the group needs to be also cloned. |
458 | | * When any of the variables within this array is changed, then |
459 | | * the complete config block and this array is replaced. */ |
460 | 0 | if(!(new_array = cfg_clone_array( |
461 | 0 | CFG_GROUP_META(*cfg_global, group), group))) |
462 | 0 | goto error; |
463 | 0 | group_inst = (cfg_group_inst_t *)translate_pointer( |
464 | 0 | (char *)new_array, |
465 | 0 | (char *)CFG_GROUP_META(*cfg_global, group)->array, |
466 | 0 | (char *)group_inst); |
467 | 0 | var_block = group_inst->vars; |
468 | 0 | CFG_GROUP_META(block, group)->array = new_array; |
469 | 0 | } else { |
470 | | /* The additional array may need to be replaced depending |
471 | | * on whether or not there is any variable in the array set |
472 | | * to the default value which is changed now. If this is the case, |
473 | | * then the array will be replaced later when the variables are |
474 | | * updated. |
475 | | */ |
476 | 0 | var_block = CFG_GROUP_DATA(block, group); |
477 | 0 | } |
478 | 0 | p = var_block + var->offset; |
479 | 0 | } |
480 | 0 | } else { |
481 | | /* we are allowed to rewrite the value on-the-fly |
482 | | * The handle either points to group->vars, or to the |
483 | | * shared memory block (dynamic group) */ |
484 | 0 | p = *(group->handle) + var->offset; |
485 | 0 | } |
486 | | |
487 | 0 | if(val_type != CFG_VAR_UNSET) { |
488 | | /* set the new value */ |
489 | 0 | switch(CFG_VAR_TYPE(var)) { |
490 | 0 | case CFG_VAR_INT: |
491 | 0 | *(int *)p = (int)(long)v; |
492 | 0 | break; |
493 | | |
494 | 0 | case CFG_VAR_STRING: |
495 | | /* clone the string to shm mem */ |
496 | 0 | s.s = v; |
497 | 0 | s.len = (s.s) ? strlen(s.s) : 0; |
498 | 0 | if(cfg_clone_str(&s, &s)) |
499 | 0 | goto error; |
500 | 0 | old_string = *(char **)p; |
501 | 0 | *(char **)p = s.s; |
502 | 0 | break; |
503 | | |
504 | 0 | case CFG_VAR_STR: |
505 | | /* clone the string to shm mem */ |
506 | 0 | s = *(str *)v; |
507 | 0 | if(cfg_clone_str(&s, &s)) |
508 | 0 | goto error; |
509 | 0 | old_string = *(char **)p; |
510 | 0 | memcpy(p, &s, sizeof(str)); |
511 | 0 | break; |
512 | | |
513 | 0 | case CFG_VAR_POINTER: |
514 | 0 | *(void **)p = v; |
515 | 0 | break; |
516 | 0 | } |
517 | 0 | if(group_inst && !CFG_VAR_TEST_AND_SET(group_inst, var)) |
518 | 0 | old_string = NULL; /* the string is the same as the default one, |
519 | | * it cannot be freed */ |
520 | 0 | } else { |
521 | | /* copy the default value to the group instance */ |
522 | 0 | if((CFG_VAR_TYPE(var) == CFG_VAR_STRING) |
523 | 0 | || (CFG_VAR_TYPE(var) == CFG_VAR_STR)) |
524 | 0 | old_string = *(char **)p; |
525 | 0 | memcpy(p, CFG_GROUP_DATA(*cfg_global, group) + var->offset, |
526 | 0 | cfg_var_size(var)); |
527 | |
|
528 | 0 | CFG_VAR_TEST_AND_RESET(group_inst, var); |
529 | 0 | } |
530 | | |
531 | 0 | if(cfg_shmized) { |
532 | 0 | if(!group_inst) { |
533 | | /* the default value is changed, the copies of this value |
534 | | * need to be also updated */ |
535 | 0 | if(cfg_update_defaults( |
536 | 0 | CFG_GROUP_META(block ? block : *cfg_global, group), |
537 | 0 | group, var, p, block ? 1 : 0) /* clone if needed */ |
538 | 0 | ) |
539 | 0 | goto error; |
540 | 0 | if(block |
541 | 0 | && (CFG_GROUP_META(block, group)->array |
542 | 0 | != CFG_GROUP_META(*cfg_global, group)->array)) |
543 | 0 | new_array = CFG_GROUP_META(block, group)->array; |
544 | 0 | } |
545 | | |
546 | 0 | if(old_string || new_array) { |
547 | | /* prepare the array of the replaced strings, |
548 | | * and replaced group instances, |
549 | | * they will be freed when the old block is freed */ |
550 | 0 | replaced = (void **)shm_malloc( |
551 | 0 | sizeof(void *) |
552 | 0 | * ((old_string ? 1 : 0) + (new_array ? 1 : 0) + 1)); |
553 | 0 | if(!replaced) { |
554 | 0 | SHM_MEM_ERROR; |
555 | 0 | goto error; |
556 | 0 | } |
557 | 0 | i = 0; |
558 | 0 | if(old_string) { |
559 | 0 | replaced[i] = old_string; |
560 | 0 | i++; |
561 | 0 | } |
562 | 0 | if(new_array) { |
563 | 0 | replaced[i] = CFG_GROUP_META(*cfg_global, group)->array; |
564 | 0 | i++; |
565 | 0 | } |
566 | 0 | replaced[i] = NULL; |
567 | 0 | } |
568 | | /* replace the global config with the new one */ |
569 | 0 | if(block) |
570 | 0 | cfg_install_global(block, replaced, child_cb, child_cb); |
571 | 0 | CFG_WRITER_UNLOCK(); |
572 | 0 | } else { |
573 | | /* cfg_set() may be called more than once before forking */ |
574 | 0 | if(old_string && (var->flag & cfg_var_shmized)) |
575 | 0 | shm_free(old_string); |
576 | | |
577 | | /* flag the variable because there is no need |
578 | | * to shmize it again */ |
579 | 0 | var->flag |= cfg_var_shmized; |
580 | | |
581 | | /* the global config does not have to be replaced, |
582 | | * but the child callback has to be installed, otherwise the |
583 | | * child processes will miss the change */ |
584 | 0 | if(child_cb) |
585 | 0 | cfg_install_child_cb(child_cb, child_cb); |
586 | 0 | } |
587 | | |
588 | 0 | if(val_type == CFG_VAR_INT) |
589 | 0 | LM_INFO("%.*s.%.*s has been changed to %d\n", group_name->len, |
590 | 0 | group_name->s, var_name->len, var_name->s, (int)(long)val); |
591 | | |
592 | 0 | else if(val_type == CFG_VAR_STRING) |
593 | 0 | LM_INFO("%.*s.%.*s has been changed to \"%s\"\n", group_name->len, |
594 | 0 | group_name->s, var_name->len, var_name->s, (char *)val); |
595 | | |
596 | 0 | else if(val_type == CFG_VAR_STR) |
597 | 0 | LM_INFO("%.*s.%.*s has been changed to \"%.*s\"\n", group_name->len, |
598 | 0 | group_name->s, var_name->len, var_name->s, ((str *)val)->len, |
599 | 0 | ((str *)val)->s); |
600 | | |
601 | 0 | else if(val_type == CFG_VAR_UNSET) |
602 | 0 | LM_INFO("%.*s.%.*s has been deleted\n", group_name->len, group_name->s, |
603 | 0 | var_name->len, var_name->s); |
604 | | |
605 | 0 | else |
606 | 0 | LM_INFO("%.*s.%.*s has been changed\n", group_name->len, group_name->s, |
607 | 0 | var_name->len, var_name->s); |
608 | |
|
609 | 0 | if(group_id) |
610 | 0 | LM_INFO("group id = %u\n", *group_id); |
611 | |
|
612 | 0 | convert_val_cleanup(); |
613 | 0 | return 0; |
614 | | |
615 | 0 | error: |
616 | 0 | if(cfg_shmized) |
617 | 0 | CFG_WRITER_UNLOCK(); |
618 | 0 | if(block) |
619 | 0 | cfg_block_free(block); |
620 | 0 | if(new_array) |
621 | 0 | shm_free(new_array); |
622 | 0 | if(child_cb) |
623 | 0 | cfg_child_cb_free_list(child_cb); |
624 | 0 | if(replaced) |
625 | 0 | shm_free(replaced); |
626 | |
|
627 | 0 | error0: |
628 | 0 | LM_ERR("failed to set the variable: %.*s.%.*s\n", group_name->len, |
629 | 0 | group_name->s, var_name->len, var_name->s); |
630 | | |
631 | |
|
632 | 0 | convert_val_cleanup(); |
633 | 0 | return -1; |
634 | 0 | } |
635 | | |
636 | | /* wrapper function for cfg_set_now */ |
637 | | int cfg_set_now_int(cfg_ctx_t *ctx, str *group_name, unsigned int *group_id, |
638 | | str *var_name, int val) |
639 | 0 | { |
640 | 0 | return cfg_set_now(ctx, group_name, group_id, var_name, (void *)(long)val, |
641 | 0 | CFG_VAR_INT); |
642 | 0 | } |
643 | | |
644 | | /* wrapper function for cfg_set_now */ |
645 | | int cfg_set_now_string(cfg_ctx_t *ctx, str *group_name, unsigned int *group_id, |
646 | | str *var_name, char *val) |
647 | 0 | { |
648 | 0 | return cfg_set_now( |
649 | 0 | ctx, group_name, group_id, var_name, (void *)val, CFG_VAR_STRING); |
650 | 0 | } |
651 | | |
652 | | /* wrapper function for cfg_set_now */ |
653 | | int cfg_set_now_str(cfg_ctx_t *ctx, str *group_name, unsigned int *group_id, |
654 | | str *var_name, str *val) |
655 | 0 | { |
656 | 0 | return cfg_set_now( |
657 | 0 | ctx, group_name, group_id, var_name, (void *)val, CFG_VAR_STR); |
658 | 0 | } |
659 | | |
660 | | /* Delete a variable from the group instance. |
661 | | * wrapper function for cfg_set_now */ |
662 | | int cfg_del_now( |
663 | | cfg_ctx_t *ctx, str *group_name, unsigned int *group_id, str *var_name) |
664 | 0 | { |
665 | 0 | return cfg_set_now( |
666 | 0 | ctx, group_name, group_id, var_name, NULL, CFG_VAR_UNSET); |
667 | 0 | } |
668 | | |
669 | | /* sets the value of a variable but does not commit the change |
670 | | * |
671 | | * return value: |
672 | | * 0: success |
673 | | * -1: error |
674 | | * 1: variable has not been found |
675 | | */ |
676 | | int cfg_set_delayed(cfg_ctx_t *ctx, str *group_name, unsigned int *group_id, |
677 | | str *var_name, void *val, unsigned int val_type) |
678 | 0 | { |
679 | 0 | cfg_group_t *group; |
680 | 0 | cfg_mapping_t *var; |
681 | 0 | void *v; |
682 | 0 | unsigned char *temp_handle; |
683 | 0 | int temp_handle_created; |
684 | 0 | cfg_changed_var_t *changed = NULL, **changed_p; |
685 | 0 | int size; |
686 | 0 | str s; |
687 | 0 | cfg_group_inst_t *group_inst = NULL; |
688 | 0 | unsigned char *var_block; |
689 | |
|
690 | 0 | if(!cfg_shmized) |
691 | | /* the cfg has not been shmized yet, there is no |
692 | | * point in registering the change and committing it later */ |
693 | 0 | return cfg_set_now(ctx, group_name, group_id, var_name, val, val_type); |
694 | | |
695 | 0 | if(!ctx) { |
696 | 0 | LM_ERR("context is undefined\n"); |
697 | 0 | return -1; |
698 | 0 | } |
699 | | |
700 | 0 | if((val_type == CFG_VAR_UNSET) && !group_id) { |
701 | 0 | LM_ERR("Only group instance values can be deleted\n"); |
702 | 0 | return -1; |
703 | 0 | } |
704 | | |
705 | | /* look-up the group and the variable */ |
706 | 0 | if(cfg_lookup_var(group_name, var_name, &group, &var)) |
707 | 0 | return 1; |
708 | | |
709 | | /* check whether the variable is read-only */ |
710 | 0 | if(var->def->type & CFG_READONLY) { |
711 | 0 | LM_ERR("variable is read-only\n"); |
712 | 0 | goto error0; |
713 | 0 | } |
714 | | |
715 | | /* The additional variable instances having per-child process callback |
716 | | * with CFG_CB_ONLY_ONCE flag cannot be rewritten. |
717 | | * The reason is that such variables typically set global parameters |
718 | | * as opposed to per-process variables. Hence, it is not possible to set |
719 | | * the group handle temporary to another block, and then reset it back later. */ |
720 | 0 | if(group_id && var->def->on_set_child_cb |
721 | 0 | && var->def->type & CFG_CB_ONLY_ONCE) { |
722 | 0 | LM_ERR("This variable does not support multiple values.\n"); |
723 | 0 | goto error0; |
724 | 0 | } |
725 | | |
726 | | /* check whether we have to convert the type */ |
727 | 0 | if((val_type != CFG_VAR_UNSET) |
728 | 0 | && convert_val(val_type, val, CFG_INPUT_TYPE(var), &v)) |
729 | 0 | goto error0; |
730 | | |
731 | 0 | if((val_type != CFG_VAR_UNSET) && (CFG_INPUT_TYPE(var) == CFG_INPUT_INT) |
732 | 0 | && (var->def->min || var->def->max)) { |
733 | | /* perform a simple min-max check for integers */ |
734 | 0 | if(((int)(long)v < var->def->min) || ((int)(long)v > var->def->max)) { |
735 | 0 | LM_ERR("integer value is out of range\n"); |
736 | 0 | goto error0; |
737 | 0 | } |
738 | 0 | } |
739 | | |
740 | | /* the ctx must be locked while reading and writing |
741 | | * the list of changed variables */ |
742 | 0 | CFG_CTX_LOCK(ctx); |
743 | |
|
744 | 0 | if((val_type != CFG_VAR_UNSET) && var->def->on_change_cb) { |
745 | | /* The fixup function must see also the |
746 | | * not yet committed values, so a temporary handle |
747 | | * must be prepared that points to the new config. |
748 | | * Only the values within the group are applied, |
749 | | * other modifications are not visible to the callback. |
750 | | * The local config is the base. */ |
751 | 0 | if(!group_id) { |
752 | 0 | var_block = *(group->handle); |
753 | 0 | } else { |
754 | 0 | if(!cfg_local) { |
755 | 0 | LM_ERR("Local configuration is missing\n"); |
756 | 0 | goto error; |
757 | 0 | } |
758 | 0 | group_inst = cfg_find_group( |
759 | 0 | CFG_GROUP_META(cfg_local, group), group->size, *group_id); |
760 | 0 | if(!group_inst) { |
761 | 0 | LM_ERR("local group instance %.*s[%u] is not found\n", |
762 | 0 | group_name->len, group_name->s, *group_id); |
763 | 0 | goto error; |
764 | 0 | } |
765 | 0 | var_block = group_inst->vars; |
766 | 0 | } |
767 | | |
768 | 0 | if(ctx->changed_first) { |
769 | 0 | temp_handle = (unsigned char *)pkg_malloc(group->size); |
770 | 0 | if(!temp_handle) { |
771 | 0 | PKG_MEM_ERROR; |
772 | 0 | goto error; |
773 | 0 | } |
774 | 0 | temp_handle_created = 1; |
775 | 0 | memcpy(temp_handle, var_block, group->size); |
776 | | |
777 | | /* apply the changes */ |
778 | 0 | for(changed = ctx->changed_first; changed; |
779 | 0 | changed = changed->next) { |
780 | 0 | if(changed->group != group) |
781 | 0 | continue; |
782 | 0 | if((!group_id && !changed->group_id_set) /* default values */ |
783 | 0 | || (group_id && !changed->group_id_set |
784 | 0 | && !CFG_VAR_TEST(group_inst, changed->var)) |
785 | | /* default value is changed which affects the group_instance */ |
786 | 0 | || (group_id && changed->group_id_set |
787 | 0 | && (*group_id == changed->group_id)) |
788 | | /* change within the group instance */ |
789 | 0 | ) |
790 | 0 | memcpy(temp_handle + changed->var->offset, |
791 | 0 | changed->new_val.vraw, cfg_var_size(changed->var)); |
792 | 0 | } |
793 | 0 | } else { |
794 | | /* there is not any change */ |
795 | 0 | temp_handle = var_block; |
796 | 0 | temp_handle_created = 0; |
797 | 0 | } |
798 | | |
799 | 0 | if(var->def->on_change_cb(temp_handle, group_name, var_name, &v) < 0) { |
800 | 0 | LM_ERR("fixup failed\n"); |
801 | 0 | if(temp_handle_created) |
802 | 0 | pkg_free(temp_handle); |
803 | 0 | goto error; |
804 | 0 | } |
805 | 0 | if(temp_handle_created) |
806 | 0 | pkg_free(temp_handle); |
807 | 0 | } |
808 | | |
809 | | /* everything went ok, we can add the new value to the list */ |
810 | 0 | size = sizeof(cfg_changed_var_t) - sizeof(((cfg_changed_var_t *)0)->new_val) |
811 | 0 | + ((val_type != CFG_VAR_UNSET) ? cfg_var_size(var) : 0); |
812 | 0 | changed = (cfg_changed_var_t *)shm_malloc(size); |
813 | 0 | if(!changed) { |
814 | 0 | SHM_MEM_ERROR; |
815 | 0 | goto error; |
816 | 0 | } |
817 | 0 | memset(changed, 0, size); |
818 | 0 | changed->group = group; |
819 | 0 | changed->var = var; |
820 | 0 | if(group_id) { |
821 | 0 | changed->group_id = *group_id; |
822 | 0 | changed->group_id_set = 1; |
823 | 0 | } |
824 | |
|
825 | 0 | if(val_type != CFG_VAR_UNSET) { |
826 | 0 | switch(CFG_VAR_TYPE(var)) { |
827 | | |
828 | 0 | case CFG_VAR_INT: |
829 | 0 | changed->new_val.vint = (int)(long)v; |
830 | 0 | break; |
831 | | |
832 | 0 | case CFG_VAR_STRING: |
833 | | /* clone the string to shm mem */ |
834 | 0 | s.s = v; |
835 | 0 | s.len = (s.s) ? strlen(s.s) : 0; |
836 | 0 | if(cfg_clone_str(&s, &s)) |
837 | 0 | goto error; |
838 | 0 | changed->new_val.vp = s.s; |
839 | 0 | break; |
840 | | |
841 | 0 | case CFG_VAR_STR: |
842 | | /* clone the string to shm mem */ |
843 | 0 | s = *(str *)v; |
844 | 0 | if(cfg_clone_str(&s, &s)) |
845 | 0 | goto error; |
846 | 0 | changed->new_val.vstr = s; |
847 | 0 | break; |
848 | | |
849 | 0 | case CFG_VAR_POINTER: |
850 | 0 | changed->new_val.vp = v; |
851 | 0 | break; |
852 | 0 | } |
853 | 0 | } else { |
854 | 0 | changed->del_value = 1; |
855 | 0 | } |
856 | | |
857 | | /* Order the changes by group + group_id + original order. |
858 | | * Hence, the list is still kept in order within the group. |
859 | | * The changes can be committed faster this way, the group instances |
860 | | * do not have to be looked-up for each and every variable. */ |
861 | | /* Check whether there is any variable in the list which |
862 | | * belongs to the same group */ |
863 | 0 | for(changed_p = &ctx->changed_first; |
864 | 0 | *changed_p && ((*changed_p)->group != changed->group); |
865 | 0 | changed_p = &(*changed_p)->next) |
866 | 0 | ; |
867 | | /* try to find the group instance, and move changed_p to the end of |
868 | | * the instance. */ |
869 | 0 | for(; *changed_p && ((*changed_p)->group == changed->group) |
870 | 0 | && (!(*changed_p)->group_id_set |
871 | 0 | || ((*changed_p)->group_id_set && changed->group_id_set |
872 | 0 | && ((*changed_p)->group_id <= changed->group_id))); |
873 | 0 | changed_p = &(*changed_p)->next) |
874 | 0 | ; |
875 | | /* Add the new variable before *changed_p */ |
876 | 0 | changed->next = *changed_p; |
877 | 0 | *changed_p = changed; |
878 | |
|
879 | 0 | CFG_CTX_UNLOCK(ctx); |
880 | |
|
881 | 0 | if(val_type == CFG_VAR_INT) |
882 | 0 | LM_INFO("%.*s.%.*s is going to be changed to %d " |
883 | 0 | "[context=%p]\n", |
884 | 0 | group_name->len, group_name->s, var_name->len, var_name->s, |
885 | 0 | (int)(long)val, ctx); |
886 | | |
887 | 0 | else if(val_type == CFG_VAR_STRING) |
888 | 0 | LM_INFO("%.*s.%.*s is going to be changed to \"%s\" " |
889 | 0 | "[context=%p]\n", |
890 | 0 | group_name->len, group_name->s, var_name->len, var_name->s, |
891 | 0 | (char *)val, ctx); |
892 | | |
893 | 0 | else if(val_type == CFG_VAR_STR) |
894 | 0 | LM_INFO("%.*s.%.*s is going to be changed to \"%.*s\" " |
895 | 0 | "[context=%p]\n", |
896 | 0 | group_name->len, group_name->s, var_name->len, var_name->s, |
897 | 0 | ((str *)val)->len, ((str *)val)->s, ctx); |
898 | | |
899 | 0 | else if(val_type == CFG_VAR_UNSET) |
900 | 0 | LM_INFO("%.*s.%.*s is going to be deleted " |
901 | 0 | "[context=%p]\n", |
902 | 0 | group_name->len, group_name->s, var_name->len, var_name->s, |
903 | 0 | ctx); |
904 | | |
905 | 0 | else |
906 | 0 | LM_INFO("%.*s.%.*s is going to be changed " |
907 | 0 | "[context=%p]\n", |
908 | 0 | group_name->len, group_name->s, var_name->len, var_name->s, |
909 | 0 | ctx); |
910 | |
|
911 | 0 | if(group_id) |
912 | 0 | LM_INFO("group id = %u [context=%p]\n", *group_id, ctx); |
913 | |
|
914 | 0 | convert_val_cleanup(); |
915 | 0 | return 0; |
916 | | |
917 | 0 | error: |
918 | 0 | CFG_CTX_UNLOCK(ctx); |
919 | 0 | if(changed) |
920 | 0 | shm_free(changed); |
921 | 0 | error0: |
922 | 0 | LM_ERR("failed to set the variable: %.*s.%.*s\n", group_name->len, |
923 | 0 | group_name->s, var_name->len, var_name->s); |
924 | |
|
925 | 0 | convert_val_cleanup(); |
926 | 0 | return -1; |
927 | 0 | } |
928 | | |
929 | | /* wrapper function for cfg_set_delayed */ |
930 | | int cfg_set_delayed_int(cfg_ctx_t *ctx, str *group_name, unsigned int *group_id, |
931 | | str *var_name, int val) |
932 | 0 | { |
933 | 0 | return cfg_set_delayed(ctx, group_name, group_id, var_name, |
934 | 0 | (void *)(long)val, CFG_VAR_INT); |
935 | 0 | } |
936 | | |
937 | | /* wrapper function for cfg_set_delayed */ |
938 | | int cfg_set_delayed_string(cfg_ctx_t *ctx, str *group_name, |
939 | | unsigned int *group_id, str *var_name, char *val) |
940 | 0 | { |
941 | 0 | return cfg_set_delayed( |
942 | 0 | ctx, group_name, group_id, var_name, (void *)val, CFG_VAR_STRING); |
943 | 0 | } |
944 | | |
945 | | /* wrapper function for cfg_set_delayed */ |
946 | | int cfg_set_delayed_str(cfg_ctx_t *ctx, str *group_name, unsigned int *group_id, |
947 | | str *var_name, str *val) |
948 | 0 | { |
949 | 0 | return cfg_set_delayed( |
950 | 0 | ctx, group_name, group_id, var_name, (void *)val, CFG_VAR_STR); |
951 | 0 | } |
952 | | |
953 | | /* Delete a variable from the group instance. |
954 | | * wrapper function for cfg_set_delayed */ |
955 | | int cfg_del_delayed( |
956 | | cfg_ctx_t *ctx, str *group_name, unsigned int *group_id, str *var_name) |
957 | 0 | { |
958 | 0 | return cfg_set_delayed( |
959 | 0 | ctx, group_name, group_id, var_name, NULL, CFG_VAR_UNSET); |
960 | 0 | } |
961 | | |
962 | | /* commits the previously prepared changes within the context */ |
963 | | int cfg_commit(cfg_ctx_t *ctx) |
964 | 0 | { |
965 | 0 | int replaced_num = 0; |
966 | 0 | cfg_changed_var_t *changed, *changed2; |
967 | 0 | cfg_block_t *block = NULL; |
968 | 0 | void **replaced = NULL; |
969 | 0 | cfg_child_cb_t *child_cb; |
970 | 0 | cfg_child_cb_t *child_cb_first = NULL; |
971 | 0 | cfg_child_cb_t *child_cb_last = NULL; |
972 | 0 | int size; |
973 | 0 | void *p; |
974 | 0 | str s, s2; |
975 | 0 | cfg_group_t *group; |
976 | 0 | cfg_group_inst_t *group_inst = NULL; |
977 | |
|
978 | 0 | if(!ctx) { |
979 | 0 | LM_ERR("context is undefined\n"); |
980 | 0 | return -1; |
981 | 0 | } |
982 | | |
983 | 0 | if(!cfg_shmized) |
984 | 0 | return 0; /* nothing to do */ |
985 | | |
986 | | /* the ctx must be locked while reading and writing |
987 | | * the list of changed variables */ |
988 | 0 | CFG_CTX_LOCK(ctx); |
989 | | |
990 | | /* is there any change? */ |
991 | 0 | if(!ctx->changed_first) |
992 | 0 | goto done; |
993 | | |
994 | | /* Count the number of replaced strings, |
995 | | * and replaced group arrays. |
996 | | * Prepare the linked list of per-child process |
997 | | * callbacks, that will be added to the global list. */ |
998 | 0 | for(changed = ctx->changed_first, group = NULL; changed; |
999 | 0 | changed = changed->next) { |
1000 | | /* Each string/str potentially causes an old string to be freed |
1001 | | * unless the variable of an additional group instance is set |
1002 | | * which uses the default value. This case cannot be determined |
1003 | | * without locking *cfg_global, hence, it is better to count these |
1004 | | * strings as well even though the slot might not be used later. */ |
1005 | 0 | if((CFG_VAR_TYPE(changed->var) == CFG_VAR_STRING) |
1006 | 0 | || (CFG_VAR_TYPE(changed->var) == CFG_VAR_STR)) |
1007 | 0 | replaced_num++; |
1008 | | |
1009 | | /* See the above comments for strings */ |
1010 | 0 | if(group != changed->group) { |
1011 | 0 | replaced_num++; |
1012 | 0 | group = changed->group; |
1013 | 0 | } |
1014 | |
|
1015 | 0 | if(changed->group && !changed->group_id_set |
1016 | 0 | && changed->var->def->on_set_child_cb) { |
1017 | 0 | s.s = changed->group->name; |
1018 | 0 | s.len = changed->group->name_len; |
1019 | 0 | s2.s = changed->var->def->name; |
1020 | 0 | s2.len = changed->var->name_len; |
1021 | 0 | child_cb = cfg_child_cb_new(&s, &s2, |
1022 | 0 | changed->var->def->on_set_child_cb, |
1023 | 0 | changed->var->def->type); |
1024 | 0 | if(!child_cb) |
1025 | 0 | goto error0; |
1026 | | |
1027 | 0 | if(child_cb_last) |
1028 | 0 | child_cb_last->next = child_cb; |
1029 | 0 | else |
1030 | 0 | child_cb_first = child_cb; |
1031 | 0 | child_cb_last = child_cb; |
1032 | 0 | } |
1033 | 0 | } |
1034 | | |
1035 | 0 | if(replaced_num) { |
1036 | | /* allocate memory for the replaced string array */ |
1037 | 0 | size = sizeof(void *) * (replaced_num + 1); |
1038 | 0 | replaced = (void **)shm_malloc(size); |
1039 | 0 | if(!replaced) { |
1040 | 0 | SHM_MEM_ERROR; |
1041 | 0 | goto error0; |
1042 | 0 | } |
1043 | 0 | memset(replaced, 0, size); |
1044 | 0 | } else { |
1045 | | /* no replacement */ |
1046 | 0 | LM_INFO("commit operation executed without having changes\n"); |
1047 | 0 | return 0; |
1048 | 0 | } |
1049 | | |
1050 | | /* make sure that nobody else replaces the global config |
1051 | | * while the new one is prepared */ |
1052 | 0 | CFG_WRITER_LOCK(); |
1053 | | |
1054 | | /* clone the memory block, and prepare the modification */ |
1055 | 0 | if(!(block = cfg_clone_global())) |
1056 | 0 | goto error; |
1057 | | |
1058 | | /* Apply the modifications to the buffer. |
1059 | | * Note that the cycle relies on the order of the groups and group |
1060 | | * instances, i.e. the order is group + group_id + order of commits. */ |
1061 | 0 | replaced_num = 0; |
1062 | 0 | for(changed = ctx->changed_first, group = NULL; /* group points to the |
1063 | | * last group array that |
1064 | | * has been cloned */ |
1065 | 0 | changed; changed = changed->next) { |
1066 | 0 | if(!changed->group_id_set) { |
1067 | 0 | p = CFG_GROUP_DATA(block, changed->group) + changed->var->offset; |
1068 | 0 | group_inst = NULL; /* force the look-up of the next group_inst */ |
1069 | 0 | } else { |
1070 | 0 | if(group != changed->group) { |
1071 | | /* The group array has not been cloned yet. */ |
1072 | 0 | group = changed->group; |
1073 | 0 | if(!(CFG_GROUP_META(block, group)->array = cfg_clone_array( |
1074 | 0 | CFG_GROUP_META(*cfg_global, group), group))) { |
1075 | 0 | LM_ERR("group array cannot be cloned for %.*s[%u]\n", |
1076 | 0 | group->name_len, group->name, changed->group_id); |
1077 | 0 | goto error; |
1078 | 0 | } |
1079 | | |
1080 | 0 | replaced[replaced_num] = |
1081 | 0 | CFG_GROUP_META(*cfg_global, group)->array; |
1082 | 0 | replaced_num++; |
1083 | |
|
1084 | 0 | group_inst = NULL; /* fore the look-up of group_inst */ |
1085 | 0 | } |
1086 | 0 | if(group |
1087 | 0 | && (!group_inst || (group_inst->id != changed->group_id))) { |
1088 | 0 | group_inst = cfg_find_group(CFG_GROUP_META(block, group), |
1089 | 0 | group->size, changed->group_id); |
1090 | 0 | } |
1091 | 0 | if(group && !group_inst) { |
1092 | 0 | LM_ERR("global group instance %.*s[%u] is not found\n", |
1093 | 0 | group->name_len, group->name, changed->group_id); |
1094 | 0 | goto error; |
1095 | 0 | } |
1096 | 0 | p = group_inst->vars + changed->var->offset; |
1097 | 0 | } |
1098 | 0 | if(p == NULL) { |
1099 | 0 | LM_ERR("failed to resolve valid variable offset\n"); |
1100 | 0 | goto error; |
1101 | 0 | } |
1102 | | |
1103 | 0 | if(((changed->group_id_set && !changed->del_value |
1104 | 0 | && CFG_VAR_TEST_AND_SET(group_inst, changed->var)) |
1105 | 0 | || (changed->group_id_set && changed->del_value |
1106 | 0 | && CFG_VAR_TEST_AND_RESET(group_inst, changed->var)) |
1107 | 0 | || !changed->group_id_set) |
1108 | 0 | && ((CFG_VAR_TYPE(changed->var) == CFG_VAR_STRING) |
1109 | 0 | || (CFG_VAR_TYPE(changed->var) == CFG_VAR_STR))) { |
1110 | 0 | replaced[replaced_num] = *(char **)p; |
1111 | 0 | if(replaced[replaced_num]) |
1112 | 0 | replaced_num++; |
1113 | | /* else do not increase replaced_num, because |
1114 | | * the cfg_block_free() will stop at the first |
1115 | | * NULL value */ |
1116 | 0 | } |
1117 | |
|
1118 | 0 | if(!changed->del_value) |
1119 | 0 | memcpy(p, changed->new_val.vraw, cfg_var_size(changed->var)); |
1120 | 0 | else |
1121 | 0 | memcpy(p, |
1122 | 0 | CFG_GROUP_DATA(block, changed->group) |
1123 | 0 | + changed->var->offset, |
1124 | 0 | cfg_var_size(changed->var)); |
1125 | | |
1126 | |
|
1127 | 0 | if(!changed->group_id_set) { |
1128 | | /* the default value is changed, the copies of this value |
1129 | | * need to be also updated */ |
1130 | 0 | if(cfg_update_defaults(CFG_GROUP_META(block, changed->group), |
1131 | 0 | changed->group, changed->var, p, |
1132 | 0 | (group != changed->group)) /* clone if the array |
1133 | | * has not been cloned yet */ |
1134 | 0 | ) |
1135 | 0 | goto error; |
1136 | 0 | if((group != changed->group) |
1137 | 0 | && (CFG_GROUP_META(block, changed->group)->array |
1138 | 0 | != CFG_GROUP_META(*cfg_global, changed->group) |
1139 | 0 | ->array)) { |
1140 | | /* The array has been cloned */ |
1141 | 0 | group = changed->group; |
1142 | |
|
1143 | 0 | replaced[replaced_num] = |
1144 | 0 | CFG_GROUP_META(*cfg_global, group)->array; |
1145 | 0 | replaced_num++; |
1146 | 0 | } |
1147 | 0 | } |
1148 | 0 | } |
1149 | | |
1150 | | /* replace the global config with the new one */ |
1151 | 0 | cfg_install_global(block, replaced, child_cb_first, child_cb_last); |
1152 | 0 | CFG_WRITER_UNLOCK(); |
1153 | | |
1154 | | /* free the changed list */ |
1155 | 0 | for(changed = ctx->changed_first; changed; changed = changed2) { |
1156 | 0 | changed2 = changed->next; |
1157 | 0 | shm_free(changed); |
1158 | 0 | } |
1159 | 0 | ctx->changed_first = NULL; |
1160 | |
|
1161 | 0 | done: |
1162 | 0 | LM_INFO("config changes have been applied [context=%p]\n", ctx); |
1163 | |
|
1164 | 0 | CFG_CTX_UNLOCK(ctx); |
1165 | 0 | return 0; |
1166 | | |
1167 | 0 | error: |
1168 | 0 | if(block) { |
1169 | | /* clean the new block from the cloned arrays */ |
1170 | 0 | for(group = cfg_group; group; group = group->next) |
1171 | 0 | if(CFG_GROUP_META(block, group)->array |
1172 | 0 | && (CFG_GROUP_META(block, group)->array |
1173 | 0 | != CFG_GROUP_META(*cfg_global, group)->array)) |
1174 | 0 | shm_free(CFG_GROUP_META(block, group)->array); |
1175 | | /* the block can be freed outside of the writer lock */ |
1176 | 0 | } |
1177 | 0 | CFG_WRITER_UNLOCK(); |
1178 | 0 | if(block) |
1179 | 0 | shm_free(block); |
1180 | |
|
1181 | 0 | error0: |
1182 | 0 | CFG_CTX_UNLOCK(ctx); |
1183 | |
|
1184 | 0 | if(child_cb_first) |
1185 | 0 | cfg_child_cb_free_list(child_cb_first); |
1186 | 0 | if(replaced) |
1187 | 0 | shm_free(replaced); |
1188 | |
|
1189 | 0 | return -1; |
1190 | 0 | } |
1191 | | |
1192 | | /* drops the not yet committed changes within the context */ |
1193 | | int cfg_rollback(cfg_ctx_t *ctx) |
1194 | 0 | { |
1195 | 0 | cfg_changed_var_t *changed, *changed2; |
1196 | |
|
1197 | 0 | if(!ctx) { |
1198 | 0 | LM_ERR("context is undefined\n"); |
1199 | 0 | return -1; |
1200 | 0 | } |
1201 | | |
1202 | 0 | if(!cfg_shmized) |
1203 | 0 | return 0; /* nothing to do */ |
1204 | | |
1205 | 0 | LM_INFO("deleting the config changes [context=%p]\n", ctx); |
1206 | | |
1207 | | /* the ctx must be locked while reading and writing |
1208 | | * the list of changed variables */ |
1209 | 0 | CFG_CTX_LOCK(ctx); |
1210 | |
|
1211 | 0 | for(changed = ctx->changed_first; changed; changed = changed2) { |
1212 | 0 | changed2 = changed->next; |
1213 | |
|
1214 | 0 | if(!changed->del_value |
1215 | 0 | && ((CFG_VAR_TYPE(changed->var) == CFG_VAR_STRING) |
1216 | 0 | || (CFG_VAR_TYPE(changed->var) == CFG_VAR_STR))) { |
1217 | 0 | if(changed->new_val.vp) |
1218 | 0 | shm_free(changed->new_val.vp); |
1219 | 0 | } |
1220 | 0 | shm_free(changed); |
1221 | 0 | } |
1222 | 0 | ctx->changed_first = NULL; |
1223 | |
|
1224 | 0 | CFG_CTX_UNLOCK(ctx); |
1225 | |
|
1226 | 0 | return 0; |
1227 | 0 | } |
1228 | | |
1229 | | /* retrieves the value of a variable |
1230 | | * Return value: |
1231 | | * 0 - success |
1232 | | * -1 - error |
1233 | | * 1 - variable exists, but it is not readable |
1234 | | */ |
1235 | | int cfg_get_by_name(cfg_ctx_t *ctx, str *group_name, unsigned int *group_id, |
1236 | | str *var_name, void **val, unsigned int *val_type) |
1237 | 0 | { |
1238 | 0 | cfg_group_t *group; |
1239 | 0 | cfg_mapping_t *var; |
1240 | 0 | void *p; |
1241 | 0 | static str s; /* we need the value even |
1242 | | * after the function returns */ |
1243 | 0 | cfg_group_inst_t *group_inst; |
1244 | | |
1245 | | /* verify the context even if we do not need it now |
1246 | | * to make sure that a cfg driver has called the function |
1247 | | * (very very weak security) */ |
1248 | 0 | if(!ctx) { |
1249 | 0 | LM_ERR("context is undefined\n"); |
1250 | 0 | return -1; |
1251 | 0 | } |
1252 | | |
1253 | | /* look-up the group and the variable */ |
1254 | 0 | if(cfg_lookup_var(group_name, var_name, &group, &var)) |
1255 | 0 | return -1; |
1256 | | |
1257 | 0 | if(var->def->on_change_cb) { |
1258 | | /* The variable cannot be retrieved, because the fixup |
1259 | | * function may have changed it, and it is better to return |
1260 | | * an error than an incorrect value */ |
1261 | 0 | return 1; |
1262 | 0 | } |
1263 | | |
1264 | 0 | if(group_id) { |
1265 | 0 | if(!cfg_local) { |
1266 | 0 | LM_ERR("Local configuration is missing\n"); |
1267 | 0 | return -1; |
1268 | 0 | } |
1269 | 0 | group_inst = cfg_find_group( |
1270 | 0 | CFG_GROUP_META(cfg_local, group), group->size, *group_id); |
1271 | 0 | if(!group_inst) { |
1272 | 0 | LM_ERR("local group instance %.*s[%u] is not found\n", |
1273 | 0 | group_name->len, group_name->s, *group_id); |
1274 | 0 | return -1; |
1275 | 0 | } |
1276 | 0 | p = group_inst->vars + var->offset; |
1277 | |
|
1278 | 0 | } else { |
1279 | | /* use the module's handle to access the variable |
1280 | | * It means that the variable is read from the local config |
1281 | | * after forking */ |
1282 | 0 | p = *(group->handle) + var->offset; |
1283 | 0 | } |
1284 | | |
1285 | 0 | switch(CFG_VAR_TYPE(var)) { |
1286 | 0 | case CFG_VAR_INT: |
1287 | 0 | *val = (void *)(long)*(int *)p; |
1288 | 0 | break; |
1289 | | |
1290 | 0 | case CFG_VAR_STRING: |
1291 | 0 | *val = (void *)*(char **)p; |
1292 | 0 | break; |
1293 | | |
1294 | 0 | case CFG_VAR_STR: |
1295 | 0 | memcpy(&s, p, sizeof(str)); |
1296 | 0 | *val = (void *)&s; |
1297 | 0 | break; |
1298 | | |
1299 | 0 | case CFG_VAR_POINTER: |
1300 | 0 | *val = *(void **)p; |
1301 | 0 | break; |
1302 | 0 | } |
1303 | 0 | *val_type = CFG_VAR_TYPE(var); |
1304 | |
|
1305 | 0 | return 0; |
1306 | 0 | } |
1307 | | |
1308 | | /* retrieves the default value of a variable |
1309 | | * Return value: |
1310 | | * 0 - success |
1311 | | * -1 - error |
1312 | | * 1 - variable exists, but it is not readable |
1313 | | */ |
1314 | | int cfg_get_default_value_by_name(cfg_ctx_t *ctx, str *group_name, |
1315 | | unsigned int *group_id, str *var_name, void **val, |
1316 | | unsigned int *val_type) |
1317 | 0 | { |
1318 | 0 | cfg_group_t *group; |
1319 | 0 | cfg_mapping_t *var; |
1320 | 0 | void *p; |
1321 | 0 | static str s; /* we need the value even |
1322 | | * after the function returns */ |
1323 | | |
1324 | | /* verify the context even if we do not need it now |
1325 | | * to make sure that a cfg driver has called the function |
1326 | | * (very very weak security) */ |
1327 | 0 | if(!ctx) { |
1328 | 0 | LM_ERR("context is undefined\n"); |
1329 | 0 | return -1; |
1330 | 0 | } |
1331 | | |
1332 | | /* look-up the group and the variable */ |
1333 | 0 | if(cfg_lookup_var(group_name, var_name, &group, &var)) { |
1334 | 0 | return -1; |
1335 | 0 | } else { |
1336 | | /* if variables exist then prevents resetting the read-only ones */ |
1337 | 0 | if(var->def->type & CFG_READONLY) |
1338 | 0 | return -1; |
1339 | 0 | } |
1340 | | |
1341 | 0 | if(var->def->on_change_cb) { |
1342 | | /* The variable cannot be retrieved, because the fixup |
1343 | | * function may have changed it, and it is better to return |
1344 | | * an error than an incorrect value */ |
1345 | 0 | return 1; |
1346 | 0 | } |
1347 | | |
1348 | | /* use the module's orig_handle to access the default registered value of |
1349 | | * the variable for any group*/ |
1350 | 0 | p = (group->orig_handle) + var->offset; |
1351 | |
|
1352 | 0 | switch(CFG_VAR_TYPE(var)) { |
1353 | 0 | case CFG_VAR_INT: |
1354 | 0 | *val = (void *)(long)*(int *)p; |
1355 | 0 | break; |
1356 | | |
1357 | 0 | case CFG_VAR_STRING: |
1358 | 0 | *val = (void *)*(char **)p; |
1359 | 0 | break; |
1360 | | |
1361 | 0 | case CFG_VAR_STR: |
1362 | 0 | memcpy(&s, p, sizeof(str)); |
1363 | 0 | *val = (void *)&s; |
1364 | 0 | break; |
1365 | | |
1366 | 0 | case CFG_VAR_POINTER: |
1367 | 0 | *val = *(void **)p; |
1368 | 0 | break; |
1369 | 0 | } |
1370 | 0 | *val_type = CFG_VAR_TYPE(var); |
1371 | |
|
1372 | 0 | return 0; |
1373 | 0 | } |
1374 | | |
1375 | | |
1376 | | /* returns the description of a variable */ |
1377 | | int cfg_help(cfg_ctx_t *ctx, str *group_name, str *var_name, char **ch, |
1378 | | unsigned int *input_type) |
1379 | 0 | { |
1380 | 0 | cfg_mapping_t *var; |
1381 | | |
1382 | | /* verify the context even if we do not need it now |
1383 | | * to make sure that a cfg driver has called the function |
1384 | | * (very very weak security) */ |
1385 | 0 | if(!ctx) { |
1386 | 0 | LM_ERR("context is undefined\n"); |
1387 | 0 | return -1; |
1388 | 0 | } |
1389 | | |
1390 | | /* look-up the group and the variable */ |
1391 | 0 | if(cfg_lookup_var(group_name, var_name, NULL, &var)) |
1392 | 0 | return -1; |
1393 | | |
1394 | 0 | *ch = var->def->descr; |
1395 | 0 | if(input_type) |
1396 | 0 | *input_type = CFG_INPUT_TYPE(var); |
1397 | 0 | return 0; |
1398 | 0 | } |
1399 | | |
1400 | | /* return the group name and the cfg structure definition, |
1401 | | * and moves the handle to the next group |
1402 | | * Return value: |
1403 | | * 0: no more group |
1404 | | * 1: group exists |
1405 | | */ |
1406 | | int cfg_get_group_next(void **h, str *gname, cfg_def_t **def) |
1407 | 0 | { |
1408 | 0 | cfg_group_t *group; |
1409 | |
|
1410 | 0 | group = (cfg_group_t *)(*h); |
1411 | 0 | if(group == NULL) |
1412 | 0 | return 0; |
1413 | | |
1414 | 0 | gname->s = group->name; |
1415 | 0 | gname->len = group->name_len; |
1416 | 0 | (*def) = group->mapping->def; |
1417 | |
|
1418 | 0 | (*h) = (void *)group->next; |
1419 | 0 | return 1; |
1420 | 0 | } |
1421 | | |
1422 | | /* Initialize the handle for cfg_diff_next() */ |
1423 | | int cfg_diff_init(cfg_ctx_t *ctx, void **h) |
1424 | 0 | { |
1425 | 0 | if(!ctx) { |
1426 | 0 | LM_ERR("context is undefined\n"); |
1427 | 0 | return -1; |
1428 | 0 | } |
1429 | | |
1430 | 0 | CFG_CTX_LOCK(ctx); |
1431 | 0 | (*h) = (void *)ctx->changed_first; |
1432 | |
|
1433 | 0 | return 0; |
1434 | 0 | } |
1435 | | |
1436 | | /* return the pending changes that have not been |
1437 | | * committed yet |
1438 | | * return value: |
1439 | | * 1: valid value is found |
1440 | | * 0: no more changed value found |
1441 | | * -1: error occurred |
1442 | | */ |
1443 | | int cfg_diff_next(void **h, str *gname, unsigned int **gid, str *vname, |
1444 | | void **old_val, void **new_val, unsigned int *val_type) |
1445 | 0 | { |
1446 | 0 | cfg_changed_var_t *changed; |
1447 | 0 | cfg_group_inst_t *group_inst; |
1448 | 0 | union cfg_var_value *pval_old, *pval_new; |
1449 | 0 | static str old_s, new_s; /* we need the value even |
1450 | | * after the function returns */ |
1451 | |
|
1452 | 0 | changed = (cfg_changed_var_t *)(*h); |
1453 | 0 | if(changed == NULL) |
1454 | 0 | return 0; |
1455 | | |
1456 | 0 | gname->s = changed->group->name; |
1457 | 0 | gname->len = changed->group->name_len; |
1458 | 0 | *gid = (changed->group_id_set ? &changed->group_id : NULL); |
1459 | 0 | vname->s = changed->var->def->name; |
1460 | 0 | vname->len = changed->var->name_len; |
1461 | | |
1462 | | /* use the module's handle to access the variable |
1463 | | * It means that the variable is read from the local config |
1464 | | * after forking */ |
1465 | 0 | if(!changed->group_id_set) { |
1466 | 0 | pval_old = (union cfg_var_value *)(*(changed->group->handle) |
1467 | 0 | + changed->var->offset); |
1468 | 0 | } else { |
1469 | 0 | if(!cfg_local) { |
1470 | 0 | LM_ERR("Local configuration is missing\n"); |
1471 | 0 | return -1; |
1472 | 0 | } |
1473 | 0 | group_inst = cfg_find_group(CFG_GROUP_META(cfg_local, changed->group), |
1474 | 0 | changed->group->size, changed->group_id); |
1475 | 0 | if(!group_inst) { |
1476 | 0 | LM_ERR("local group instance %.*s[%u] is not found\n", |
1477 | 0 | changed->group->name_len, changed->group->name, |
1478 | 0 | changed->group_id); |
1479 | 0 | return -1; |
1480 | 0 | } |
1481 | 0 | pval_old = (union cfg_var_value *)(group_inst->vars |
1482 | 0 | + changed->var->offset); |
1483 | 0 | } |
1484 | 0 | if(!changed->del_value) |
1485 | 0 | pval_new = &changed->new_val; |
1486 | 0 | else |
1487 | 0 | pval_new = (union cfg_var_value *)(*(changed->group->handle) |
1488 | 0 | + changed->var->offset); |
1489 | |
|
1490 | 0 | switch(CFG_VAR_TYPE(changed->var)) { |
1491 | 0 | case CFG_VAR_INT: |
1492 | 0 | *old_val = (void *)(long)pval_old->vint; |
1493 | 0 | *new_val = (void *)(long)pval_new->vint; |
1494 | 0 | break; |
1495 | | |
1496 | 0 | case CFG_VAR_STRING: |
1497 | 0 | *old_val = pval_old->vp; |
1498 | 0 | *new_val = pval_new->vp; |
1499 | 0 | break; |
1500 | | |
1501 | 0 | case CFG_VAR_STR: |
1502 | 0 | old_s = pval_old->vstr; |
1503 | 0 | *old_val = (void *)&old_s; |
1504 | 0 | new_s = pval_new->vstr; |
1505 | 0 | *new_val = (void *)&new_s; |
1506 | 0 | break; |
1507 | | |
1508 | 0 | case CFG_VAR_POINTER: |
1509 | 0 | *old_val = pval_old->vp; |
1510 | 0 | *new_val = pval_new->vp; |
1511 | 0 | break; |
1512 | 0 | } |
1513 | 0 | *val_type = CFG_VAR_TYPE(changed->var); |
1514 | |
|
1515 | 0 | (*h) = (void *)changed->next; |
1516 | 0 | return 1; |
1517 | 0 | } |
1518 | | |
1519 | | /* release the handle of cfg_diff_next() */ |
1520 | | void cfg_diff_release(cfg_ctx_t *ctx) |
1521 | 0 | { |
1522 | 0 | if(!ctx) { |
1523 | 0 | LM_ERR("context is undefined\n"); |
1524 | 0 | return; |
1525 | 0 | } |
1526 | | |
1527 | 0 | CFG_CTX_UNLOCK(ctx); |
1528 | 0 | } |
1529 | | |
1530 | | /* Add a new instance to an existing group */ |
1531 | | int cfg_add_group_inst(cfg_ctx_t *ctx, str *group_name, unsigned int group_id) |
1532 | 0 | { |
1533 | 0 | cfg_group_t *group; |
1534 | 0 | cfg_block_t *block = NULL; |
1535 | 0 | void **replaced = NULL; |
1536 | 0 | cfg_group_inst_t *new_array = NULL, *new_inst; |
1537 | | |
1538 | | /* verify the context even if we do not need it now |
1539 | | * to make sure that a cfg driver has called the function |
1540 | | * (very very weak security) */ |
1541 | 0 | if(!ctx) { |
1542 | 0 | LM_ERR("context is undefined\n"); |
1543 | 0 | return -1; |
1544 | 0 | } |
1545 | | |
1546 | 0 | if(!cfg_shmized) { |
1547 | | /* Add a new variable without any value to |
1548 | | * the linked list of additional values. This variable |
1549 | | * will force a new group instance to be created. */ |
1550 | 0 | return new_add_var(group_name, group_id, NULL /* var_name */, |
1551 | 0 | NULL /* val */, 0 /* type */); |
1552 | 0 | } |
1553 | | |
1554 | 0 | if(!(group = cfg_lookup_group(group_name->s, group_name->len))) { |
1555 | 0 | LM_ERR("group not found\n"); |
1556 | 0 | return -1; |
1557 | 0 | } |
1558 | | |
1559 | | /* make sure that nobody else replaces the global config |
1560 | | * while the new one is prepared */ |
1561 | 0 | CFG_WRITER_LOCK(); |
1562 | 0 | if(cfg_find_group( |
1563 | 0 | CFG_GROUP_META(*cfg_global, group), group->size, group_id)) { |
1564 | 0 | LM_DBG("the group instance already exists\n"); |
1565 | 0 | CFG_WRITER_UNLOCK(); |
1566 | 0 | return 0; /* not an error */ |
1567 | 0 | } |
1568 | | |
1569 | | /* clone the global memory block because the additional array can be |
1570 | | * replaced only together with the block. */ |
1571 | 0 | if(!(block = cfg_clone_global())) |
1572 | 0 | goto error; |
1573 | | |
1574 | | /* Extend the array with a new group instance */ |
1575 | 0 | if(!(new_array = cfg_extend_array(CFG_GROUP_META(*cfg_global, group), group, |
1576 | 0 | group_id, &new_inst))) |
1577 | 0 | goto error; |
1578 | | |
1579 | | /* fill in the new group instance with the default data */ |
1580 | 0 | memcpy(new_inst->vars, CFG_GROUP_DATA(*cfg_global, group), group->size); |
1581 | |
|
1582 | 0 | CFG_GROUP_META(block, group)->array = new_array; |
1583 | 0 | CFG_GROUP_META(block, group)->num++; |
1584 | |
|
1585 | 0 | if(CFG_GROUP_META(*cfg_global, group)->array) { |
1586 | | /* prepare the array of the replaced strings, |
1587 | | * and replaced group instances, |
1588 | | * they will be freed when the old block is freed */ |
1589 | 0 | replaced = (void **)shm_malloc(sizeof(void *) * 2); |
1590 | 0 | if(!replaced) { |
1591 | 0 | SHM_MEM_ERROR; |
1592 | 0 | goto error; |
1593 | 0 | } |
1594 | 0 | replaced[0] = CFG_GROUP_META(*cfg_global, group)->array; |
1595 | 0 | replaced[1] = NULL; |
1596 | 0 | } |
1597 | | /* replace the global config with the new one */ |
1598 | 0 | cfg_install_global(block, replaced, NULL, NULL); |
1599 | 0 | CFG_WRITER_UNLOCK(); |
1600 | |
|
1601 | 0 | LM_INFO("group instance is added: %.*s[%u]\n", group_name->len, |
1602 | 0 | group_name->s, group_id); |
1603 | | |
1604 | | /* Make sure that cfg_set_*() sees the change when |
1605 | | * the function is immediately called after the group |
1606 | | * instance has been added. */ |
1607 | 0 | cfg_update(); |
1608 | |
|
1609 | 0 | return 0; |
1610 | 0 | error: |
1611 | 0 | CFG_WRITER_UNLOCK(); |
1612 | 0 | if(block) |
1613 | 0 | cfg_block_free(block); |
1614 | 0 | if(new_array) |
1615 | 0 | shm_free(new_array); |
1616 | 0 | if(replaced) |
1617 | 0 | shm_free(replaced); |
1618 | |
|
1619 | 0 | LM_ERR("Failed to add the group instance: %.*s[%u]\n", group_name->len, |
1620 | 0 | group_name->s, group_id); |
1621 | |
|
1622 | 0 | return -1; |
1623 | 0 | } |
1624 | | |
1625 | | /* Delete an instance of a group */ |
1626 | | int cfg_del_group_inst(cfg_ctx_t *ctx, str *group_name, unsigned int group_id) |
1627 | 0 | { |
1628 | 0 | cfg_group_t *group; |
1629 | 0 | cfg_block_t *block = NULL; |
1630 | 0 | void **replaced = NULL; |
1631 | 0 | cfg_group_inst_t *new_array = NULL, *group_inst; |
1632 | 0 | cfg_mapping_t *var; |
1633 | 0 | int i, num; |
1634 | | |
1635 | | /* verify the context even if we do not need it now |
1636 | | * to make sure that a cfg driver has called the function |
1637 | | * (very very weak security) */ |
1638 | 0 | if(!ctx) { |
1639 | 0 | LM_ERR("context is undefined\n"); |
1640 | 0 | return -1; |
1641 | 0 | } |
1642 | | |
1643 | 0 | if(!cfg_shmized) { |
1644 | | /* It makes no sense to delete a group instance that has not |
1645 | | * been created yet */ |
1646 | 0 | return -1; |
1647 | 0 | } |
1648 | | |
1649 | 0 | if(!(group = cfg_lookup_group(group_name->s, group_name->len))) { |
1650 | 0 | LM_ERR("group not found\n"); |
1651 | 0 | return -1; |
1652 | 0 | } |
1653 | | |
1654 | | /* make sure that nobody else replaces the global config |
1655 | | * while the new one is prepared */ |
1656 | 0 | CFG_WRITER_LOCK(); |
1657 | 0 | if(!(group_inst = cfg_find_group( |
1658 | 0 | CFG_GROUP_META(*cfg_global, group), group->size, group_id))) { |
1659 | 0 | LM_DBG("the group instance does not exist\n"); |
1660 | 0 | goto error; |
1661 | 0 | } |
1662 | | |
1663 | | /* clone the global memory block because the additional array can be |
1664 | | * replaced only together with the block. */ |
1665 | 0 | if(!(block = cfg_clone_global())) |
1666 | 0 | goto error; |
1667 | | |
1668 | | /* Remove the group instance from the array. */ |
1669 | 0 | if(cfg_collapse_array(CFG_GROUP_META(*cfg_global, group), group, group_inst, |
1670 | 0 | &new_array)) |
1671 | 0 | goto error; |
1672 | | |
1673 | 0 | CFG_GROUP_META(block, group)->array = new_array; |
1674 | 0 | CFG_GROUP_META(block, group)->num--; |
1675 | |
|
1676 | 0 | if(CFG_GROUP_META(*cfg_global, group)->array) { |
1677 | | /* prepare the array of the replaced strings, |
1678 | | * and replaced group instances, |
1679 | | * they will be freed when the old block is freed */ |
1680 | | |
1681 | | /* count the number of strings that has to be freed */ |
1682 | 0 | num = 0; |
1683 | 0 | for(i = 0; i < group->num; i++) { |
1684 | 0 | var = &group->mapping[i]; |
1685 | 0 | if(CFG_VAR_TEST(group_inst, var) |
1686 | 0 | && ((CFG_VAR_TYPE(var) == CFG_VAR_STRING) |
1687 | 0 | || (CFG_VAR_TYPE(var) == CFG_VAR_STR)) |
1688 | 0 | && (*(char **)(group_inst->vars + var->offset) != NULL)) |
1689 | 0 | num++; |
1690 | 0 | } |
1691 | |
|
1692 | 0 | replaced = (void **)shm_malloc(sizeof(void *) * (num + 2)); |
1693 | 0 | if(!replaced) { |
1694 | 0 | SHM_MEM_ERROR; |
1695 | 0 | goto error; |
1696 | 0 | } |
1697 | | |
1698 | 0 | if(num) { |
1699 | | /* There was at least one string to free, go though the list again */ |
1700 | 0 | num = 0; |
1701 | 0 | for(i = 0; i < group->num; i++) { |
1702 | 0 | var = &group->mapping[i]; |
1703 | 0 | if(CFG_VAR_TEST(group_inst, var) |
1704 | 0 | && ((CFG_VAR_TYPE(var) == CFG_VAR_STRING) |
1705 | 0 | || (CFG_VAR_TYPE(var) == CFG_VAR_STR)) |
1706 | 0 | && (*(char **)(group_inst->vars + var->offset) |
1707 | 0 | != NULL)) { |
1708 | 0 | replaced[num] = *(char **)(group_inst->vars + var->offset); |
1709 | 0 | num++; |
1710 | 0 | } |
1711 | 0 | } |
1712 | 0 | } |
1713 | |
|
1714 | 0 | replaced[num] = CFG_GROUP_META(*cfg_global, group)->array; |
1715 | 0 | replaced[num + 1] = NULL; |
1716 | 0 | } |
1717 | | /* replace the global config with the new one */ |
1718 | 0 | cfg_install_global(block, replaced, NULL, NULL); |
1719 | 0 | CFG_WRITER_UNLOCK(); |
1720 | |
|
1721 | 0 | LM_INFO("group instance is deleted: %.*s[%u]\n", group_name->len, |
1722 | 0 | group_name->s, group_id); |
1723 | | |
1724 | | /* Make sure that cfg_set_*() sees the change when |
1725 | | * the function is immediately called after the group |
1726 | | * instance has been deleted. */ |
1727 | 0 | cfg_update(); |
1728 | |
|
1729 | 0 | return 0; |
1730 | 0 | error: |
1731 | 0 | CFG_WRITER_UNLOCK(); |
1732 | 0 | if(block) |
1733 | 0 | cfg_block_free(block); |
1734 | 0 | if(new_array) |
1735 | 0 | shm_free(new_array); |
1736 | 0 | if(replaced) |
1737 | 0 | shm_free(replaced); |
1738 | |
|
1739 | 0 | LM_ERR("Failed to delete the group instance: %.*s[%u]\n", group_name->len, |
1740 | 0 | group_name->s, group_id); |
1741 | |
|
1742 | 0 | return -1; |
1743 | 0 | } |
1744 | | |
1745 | | /* Check the existence of a group instance. |
1746 | | * return value: |
1747 | | * 1: exists |
1748 | | * 0: does not exist |
1749 | | */ |
1750 | | int cfg_group_inst_exists( |
1751 | | cfg_ctx_t *ctx, str *group_name, unsigned int group_id) |
1752 | 0 | { |
1753 | 0 | cfg_group_t *group; |
1754 | 0 | cfg_add_var_t *add_var; |
1755 | 0 | int found; |
1756 | | |
1757 | | /* verify the context even if we do not need it now |
1758 | | * to make sure that a cfg driver has called the function |
1759 | | * (very very weak security) */ |
1760 | 0 | if(!ctx) { |
1761 | 0 | LM_ERR("context is undefined\n"); |
1762 | 0 | return 0; |
1763 | 0 | } |
1764 | | |
1765 | 0 | if(!(group = cfg_lookup_group(group_name->s, group_name->len))) { |
1766 | 0 | LM_ERR("group not found\n"); |
1767 | 0 | return 0; |
1768 | 0 | } |
1769 | | |
1770 | 0 | if(!cfg_shmized) { |
1771 | | /* group instances are stored in the additional variable list |
1772 | | * before forking */ |
1773 | 0 | found = 0; |
1774 | 0 | for(add_var = group->add_var; add_var; add_var = add_var->next) |
1775 | 0 | if(add_var->group_id == group_id) { |
1776 | 0 | found = 1; |
1777 | 0 | break; |
1778 | 0 | } |
1779 | |
|
1780 | 0 | } else { |
1781 | | /* make sure that nobody else replaces the global config meantime */ |
1782 | 0 | CFG_WRITER_LOCK(); |
1783 | 0 | found = (cfg_find_group(CFG_GROUP_META(*cfg_global, group), group->size, |
1784 | 0 | group_id) |
1785 | 0 | != NULL); |
1786 | 0 | CFG_WRITER_UNLOCK(); |
1787 | 0 | } |
1788 | |
|
1789 | 0 | return found; |
1790 | 0 | } |
1791 | | |
1792 | | /* Apply the changes to a group instance as long as the additional variable |
1793 | | * belongs to the specified group_id. *add_var_p is moved to the next additional |
1794 | | * variable, and all the consumed variables are freed. |
1795 | | * This function can be used only during the cfg shmize process. |
1796 | | * For internal use only! |
1797 | | */ |
1798 | | int cfg_apply_list(cfg_group_inst_t *ginst, cfg_group_t *group, |
1799 | | unsigned int group_id, cfg_add_var_t **add_var_p) |
1800 | 0 | { |
1801 | 0 | cfg_add_var_t *add_var; |
1802 | 0 | cfg_mapping_t *var; |
1803 | 0 | void *val, *v, *p; |
1804 | 0 | str group_name, var_name, s; |
1805 | 0 | char *old_string; |
1806 | |
|
1807 | 0 | group_name.s = group->name; |
1808 | 0 | group_name.len = group->name_len; |
1809 | 0 | while(*add_var_p && ((*add_var_p)->group_id == group_id)) { |
1810 | 0 | add_var = *add_var_p; |
1811 | |
|
1812 | 0 | if(add_var->type == 0) |
1813 | 0 | goto done; /* Nothing needs to be changed, |
1814 | | * this additional variable only forces a new |
1815 | | * group instance to be created. */ |
1816 | 0 | var_name.s = add_var->name; |
1817 | 0 | var_name.len = add_var->name_len; |
1818 | |
|
1819 | 0 | if(!(var = cfg_lookup_var2(group, add_var->name, add_var->name_len))) { |
1820 | 0 | LM_ERR("Variable is not found: %.*s.%.*s\n", group->name_len, |
1821 | 0 | group->name, add_var->name_len, add_var->name); |
1822 | 0 | goto error; |
1823 | 0 | } |
1824 | | |
1825 | | /* check whether the variable is read-only */ |
1826 | 0 | if(var->def->type & CFG_READONLY) { |
1827 | 0 | LM_ERR("variable is read-only\n"); |
1828 | 0 | goto error; |
1829 | 0 | } |
1830 | | |
1831 | | /* The additional variable instances having per-child process callback |
1832 | | * with CFG_CB_ONLY_ONCE flag cannot be rewritten. |
1833 | | * The reason is that such variables typically set global parameters |
1834 | | * as opposed to per-process variables. Hence, it is not possible to set |
1835 | | * the group handle temporary to another block, and then reset it back later. */ |
1836 | 0 | if(var->def->on_set_child_cb && var->def->type & CFG_CB_ONLY_ONCE) { |
1837 | 0 | LM_ERR("This variable does not support multiple values.\n"); |
1838 | 0 | goto error; |
1839 | 0 | } |
1840 | | |
1841 | 0 | switch(add_var->type) { |
1842 | 0 | case CFG_VAR_INT: |
1843 | 0 | val = (void *)(long)add_var->val.i; |
1844 | 0 | break; |
1845 | 0 | case CFG_VAR_STR: |
1846 | 0 | val = (str *)&(add_var->val.s); |
1847 | 0 | break; |
1848 | 0 | case CFG_VAR_STRING: |
1849 | 0 | val = (char *)add_var->val.ch; |
1850 | 0 | break; |
1851 | 0 | default: |
1852 | 0 | LM_ERR("unsupported variable type: %d\n", add_var->type); |
1853 | 0 | goto error; |
1854 | 0 | } |
1855 | | /* check whether we have to convert the type */ |
1856 | 0 | if(convert_val(add_var->type, val, CFG_INPUT_TYPE(var), &v)) |
1857 | 0 | goto error; |
1858 | | |
1859 | 0 | if((CFG_INPUT_TYPE(var) == CFG_INPUT_INT) |
1860 | 0 | && (var->def->min || var->def->max)) { |
1861 | | /* perform a simple min-max check for integers */ |
1862 | 0 | if(((int)(long)v < var->def->min) |
1863 | 0 | || ((int)(long)v > var->def->max)) { |
1864 | 0 | LM_ERR("integer value is out of range\n"); |
1865 | 0 | goto error; |
1866 | 0 | } |
1867 | 0 | } |
1868 | | |
1869 | 0 | if(var->def->on_change_cb) { |
1870 | | /* Call the fixup function. |
1871 | | * The handle can point to the variables of the group instance. */ |
1872 | 0 | if(var->def->on_change_cb(ginst->vars, &group_name, &var_name, &v) |
1873 | 0 | < 0) { |
1874 | 0 | LM_ERR("fixup failed\n"); |
1875 | 0 | goto error; |
1876 | 0 | } |
1877 | 0 | } |
1878 | | |
1879 | 0 | p = ginst->vars + var->offset; |
1880 | 0 | old_string = NULL; |
1881 | | /* set the new value */ |
1882 | 0 | switch(CFG_VAR_TYPE(var)) { |
1883 | 0 | case CFG_VAR_INT: |
1884 | 0 | *(int *)p = (int)(long)v; |
1885 | 0 | break; |
1886 | | |
1887 | 0 | case CFG_VAR_STRING: |
1888 | | /* clone the string to shm mem */ |
1889 | 0 | s.s = v; |
1890 | 0 | s.len = (s.s) ? strlen(s.s) : 0; |
1891 | 0 | if(cfg_clone_str(&s, &s)) |
1892 | 0 | goto error; |
1893 | 0 | old_string = *(char **)p; |
1894 | 0 | *(char **)p = s.s; |
1895 | 0 | break; |
1896 | | |
1897 | 0 | case CFG_VAR_STR: |
1898 | | /* clone the string to shm mem */ |
1899 | 0 | s = *(str *)v; |
1900 | 0 | if(cfg_clone_str(&s, &s)) |
1901 | 0 | goto error; |
1902 | 0 | old_string = *(char **)p; |
1903 | 0 | memcpy(p, &s, sizeof(str)); |
1904 | 0 | break; |
1905 | | |
1906 | 0 | case CFG_VAR_POINTER: |
1907 | 0 | *(void **)p = v; |
1908 | 0 | break; |
1909 | 0 | } |
1910 | 0 | if(CFG_VAR_TEST_AND_SET(ginst, var) && old_string) |
1911 | 0 | shm_free(old_string); /* the string was already in shm memory, |
1912 | | * it needs to be freed. |
1913 | | * This can happen when the same variable is set |
1914 | | * multiple times before forking. */ |
1915 | |
|
1916 | 0 | if(add_var->type == CFG_VAR_INT) |
1917 | 0 | LM_INFO("%.*s[%u].%.*s has been set to %d\n", group_name.len, |
1918 | 0 | group_name.s, group_id, var_name.len, var_name.s, |
1919 | 0 | (int)(long)val); |
1920 | | |
1921 | 0 | else if(add_var->type == CFG_VAR_STRING) |
1922 | 0 | LM_INFO("%.*s[%u].%.*s has been set to \"%s\"\n", group_name.len, |
1923 | 0 | group_name.s, group_id, var_name.len, var_name.s, |
1924 | 0 | (char *)val); |
1925 | | |
1926 | 0 | else /* str type */ |
1927 | 0 | LM_INFO("%.*s[%u].%.*s has been set to \"%.*s\"\n", group_name.len, |
1928 | 0 | group_name.s, group_id, var_name.len, var_name.s, |
1929 | 0 | ((str *)val)->len, ((str *)val)->s); |
1930 | |
|
1931 | 0 | convert_val_cleanup(); |
1932 | |
|
1933 | 0 | done: |
1934 | 0 | *add_var_p = add_var->next; |
1935 | |
|
1936 | 0 | if((add_var->type == CFG_VAR_STR) && add_var->val.s.s) |
1937 | 0 | pkg_free(add_var->val.s.s); |
1938 | 0 | else if((add_var->type == CFG_VAR_STRING) && add_var->val.ch) |
1939 | 0 | pkg_free(add_var->val.ch); |
1940 | 0 | pkg_free(add_var); |
1941 | 0 | } |
1942 | 0 | return 0; |
1943 | | |
1944 | 0 | error: |
1945 | 0 | LM_ERR("Failed to set the value for: %.*s[%u].%.*s\n", group->name_len, |
1946 | 0 | group->name, group_id, add_var->name_len, add_var->name); |
1947 | 0 | convert_val_cleanup(); |
1948 | 0 | return -1; |
1949 | 0 | } |