25e9c54ae92b3cd53cf11daa01a182cba012ac91
[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  * (src and dst can be the same)
99  */
100 int cfg_clone_str(str *src, str *dst)
101 {
102         char    *c;
103
104         if (!src->s) {
105                 dst->s = NULL;
106                 dst->len = 0;
107                 return 0;
108         }
109
110         c = (char *)shm_malloc(sizeof(char)*(src->len+1));
111         if (!c) {
112                 LOG(L_ERR, "ERROR: cfg_clone_str(): not enough shm memory\n");
113                 return -1;
114         }
115         memcpy(c, src->s, src->len);
116         c[src->len] = '\0';
117
118         dst->s = c;
119         dst->len = src->len;
120
121         return 0;
122 }
123
124 /* copies the strings to shared memory */
125 static int cfg_shmize_strings(cfg_group_t *group)
126 {
127         cfg_mapping_t   *mapping;
128         int     i;
129         str     s;
130
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;
138
139                 if (CFG_VAR_TYPE(&mapping[i]) == CFG_VAR_STRING) {
140                         memcpy(&s.s, group->vars + mapping[i].offset, sizeof(char *));
141                         if (!s.s) continue;
142                         s.len = strlen(s.s);
143
144                 } else if (CFG_VAR_TYPE(&mapping[i]) == CFG_VAR_STR) {
145                         memcpy(&s, group->vars + mapping[i].offset, sizeof(str));
146                         if (!s.s) continue;
147
148                 } else {
149                         continue;
150                 }
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;
154         }
155
156         return 0;
157 }
158
159 /* copy the variables to shm mem */
160 int cfg_shmize(void)
161 {
162         cfg_group_t     *group;
163         cfg_block_t     *block = NULL;
164         int     size;
165
166         if (!cfg_group) return 0;
167
168         /* Let us allocate one memory block that
169         will contain all the variables */
170         for (   size=0, group = cfg_group;
171                 group;
172                 group=group->next
173         ) {
174                 size = ROUND_POINTER(size);
175                 group->offset = size;
176                 size += group->size;
177         }
178
179         block = (cfg_block_t*)shm_malloc(sizeof(cfg_block_t)+size-1);
180         if (!block) {
181                 LOG(L_ERR, "ERROR: cfg_clone_str(): not enough shm memory\n");
182                 goto error;
183         }
184         memset(block, 0, sizeof(cfg_block_t)+size-1);
185         cfg_block_size = size;
186
187         /* copy the memory fragments to the single block */
188         for (   group = cfg_group;
189                 group;
190                 group=group->next
191         ) {
192                 if (group->dynamic == 0) {
193                         /* clone the strings to shm mem */
194                         if (cfg_shmize_strings(group)) goto error;
195
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;
200                 } else {
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;
207
208                         /* notify the drivers about the new config definition */
209                         cfg_notify_drivers(group->name, group->name_len,
210                                         group->mapping->def);
211                 }
212         }
213
214         /* install the new config */
215         cfg_install_global(block, NULL, NULL, NULL);
216         cfg_shmized = 1;
217
218         return 0;
219
220 error:
221         if (block) shm_free(block);
222         return -1;
223 }
224
225 /* deallocate the list of groups, and the shmized strings */
226 static void cfg_destory_groups(unsigned char *block)
227 {
228         cfg_group_t     *group, *group2;
229         cfg_mapping_t   *mapping;
230         cfg_def_t       *def;
231         void            *old_string;
232         int             i;
233
234         group = cfg_group;
235         while(group) {
236                 mapping = group->mapping;
237                 def = mapping ? mapping->def : NULL;
238
239                 /* destory the shmized strings in the block */
240                 if (block && def)
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) {
245
246                                                 memcpy( &old_string,
247                                                         block + group->offset + mapping[i].offset,
248                                                         sizeof(char *));
249                                                 if (old_string) shm_free(old_string);
250                                 }
251
252                 if (group->dynamic) {
253                         /* the group was dynamically allocated */
254                         cfg_script_destroy(group);
255                 } else {
256                         /* only the mapping was allocated, all the other
257                         pointers are just set to static variables */
258                         if (mapping) pkg_free(mapping);
259                 }
260
261                 group2 = group->next;
262                 pkg_free(group);
263                 group = group2;
264         }
265 }
266
267 /* initiate the cfg framework */
268 int cfg_init(void)
269 {
270         cfg_global_lock = lock_alloc();
271         if (!cfg_global_lock) {
272                 LOG(L_ERR, "ERROR: cfg_init(): not enough shm memory\n");
273                 goto error;
274         }
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);
278                 cfg_global_lock = 0;
279                 goto error;
280         }
281
282         cfg_writer_lock = lock_alloc();
283         if (!cfg_writer_lock) {
284                 LOG(L_ERR, "ERROR: cfg_init(): not enough shm memory\n");
285                 goto error;
286         }
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);
290                 cfg_writer_lock = 0;
291                 goto error;
292         }
293
294         cfg_global = (cfg_block_t **)shm_malloc(sizeof(cfg_block_t *));
295         if (!cfg_global) {
296                 LOG(L_ERR, "ERROR: cfg_init(): not enough shm memory\n");
297                 goto error;
298         }
299         *cfg_global = NULL;
300
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");
304                 goto error;
305         }
306         *cfg_child_cb_first = NULL;
307
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");
311                 goto error;
312         }
313         *cfg_child_cb_last = NULL;
314
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);
320
321         if (!*cfg_child_cb_first) goto error;
322
323         return 0;
324
325 error:
326         cfg_destroy();
327
328         return -1;
329 }
330
331 /* destroy the memory allocated for the cfg framework */
332 void cfg_destroy(void)
333 {
334         /* free the contexts */
335         cfg_ctx_destroy();
336
337         /* free the list of groups */
338         cfg_destory_groups(cfg_global ? (*cfg_global)->vars : NULL);
339
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;
344         }
345
346         if (cfg_child_cb_last) {
347                 shm_free(cfg_child_cb_last);
348                 cfg_child_cb_last = NULL;
349         }
350
351         if (cfg_global) {
352                 if (*cfg_global) cfg_block_free(*cfg_global);
353                 shm_free(cfg_global);
354                 cfg_global = NULL;
355         }
356         if (cfg_global_lock) {
357                 lock_destroy(cfg_global_lock);
358                 lock_dealloc(cfg_global_lock);
359                 cfg_global_lock = 0;
360         }
361         if (cfg_writer_lock) {
362                 lock_destroy(cfg_writer_lock);
363                 lock_dealloc(cfg_writer_lock);
364                 cfg_writer_lock = 0;
365         }
366 }
367
368 /* per-child process init function */
369 int cfg_child_init(void)
370 {
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);
374
375         return 0;
376 }
377
378 /* per-child process destroy function
379  * Should be called only when the child process exits,
380  * but SER continues running
381  *
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.
385  */
386 void cfg_child_destroy(void)
387 {
388         /* unref the local config */
389         if (cfg_local) {
390                 CFG_UNREF(cfg_local);
391                 cfg_local = NULL;
392         }
393
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? */
400                 CFG_LOCK();
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;
404                         CFG_UNLOCK();
405                         shm_free(cfg_child_cb);
406                 } else {
407                         CFG_UNLOCK();
408                 }
409         }
410         cfg_child_cb = NULL;
411 }
412
413 /* searches a group by name */
414 cfg_group_t *cfg_lookup_group(char *name, int len)
415 {
416         cfg_group_t     *g;
417
418         for (   g = cfg_group;
419                 g;
420                 g = g->next
421         )
422                 if ((g->name_len == len)
423                 && (memcmp(g->name, name, len)==0))
424                         return g;
425
426         return NULL;
427 }
428
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)
432 {
433         cfg_group_t     *g;
434         int             i;
435
436         for (   g = cfg_group;
437                 g;
438                 g = g->next
439         )
440                 if ((g->name_len == gname->len)
441                 && (memcmp(g->name, gname->s, gname->len)==0)) {
442
443                         for (   i = 0;
444                                 i < g->num;
445                                 i++
446                         ) {
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]);
451                                         return 0;
452                                 }
453                         }
454                         break;
455                 }
456
457         LOG(L_ERR, "ERROR: cfg_lookup_var(): variable not found: %.*s.%.*s\n",
458                         gname->len, gname->s,
459                         vname->len, vname->s);
460         return -1;
461 }
462
463 /* clones the global config block
464  * WARNING: unsafe, cfg_writer_lock or cfg_global_lock must be held!
465  */
466 cfg_block_t *cfg_clone_global(void)
467 {
468         cfg_block_t     *block;
469
470         block = (cfg_block_t*)shm_malloc(sizeof(cfg_block_t)+cfg_block_size-1);
471         if (!block) {
472                 LOG(L_ERR, "ERROR: cfg_clone_global(): not enough shm memory\n");
473                 return NULL;
474         }
475         memcpy(block, *cfg_global, sizeof(cfg_block_t)+cfg_block_size-1);
476
477         /* reset the reference counter */
478         atomic_set(&block->refcnt, 0);
479
480         return block;
481 }
482
483 /* installs a new global config
484  *
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.
489  */
490 void cfg_install_global(cfg_block_t *block, char **replaced,
491                         cfg_child_cb_t *cb_first, cfg_child_cb_t *cb_last)
492 {
493         CFG_LOCK();
494
495         if (*cfg_global) {
496                 if (replaced) (*cfg_global)->replaced = replaced;
497                 CFG_UNREF(*cfg_global);
498         }
499         CFG_REF(block);
500         *cfg_global = block;
501
502         if (cb_first) {
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;
506         }
507
508         CFG_UNLOCK();
509
510 }
511
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)
514 {
515         cfg_child_cb_t  *cb_struct;
516
517         cb_struct = (cfg_child_cb_t *)shm_malloc(sizeof(cfg_child_cb_t));
518         if (!cb_struct) {
519                 LOG(L_ERR, "ERROR: cfg_child_cb_new(): not enough shm memory\n");
520                 return NULL;
521         }
522         memset(cb_struct, 0, sizeof(cfg_child_cb_t));
523         if (name) {
524                 cb_struct->name.s = name->s;
525                 cb_struct->name.len = name->len;
526         }
527         cb_struct->cb = cb;
528         atomic_set(&cb_struct->refcnt, 0);
529
530         return cb_struct;
531 }
532
533 /* free the memory allocated for a child cb list */
534 void cfg_child_cb_free(cfg_child_cb_t *child_cb_first)
535 {
536         cfg_child_cb_t  *cb, *cb_next;
537
538         for(    cb = child_cb_first;
539                 cb;
540                 cb = cb_next
541         ) {
542                 cb_next = cb->next;
543                 shm_free(cb);
544         }
545 }