b3596fa941f21f2d0a9651f0428bd0ad9dee19dc
[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 /* searches a variable definition by group and variable name */
306 int cfg_lookup_var(str *gname, str *vname,
307                         cfg_group_t **group, cfg_mapping_t **var)
308 {
309         cfg_group_t     *g;
310         int             i;
311
312         for (   g = cfg_group;
313                 g;
314                 g = g->next
315         )
316                 if ((g->name_len == gname->len)
317                 && (memcmp(g->name, gname->s, gname->len)==0)) {
318
319                         for (   i = 0;
320                                 i < g->size;
321                                 i++
322                         ) {
323                                 if ((g->mapping[i].name_len == vname->len)
324                                 && (memcmp(g->mapping[i].def->name, vname->s, vname->len)==0)) {
325                                         if (group) *group = g;
326                                         if (var) *var = &(g->mapping[i]);
327                                         return 0;
328                                 }
329                         }
330                         break;
331                 }
332
333         LOG(L_ERR, "ERROR: cfg_lookup_var(): variable not found: %.*s.%.*s\n",
334                         gname->len, gname->s,
335                         vname->len, vname->s);
336         return -1;
337 }
338
339 /* clones the global config block
340  * WARNING: unsafe, cfg_writer_lock or cfg_global_lock must be held!
341  */
342 cfg_block_t *cfg_clone_global(void)
343 {
344         cfg_block_t     *block;
345
346         block = (cfg_block_t*)shm_malloc(sizeof(cfg_block_t)+cfg_block_size-1);
347         if (!block) {
348                 LOG(L_ERR, "ERROR: cfg_clone_global(): not enough shm memory\n");
349                 return NULL;
350         }
351         memcpy(block, *cfg_global, sizeof(cfg_block_t)+cfg_block_size-1);
352
353         /* reset the reference counter */
354         atomic_set(&block->refcnt, 0);
355
356         return block;
357 }
358
359 /* installs a new global config
360  *
361  * replaced is an array of strings that must be freed together
362  * with the previous global config.
363  * cb_first and cb_last define a linked list of per-child process
364  * callbacks. This list is added to the global linked list.
365  */
366 void cfg_install_global(cfg_block_t *block, char **replaced,
367                         cfg_child_cb_t *cb_first, cfg_child_cb_t *cb_last)
368 {
369         CFG_LOCK();
370
371         if (*cfg_global) {
372                 if (replaced) (*cfg_global)->replaced = replaced;
373                 CFG_UNREF(*cfg_global);
374         }
375         CFG_REF(block);
376         *cfg_global = block;
377
378         if (cb_first) {
379                 /* add the new callbacks to the end of the linked-list */
380                 (*cfg_child_cb_last)->next = cb_first;
381                 *cfg_child_cb_last = cb_last;
382         }
383
384         CFG_UNLOCK();
385
386 }
387
388 /* creates a structure for a per-child process callback */
389 cfg_child_cb_t *cfg_child_cb_new(str *name, cfg_on_set_child cb)
390 {
391         cfg_child_cb_t  *cb_struct;
392
393         cb_struct = (cfg_child_cb_t *)shm_malloc(sizeof(cfg_child_cb_t));
394         if (!cb_struct) {
395                 LOG(L_ERR, "ERROR: cfg_child_cb_new(): not enough shm memory\n");
396                 return NULL;
397         }
398         memset(cb_struct, 0, sizeof(cfg_child_cb_t));
399         if (name) {
400                 cb_struct->name.s = name->s;
401                 cb_struct->name.len = name->len;
402         }
403         cb_struct->cb = cb;
404         atomic_set(&cb_struct->refcnt, 0);
405
406         return cb_struct;
407 }
408
409 /* free the memory allocated for a child cb list */
410 void cfg_child_cb_free(cfg_child_cb_t *child_cb_first)
411 {
412         cfg_child_cb_t  *cb, *cb_next;
413
414         for(    cb = child_cb_first;
415                 cb;
416                 cb = cb_next
417         ) {
418                 cb_next = cb->next;
419                 shm_free(cb);
420         }
421 }