4 * Copyright (C) 2007 iptelorg GmbH
6 * This file is part of ser, a free SIP server.
8 * ser is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version
13 * For a license to use the ser software under conditions
14 * other than those described here, or to purchase support for this
15 * software, please contact iptel.org by e-mail at the following addresses:
18 * ser is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29 * 2007-12-03 Initial version (Miklos)
30 * 2008-01-24 dynamic groups are introduced in order to make
31 * variable declaration possible in the script (Miklos)
36 #include "../mem/mem.h"
37 #include "../mem/shm_mem.h"
39 #include "../locking.h"
41 #include "cfg_script.h"
42 #include "cfg_struct.h"
44 cfg_group_t *cfg_group = NULL; /* linked list of registered cfg groups */
45 cfg_block_t **cfg_global = NULL; /* pointer to the active cfg block */
46 cfg_block_t *cfg_local = NULL; /* per-process pointer to the active cfg block.
47 Updated only when the child process
48 finishes working on the SIP message */
49 static int cfg_block_size = 0; /* size of the cfg block (constant) */
50 gen_lock_t *cfg_global_lock = 0; /* protects *cfg_global */
51 gen_lock_t *cfg_writer_lock = 0; /* This lock makes sure that two processes do not
52 try to clone *cfg_global at the same time.
53 Never try to get cfg_writer_lock when
54 cfg_global_lock is held */
55 int cfg_shmized = 0; /* indicates whether the cfg block has been
58 cfg_child_cb_t **cfg_child_cb_first = NULL; /* first item of the per-child process
60 cfg_child_cb_t **cfg_child_cb_last = NULL; /* last item of the above list */
61 cfg_child_cb_t *cfg_child_cb = NULL; /* pointer to the previously executed cb */
63 /* creates a new cfg group, and adds it to the linked list */
64 cfg_group_t *cfg_new_group(char *name, int name_len,
65 int num, cfg_mapping_t *mapping,
66 char *vars, int size, void **handle)
71 LOG(L_ERR, "ERROR: cfg_new_group(): too late config declaration\n");
75 group = (cfg_group_t *)pkg_malloc(sizeof(cfg_group_t)+name_len-1);
77 LOG(L_ERR, "ERROR: cfg_new_group(): not enough memory\n");
80 memset(group, 0, sizeof(cfg_group_t)+name_len-1);
83 group->mapping = mapping;
86 group->handle = handle;
87 group->name_len = name_len;
88 memcpy(&group->name, name, name_len);
90 /* add the new group to the beginning of the list */
91 group->next = cfg_group;
97 /* clones a string to shared memory
98 * (src and dst can be the same)
100 int cfg_clone_str(str *src, str *dst)
110 c = (char *)shm_malloc(sizeof(char)*(src->len+1));
112 LOG(L_ERR, "ERROR: cfg_clone_str(): not enough shm memory\n");
115 memcpy(c, src->s, src->len);
124 /* copies the strings to shared memory */
125 static int cfg_shmize_strings(cfg_group_t *group)
127 cfg_mapping_t *mapping;
131 /* We do not know in advance whether the variable will be changed or not,
132 and it can happen that we try to free the shm memory area when the variable
133 is changed, hence, it must be already in shm mem */
134 mapping = group->mapping;
135 for (i=0; i<group->num; i++) {
136 /* the cfg driver module may have already shmized the variable */
137 if (mapping[i].flag & cfg_var_shmized) continue;
139 if (CFG_VAR_TYPE(&mapping[i]) == CFG_VAR_STRING) {
140 memcpy(&s.s, group->vars + mapping[i].offset, sizeof(char *));
144 } else if (CFG_VAR_TYPE(&mapping[i]) == CFG_VAR_STR) {
145 memcpy(&s, group->vars + mapping[i].offset, sizeof(str));
151 if (cfg_clone_str(&s, &s)) return -1;
152 memcpy(group->vars + mapping[i].offset, &s.s, sizeof(char *));
153 mapping[i].flag |= cfg_var_shmized;
159 /* copy the variables to shm mem */
163 cfg_block_t *block = NULL;
166 if (!cfg_group) return 0;
168 /* Let us allocate one memory block that
169 will contain all the variables */
170 for ( size=0, group = cfg_group;
174 size = ROUND_POINTER(size);
175 group->offset = size;
179 block = (cfg_block_t*)shm_malloc(sizeof(cfg_block_t)+size-1);
181 LOG(L_ERR, "ERROR: cfg_clone_str(): not enough shm memory\n");
184 memset(block, 0, sizeof(cfg_block_t)+size-1);
185 cfg_block_size = size;
187 /* copy the memory fragments to the single block */
188 for ( group = cfg_group;
192 if (group->dynamic == 0) {
193 /* clone the strings to shm mem */
194 if (cfg_shmize_strings(group)) goto error;
196 /* copy the values to the new block,
197 and update the module's handle */
198 memcpy(block->vars+group->offset, group->vars, group->size);
199 *(group->handle) = block->vars+group->offset;
201 /* The group was declared with NULL values,
202 * we have to fix it up.
203 * The fixup function takes care about the values,
204 * it fills up the block */
205 if (cfg_script_fixup(group, block->vars+group->offset)) goto error;
206 *(group->handle) = block->vars+group->offset;
208 /* notify the drivers about the new config definition */
209 cfg_notify_drivers(group->name, group->name_len,
210 group->mapping->def);
214 /* install the new config */
215 cfg_install_global(block, NULL, NULL, NULL);
221 if (block) shm_free(block);
225 /* deallocate the list of groups, and the shmized strings */
226 static void cfg_destory_groups(unsigned char *block)
228 cfg_group_t *group, *group2;
229 cfg_mapping_t *mapping;
236 mapping = group->mapping;
237 def = mapping ? mapping->def : NULL;
239 /* destory the shmized strings in the block */
241 for (i=0; i<group->num; i++)
242 if (((CFG_VAR_TYPE(&mapping[i]) == CFG_VAR_STRING) ||
243 (CFG_VAR_TYPE(&mapping[i]) == CFG_VAR_STR)) &&
244 mapping[i].flag & cfg_var_shmized) {
247 block + group->offset + mapping[i].offset,
249 if (old_string) shm_free(old_string);
252 if (group->dynamic) {
253 /* the group was dynamically allocated */
254 cfg_script_destroy(group);
256 /* only the mapping was allocated, all the other
257 pointers are just set to static variables */
258 if (mapping) pkg_free(mapping);
261 group2 = group->next;
267 /* initiate the cfg framework */
270 cfg_global_lock = lock_alloc();
271 if (!cfg_global_lock) {
272 LOG(L_ERR, "ERROR: cfg_init(): not enough shm memory\n");
275 if (lock_init(cfg_global_lock) == 0) {
276 LOG(L_ERR, "ERROR: cfg_init(): failed to init lock\n");
277 lock_dealloc(cfg_global_lock);
282 cfg_writer_lock = lock_alloc();
283 if (!cfg_writer_lock) {
284 LOG(L_ERR, "ERROR: cfg_init(): not enough shm memory\n");
287 if (lock_init(cfg_writer_lock) == 0) {
288 LOG(L_ERR, "ERROR: cfg_init(): failed to init lock\n");
289 lock_dealloc(cfg_writer_lock);
294 cfg_global = (cfg_block_t **)shm_malloc(sizeof(cfg_block_t *));
296 LOG(L_ERR, "ERROR: cfg_init(): not enough shm memory\n");
301 cfg_child_cb_first = (cfg_child_cb_t **)shm_malloc(sizeof(cfg_child_cb_t *));
302 if (!cfg_child_cb_first) {
303 LOG(L_ERR, "ERROR: cfg_init(): not enough shm memory\n");
306 *cfg_child_cb_first = NULL;
308 cfg_child_cb_last = (cfg_child_cb_t **)shm_malloc(sizeof(cfg_child_cb_t *));
309 if (!cfg_child_cb_last) {
310 LOG(L_ERR, "ERROR: cfg_init(): not enough shm memory\n");
313 *cfg_child_cb_last = NULL;
315 /* A new cfg_child_cb struct must be created with a NULL callback function.
316 This stucture will be the entry point for the child processes, and
317 will be freed later, when none of the processes refers to it */
318 *cfg_child_cb_first = *cfg_child_cb_last =
319 cfg_child_cb_new(NULL, NULL);
321 if (!*cfg_child_cb_first) goto error;
331 /* destroy the memory allocated for the cfg framework */
332 void cfg_destroy(void)
334 /* free the contexts */
337 /* free the list of groups */
338 cfg_destory_groups(cfg_global ? (*cfg_global)->vars : NULL);
340 if (cfg_child_cb_first) {
341 if (*cfg_child_cb_first) cfg_child_cb_free(*cfg_child_cb_first);
342 shm_free(cfg_child_cb_first);
343 cfg_child_cb_first = NULL;
346 if (cfg_child_cb_last) {
347 shm_free(cfg_child_cb_last);
348 cfg_child_cb_last = NULL;
352 if (*cfg_global) cfg_block_free(*cfg_global);
353 shm_free(cfg_global);
356 if (cfg_global_lock) {
357 lock_destroy(cfg_global_lock);
358 lock_dealloc(cfg_global_lock);
361 if (cfg_writer_lock) {
362 lock_destroy(cfg_writer_lock);
363 lock_dealloc(cfg_writer_lock);
368 /* per-child process init function */
369 int cfg_child_init(void)
371 /* set the callback list pointer to the beginning of the list */
372 cfg_child_cb = *cfg_child_cb_first;
373 atomic_inc(&cfg_child_cb->refcnt);
378 /* per-child process destroy function
379 * Should be called only when the child process exits,
380 * but SER continues running
382 * WARNING: this function call must be the very last action
383 * before the child process exits, because the local config
384 * is not available afterwards.
386 void cfg_child_destroy(void)
388 /* unref the local config */
390 CFG_UNREF(cfg_local);
394 /* unref the per-process callback list */
395 if (atomic_dec_and_test(&cfg_child_cb->refcnt)) {
396 /* No more pocess refers to this callback.
397 Did this process block the deletion,
398 or is there any other process that has not
399 reached prev_cb yet? */
401 if (*cfg_child_cb_first == cfg_child_cb) {
402 /* yes, this process was blocking the deletion */
403 *cfg_child_cb_first = cfg_child_cb->next;
405 shm_free(cfg_child_cb);
413 /* searches a group by name */
414 cfg_group_t *cfg_lookup_group(char *name, int len)
422 if ((g->name_len == len)
423 && (memcmp(g->name, name, len)==0))
429 /* searches a variable definition by group and variable name */
430 int cfg_lookup_var(str *gname, str *vname,
431 cfg_group_t **group, cfg_mapping_t **var)
440 if ((g->name_len == gname->len)
441 && (memcmp(g->name, gname->s, gname->len)==0)) {
447 if ((g->mapping[i].name_len == vname->len)
448 && (memcmp(g->mapping[i].def->name, vname->s, vname->len)==0)) {
449 if (group) *group = g;
450 if (var) *var = &(g->mapping[i]);
457 LOG(L_ERR, "ERROR: cfg_lookup_var(): variable not found: %.*s.%.*s\n",
458 gname->len, gname->s,
459 vname->len, vname->s);
463 /* clones the global config block
464 * WARNING: unsafe, cfg_writer_lock or cfg_global_lock must be held!
466 cfg_block_t *cfg_clone_global(void)
470 block = (cfg_block_t*)shm_malloc(sizeof(cfg_block_t)+cfg_block_size-1);
472 LOG(L_ERR, "ERROR: cfg_clone_global(): not enough shm memory\n");
475 memcpy(block, *cfg_global, sizeof(cfg_block_t)+cfg_block_size-1);
477 /* reset the reference counter */
478 atomic_set(&block->refcnt, 0);
483 /* installs a new global config
485 * replaced is an array of strings that must be freed together
486 * with the previous global config.
487 * cb_first and cb_last define a linked list of per-child process
488 * callbacks. This list is added to the global linked list.
490 void cfg_install_global(cfg_block_t *block, char **replaced,
491 cfg_child_cb_t *cb_first, cfg_child_cb_t *cb_last)
496 if (replaced) (*cfg_global)->replaced = replaced;
497 CFG_UNREF(*cfg_global);
503 /* add the new callbacks to the end of the linked-list */
504 (*cfg_child_cb_last)->next = cb_first;
505 *cfg_child_cb_last = cb_last;
512 /* creates a structure for a per-child process callback */
513 cfg_child_cb_t *cfg_child_cb_new(str *name, cfg_on_set_child cb)
515 cfg_child_cb_t *cb_struct;
517 cb_struct = (cfg_child_cb_t *)shm_malloc(sizeof(cfg_child_cb_t));
519 LOG(L_ERR, "ERROR: cfg_child_cb_new(): not enough shm memory\n");
522 memset(cb_struct, 0, sizeof(cfg_child_cb_t));
524 cb_struct->name.s = name->s;
525 cb_struct->name.len = name->len;
528 atomic_set(&cb_struct->refcnt, 0);
533 /* free the memory allocated for a child cb list */
534 void cfg_child_cb_free(cfg_child_cb_t *child_cb_first)
536 cfg_child_cb_t *cb, *cb_next;
538 for( cb = child_cb_first;