- #define CFG_INPUT_SHIFT is used instead of a hardwired value
[sip-router] / cfg / cfg_struct.c
1 /*
2  * $Id$
3  *
4  * Copyright (C) 2007 iptelorg GmbH
5  *
6  * This file is part of ser, a free SIP server.
7  *
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
12  *
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:
16  *    info@iptel.org
17  *
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.
22  *
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
26  *
27  * History
28  * -------
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)
32  */
33
34 #include <string.h>
35
36 #include "../mem/mem.h"
37 #include "../mem/shm_mem.h"
38 #include "../ut.h"
39 #include "../locking.h"
40 #include "cfg_ctx.h"
41 #include "cfg_script.h"
42 #include "cfg_struct.h"
43
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
56                                         already shmized */
57
58 cfg_child_cb_t  **cfg_child_cb_first = NULL;    /* first item of the per-child process
59                                                 callback list */
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 */     
62
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)
67 {
68         cfg_group_t     *group;
69
70         if (cfg_shmized) {
71                 LOG(L_ERR, "ERROR: cfg_new_group(): too late config declaration\n");
72                 return NULL;
73         }
74
75         group = (cfg_group_t *)pkg_malloc(sizeof(cfg_group_t)+name_len-1);
76         if (!group) {
77                 LOG(L_ERR, "ERROR: cfg_new_group(): not enough memory\n");
78                 return NULL;
79         }
80         memset(group, 0, sizeof(cfg_group_t)+name_len-1);
81
82         group->num = num;
83         group->mapping = mapping;
84         group->vars = vars;
85         group->size = size;
86         group->handle = handle;
87         group->name_len = name_len;
88         memcpy(&group->name, name, name_len);
89
90         /* add the new group to the beginning of the list */
91         group->next = cfg_group;
92         cfg_group = group;
93
94         return group;
95 }
96
97 /* clones a string to shared memory */
98 char *cfg_clone_str(str s)
99 {
100         char    *c;
101
102         c = (char *)shm_malloc(sizeof(char)*(s.len+1));
103         if (!c) {
104                 LOG(L_ERR, "ERROR: cfg_clone_str(): not enough shm memory\n");
105                 return NULL;
106         }
107         memcpy(c, s.s, s.len);
108         c[s.len] = '\0';
109
110         return c;
111 }
112
113 /* copies the strings to shared memory */
114 static int cfg_shmize_strings(cfg_group_t *group)
115 {
116         cfg_mapping_t   *mapping;
117         int     i;
118         str     s;
119
120         /* We do not know in advance whether the variable will be changed or not,
121         and it can happen that we try to free the shm memory area when the variable
122         is changed, hence, it must be already in shm mem */
123         mapping = group->mapping;
124         for (i=0; i<group->num; i++) {
125                 /* the cfg driver module may have already shmized the variable */
126                 if (mapping[i].flag & cfg_var_shmized) continue;
127
128                 if (CFG_VAR_TYPE(&mapping[i]) == CFG_VAR_STRING) {
129                         memcpy(&s.s, group->vars + mapping[i].offset, sizeof(char *));
130                         s.len = strlen(s.s);
131
132                 } else if (CFG_VAR_TYPE(&mapping[i]) == CFG_VAR_STR) {
133                         memcpy(&s, group->vars + mapping[i].offset, sizeof(str));
134
135                 } else {
136                         continue;
137                 }
138                 if (!(s.s = cfg_clone_str(s))) return -1;
139                 memcpy(group->vars + mapping[i].offset, &s.s, sizeof(char *));
140                 mapping[i].flag |= cfg_var_shmized;
141         }
142
143         return 0;
144 }
145
146 /* copy the variables to shm mem */
147 int cfg_shmize(void)
148 {
149         cfg_group_t     *group;
150         cfg_block_t     *block = NULL;
151         int     size;
152
153         if (!cfg_group) return 0;
154
155         /* Let us allocate one memory block that
156         will contain all the variables */
157         for (   size=0, group = cfg_group;
158                 group;
159                 group=group->next
160         ) {
161                 size = ROUND_POINTER(size);
162                 group->offset = size;
163                 size += group->size;
164         }
165
166         block = (cfg_block_t*)shm_malloc(sizeof(cfg_block_t)+size-1);
167         if (!block) {
168                 LOG(L_ERR, "ERROR: cfg_clone_str(): not enough shm memory\n");
169                 goto error;
170         }
171         memset(block, 0, sizeof(cfg_block_t)+size-1);
172         cfg_block_size = size;
173
174         /* copy the memory fragments to the single block */
175         for (   group = cfg_group;
176                 group;
177                 group=group->next
178         ) {
179                 if (group->dynamic == 0) {
180                         /* clone the strings to shm mem */
181                         if (cfg_shmize_strings(group)) goto error;
182
183                         /* copy the values to the new block,
184                         and update the module's handle */
185                         memcpy(block->vars+group->offset, group->vars, group->size);
186                         *(group->handle) = block->vars+group->offset;
187                 } else {
188                         /* The group was declared with NULL values,
189                          * we have to fix it up.
190                          * The fixup function takes care about the values,
191                          * it fills up the block */
192                         if (cfg_script_fixup(group, block->vars+group->offset)) goto error;
193                         *(group->handle) = block->vars+group->offset;
194
195                         /* notify the drivers about the new config definition */
196                         cfg_notify_drivers(group->name, group->name_len,
197                                         group->mapping->def);
198                 }
199         }
200
201         /* install the new config */
202         cfg_install_global(block, NULL, NULL, NULL);
203         cfg_shmized = 1;
204
205         return 0;
206
207 error:
208         if (block) shm_free(block);
209         return -1;
210 }
211
212 /* deallocate the list of groups, and the shmized strings */
213 static void cfg_destory_groups(unsigned char *block)
214 {
215         cfg_group_t     *group, *group2;
216         cfg_mapping_t   *mapping;
217         cfg_def_t       *def;
218         void            *old_string;
219         int             i;
220
221         group = cfg_group;
222         while(group) {
223                 mapping = group->mapping;
224                 def = mapping ? mapping->def : NULL;
225
226                 /* destory the shmized strings in the block */
227                 if (block && def)
228                         for (i=0; i<group->num; i++)
229                                 if (((CFG_VAR_TYPE(&mapping[i]) == CFG_VAR_STRING) ||
230                                 (CFG_VAR_TYPE(&mapping[i]) == CFG_VAR_STR)) &&
231                                         mapping[i].flag & cfg_var_shmized) {
232
233                                                 memcpy( &old_string,
234                                                         block + group->offset + mapping[i].offset,
235                                                         sizeof(char *));
236                                                 shm_free(old_string);
237                                 }
238
239                 if (group->dynamic) {
240                         /* the group was dynamically allocated */
241                         cfg_script_destroy(group);
242                 } else {
243                         /* only the mapping was allocated, all the other
244                         pointers are just set to static variables */
245                         if (mapping) pkg_free(mapping);
246                 }
247
248                 group2 = group->next;
249                 pkg_free(group);
250                 group = group2;
251         }
252 }
253
254 /* initiate the cfg framework */
255 int cfg_init(void)
256 {
257         cfg_global_lock = lock_alloc();
258         if (!cfg_global_lock) {
259                 LOG(L_ERR, "ERROR: cfg_init(): not enough shm memory\n");
260                 goto error;
261         }
262         if (lock_init(cfg_global_lock) == 0) {
263                 LOG(L_ERR, "ERROR: cfg_init(): failed to init lock\n");
264                 lock_dealloc(cfg_global_lock);
265                 cfg_global_lock = 0;
266                 goto error;
267         }
268
269         cfg_writer_lock = lock_alloc();
270         if (!cfg_writer_lock) {
271                 LOG(L_ERR, "ERROR: cfg_init(): not enough shm memory\n");
272                 goto error;
273         }
274         if (lock_init(cfg_writer_lock) == 0) {
275                 LOG(L_ERR, "ERROR: cfg_init(): failed to init lock\n");
276                 lock_dealloc(cfg_writer_lock);
277                 cfg_writer_lock = 0;
278                 goto error;
279         }
280
281         cfg_global = (cfg_block_t **)shm_malloc(sizeof(cfg_block_t *));
282         if (!cfg_global) {
283                 LOG(L_ERR, "ERROR: cfg_init(): not enough shm memory\n");
284                 goto error;
285         }
286         *cfg_global = NULL;
287
288         cfg_child_cb_first = (cfg_child_cb_t **)shm_malloc(sizeof(cfg_child_cb_t *));
289         if (!cfg_child_cb_first) {
290                 LOG(L_ERR, "ERROR: cfg_init(): not enough shm memory\n");
291                 goto error;
292         }
293         *cfg_child_cb_first = NULL;
294
295         cfg_child_cb_last = (cfg_child_cb_t **)shm_malloc(sizeof(cfg_child_cb_t *));
296         if (!cfg_child_cb_last) {
297                 LOG(L_ERR, "ERROR: cfg_init(): not enough shm memory\n");
298                 goto error;
299         }
300         *cfg_child_cb_last = NULL;
301
302         /* A new cfg_child_cb struct must be created with a NULL callback function.
303         This stucture will be the entry point for the child processes, and
304         will be freed later, when none of the processes refers to it */
305         *cfg_child_cb_first = *cfg_child_cb_last =
306                 cfg_child_cb_new(NULL, NULL);
307
308         if (!*cfg_child_cb_first) goto error;
309
310         return 0;
311
312 error:
313         cfg_destroy();
314
315         return -1;
316 }
317
318 /* destroy the memory allocated for the cfg framework */
319 void cfg_destroy(void)
320 {
321         /* free the contexts */
322         cfg_ctx_destroy();
323
324         /* free the list of groups */
325         cfg_destory_groups(cfg_global ? (*cfg_global)->vars : NULL);
326
327         if (cfg_child_cb_first) {
328                 if (*cfg_child_cb_first) cfg_child_cb_free(*cfg_child_cb_first);
329                 shm_free(cfg_child_cb_first);
330                 cfg_child_cb_first = NULL;
331         }
332
333         if (cfg_child_cb_last) {
334                 shm_free(cfg_child_cb_last);
335                 cfg_child_cb_last = NULL;
336         }
337
338         if (cfg_global) {
339                 if (*cfg_global) cfg_block_free(*cfg_global);
340                 shm_free(cfg_global);
341                 cfg_global = NULL;
342         }
343         if (cfg_global_lock) {
344                 lock_destroy(cfg_global_lock);
345                 lock_dealloc(cfg_global_lock);
346                 cfg_global_lock = 0;
347         }
348         if (cfg_writer_lock) {
349                 lock_destroy(cfg_writer_lock);
350                 lock_dealloc(cfg_writer_lock);
351                 cfg_writer_lock = 0;
352         }
353 }
354
355 /* per-child process init function */
356 int cfg_child_init(void)
357 {
358         /* set the callback list pointer to the beginning of the list */
359         cfg_child_cb = *cfg_child_cb_first;
360         atomic_inc(&cfg_child_cb->refcnt);
361
362         return 0;
363 }
364
365 /* per-child process destroy function
366  * Should be called only when the child process exits,
367  * but SER continues running
368  *
369  * WARNING: this function call must be the very last action
370  * before the child process exits, because the local config
371  * is not available afterwards.
372  */
373 void cfg_child_destroy(void)
374 {
375         /* unref the local config */
376         if (cfg_local) {
377                 CFG_UNREF(cfg_local);
378                 cfg_local = NULL;
379         }
380
381         /* unref the per-process callback list */
382         if (atomic_dec_and_test(&cfg_child_cb->refcnt)) {
383                 /* No more pocess refers to this callback.
384                 Did this process block the deletion,
385                 or is there any other process that has not
386                 reached prev_cb yet? */
387                 CFG_LOCK();
388                 if (*cfg_child_cb_first == cfg_child_cb) {
389                         /* yes, this process was blocking the deletion */
390                         *cfg_child_cb_first = cfg_child_cb->next;
391                         CFG_UNLOCK();
392                         shm_free(cfg_child_cb);
393                 } else {
394                         CFG_UNLOCK();
395                 }
396         }
397         cfg_child_cb = NULL;
398 }
399
400 /* searches a variable definition by group and variable name */
401 int cfg_lookup_var(str *gname, str *vname,
402                         cfg_group_t **group, cfg_mapping_t **var)
403 {
404         cfg_group_t     *g;
405         int             i;
406
407         for (   g = cfg_group;
408                 g;
409                 g = g->next
410         )
411                 if ((g->name_len == gname->len)
412                 && (memcmp(g->name, gname->s, gname->len)==0)) {
413
414                         for (   i = 0;
415                                 i < g->num;
416                                 i++
417                         ) {
418                                 if ((g->mapping[i].name_len == vname->len)
419                                 && (memcmp(g->mapping[i].def->name, vname->s, vname->len)==0)) {
420                                         if (group) *group = g;
421                                         if (var) *var = &(g->mapping[i]);
422                                         return 0;
423                                 }
424                         }
425                         break;
426                 }
427
428         LOG(L_ERR, "ERROR: cfg_lookup_var(): variable not found: %.*s.%.*s\n",
429                         gname->len, gname->s,
430                         vname->len, vname->s);
431         return -1;
432 }
433
434 /* clones the global config block
435  * WARNING: unsafe, cfg_writer_lock or cfg_global_lock must be held!
436  */
437 cfg_block_t *cfg_clone_global(void)
438 {
439         cfg_block_t     *block;
440
441         block = (cfg_block_t*)shm_malloc(sizeof(cfg_block_t)+cfg_block_size-1);
442         if (!block) {
443                 LOG(L_ERR, "ERROR: cfg_clone_global(): not enough shm memory\n");
444                 return NULL;
445         }
446         memcpy(block, *cfg_global, sizeof(cfg_block_t)+cfg_block_size-1);
447
448         /* reset the reference counter */
449         atomic_set(&block->refcnt, 0);
450
451         return block;
452 }
453
454 /* installs a new global config
455  *
456  * replaced is an array of strings that must be freed together
457  * with the previous global config.
458  * cb_first and cb_last define a linked list of per-child process
459  * callbacks. This list is added to the global linked list.
460  */
461 void cfg_install_global(cfg_block_t *block, char **replaced,
462                         cfg_child_cb_t *cb_first, cfg_child_cb_t *cb_last)
463 {
464         CFG_LOCK();
465
466         if (*cfg_global) {
467                 if (replaced) (*cfg_global)->replaced = replaced;
468                 CFG_UNREF(*cfg_global);
469         }
470         CFG_REF(block);
471         *cfg_global = block;
472
473         if (cb_first) {
474                 /* add the new callbacks to the end of the linked-list */
475                 (*cfg_child_cb_last)->next = cb_first;
476                 *cfg_child_cb_last = cb_last;
477         }
478
479         CFG_UNLOCK();
480
481 }
482
483 /* creates a structure for a per-child process callback */
484 cfg_child_cb_t *cfg_child_cb_new(str *name, cfg_on_set_child cb)
485 {
486         cfg_child_cb_t  *cb_struct;
487
488         cb_struct = (cfg_child_cb_t *)shm_malloc(sizeof(cfg_child_cb_t));
489         if (!cb_struct) {
490                 LOG(L_ERR, "ERROR: cfg_child_cb_new(): not enough shm memory\n");
491                 return NULL;
492         }
493         memset(cb_struct, 0, sizeof(cfg_child_cb_t));
494         if (name) {
495                 cb_struct->name.s = name->s;
496                 cb_struct->name.len = name->len;
497         }
498         cb_struct->cb = cb;
499         atomic_set(&cb_struct->refcnt, 0);
500
501         return cb_struct;
502 }
503
504 /* free the memory allocated for a child cb list */
505 void cfg_child_cb_free(cfg_child_cb_t *child_cb_first)
506 {
507         cfg_child_cb_t  *cb, *cb_next;
508
509         for(    cb = child_cb_first;
510                 cb;
511                 cb = cb_next
512         ) {
513                 cb_next = cb->next;
514                 shm_free(cb);
515         }
516 }