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