Browse code

Implementing a configuration framework, that can be used by SER core, and by the modules, to get and set internal variables on-the-fly, and eliminate SER restarts whenever it is possible.

The core and the modules can declare configuration variables, and can
retrieve the value of the variables at any time without performance
overhead. The framework makes sure that the variables do not change
during the SIP message processing, the child processes see a snapshot
of the variables with constant values. The variable, that is changed by
a cfg driver module, will be automatically replaced by the framework
the next time a SIP message is started to be processed.

The drivers can change the value of all the variables by names with or
without the need of commit. That means a kind of transaction support,
the framework can keep track of the changes (per driver) until they
are committed or rolled-back.

Miklos Tirpak authored on 05/12/2007 15:22:01
Showing 6 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,152 @@
0
+/*
1
+ * $Id$
2
+ *
3
+ * Copyright (C) 2007 iptelorg GmbH
4
+ *
5
+ * This file is part of ser, a free SIP server.
6
+ *
7
+ * ser is free software; you can redistribute it and/or modify
8
+ * it under the terms of the GNU General Public License as published by
9
+ * the Free Software Foundation; either version 2 of the License, or
10
+ * (at your option) any later version
11
+ *
12
+ * For a license to use the ser software under conditions
13
+ * other than those described here, or to purchase support for this
14
+ * software, please contact iptel.org by e-mail at the following addresses:
15
+ *    info@iptel.org
16
+ *
17
+ * ser is distributed in the hope that it will be useful,
18
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
+ * GNU General Public License for more details.
21
+ *
22
+ * You should have received a copy of the GNU General Public License
23
+ * along with this program; if not, write to the Free Software
24
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
25
+ *
26
+ * History
27
+ * -------
28
+ *  2007-12-03	Initial version (Miklos)
29
+ */
30
+
31
+#include <string.h>
32
+
33
+#include "../ut.h"
34
+#include "../mem/mem.h"
35
+#include "cfg_struct.h"
36
+#include "cfg_ctx.h"
37
+#include "cfg.h"
38
+
39
+/* declares a new cfg group
40
+ * handler is set to the memory area where the variables are stored
41
+ * return value is -1 on error
42
+ */
43
+int cfg_declare(char *group_name, cfg_def_t *def, void *values, int def_size,
44
+			void **handle)
45
+{
46
+	int	i, num, size;
47
+	cfg_mapping_t	*mapping = NULL;
48
+
49
+	/* check the number of the variables */
50
+	for (num=0; def[num].name; num++);
51
+
52
+	mapping = (cfg_mapping_t *)pkg_malloc(sizeof(cfg_mapping_t)*num);
53
+	if (!mapping) {
54
+		LOG(L_ERR, "ERROR: register_cfg_def(): not enough memory\n");
55
+		goto error;
56
+	}
57
+	memset(mapping, 0, sizeof(cfg_mapping_t)*num);
58
+
59
+	/* calculate the size of the memory block that has to
60
+	be allocated for the cfg variables, and set the content of the 
61
+	cfg_mapping array the same time */
62
+	for (i=0, size=0; i<num; i++) {
63
+		mapping[i].def = &(def[i]);
64
+		mapping[i].name_len = strlen(def[i].name);
65
+
66
+		/* padding depends on the type of the next variable */
67
+		switch (CFG_VAR_MASK(def[i].type)) {
68
+
69
+		case CFG_VAR_INT:
70
+			size = ROUND_INT(size);
71
+			mapping[i].offset = size;
72
+			size += sizeof(int);
73
+			break;
74
+
75
+		case CFG_VAR_STRING:
76
+		case CFG_VAR_POINTER:
77
+			size = ROUND_POINTER(size);
78
+			mapping[i].offset = size;
79
+			size += sizeof(char *);
80
+			break;
81
+
82
+		case CFG_VAR_STR:
83
+			size = ROUND_POINTER(size);
84
+			mapping[i].offset = size;
85
+			size += sizeof(str);
86
+			break;
87
+
88
+		default:
89
+			LOG(L_ERR, "ERROR: register_cfg_def(): %s.%s: unsupported variable type\n",
90
+					group_name, def[i].name);
91
+			goto error;
92
+		}
93
+
94
+		/* verify the type of the input */
95
+		if (CFG_INPUT_MASK(def[i].type)==0) {
96
+			def[i].type |= def[i].type << 3;
97
+		} else {
98
+			if ((CFG_INPUT_MASK(def[i].type) != CFG_VAR_MASK(def[i].type) << 3)
99
+			&& (def[i].on_change_cb == 0)) {
100
+				LOG(L_ERR, "ERROR: register_cfg_def(): %s.%s: variable and input types are "
101
+					"different, but no callback is defined for conversion\n",
102
+					group_name, def[i].name);
103
+				goto error;
104
+			}
105
+		}
106
+
107
+		if (CFG_INPUT_MASK(def[i].type) > CFG_INPUT_STR) {
108
+			LOG(L_ERR, "ERROR: register_cfg_def(): %s.%s: unsupported input type\n",
109
+					group_name, def[i].name);
110
+			goto error;
111
+		}
112
+	}
113
+
114
+	/* minor validation */
115
+	if (size != def_size) {
116
+		LOG(L_ERR, "ERROR: register_cfg_def(): the specified size of the config "
117
+			"structure does not equal with the calculated size, check whether "
118
+			"the variable types are correctly defined!\n");
119
+		goto error;
120
+	}
121
+
122
+	/* create a new group
123
+	I will allocate memory in shm mem for the variables later in a single block,
124
+	when we know the size of all the registered groups. */
125
+	if (cfg_new_group(group_name, num, mapping, values, size, handle))
126
+		goto error;
127
+
128
+	/* The cfg variables are ready to use, let us set the handle
129
+	before passing the new definitions to the drivers.
130
+	We make the interface usable for the fixup functions
131
+	at this step */
132
+	*handle = values;
133
+
134
+	/* notify the drivers about the new config definition */
135
+	cfg_notify_drivers(group_name, def);
136
+
137
+	LOG(L_DBG, "DEBUG: register_cfg_def(): "
138
+		"new config group has been registered: '%s' (num=%d, size=%d)\n",
139
+		group_name, num, size);
140
+
141
+	/* TODO: inform the drivers about the new definition */
142
+
143
+	return 0;
144
+
145
+error:
146
+	if (mapping) pkg_free(mapping);
147
+	LOG(L_ERR, "ERROR: register_cfg_def(): Failed to register the config group: %s\n",
148
+			group_name);
149
+
150
+	return -1;
151
+}
0 152
new file mode 100644
... ...
@@ -0,0 +1,76 @@
0
+/*
1
+ * $Id$
2
+ *
3
+ * Copyright (C) 2007 iptelorg GmbH
4
+ *
5
+ * This file is part of ser, a free SIP server.
6
+ *
7
+ * ser is free software; you can redistribute it and/or modify
8
+ * it under the terms of the GNU General Public License as published by
9
+ * the Free Software Foundation; either version 2 of the License, or
10
+ * (at your option) any later version
11
+ *
12
+ * For a license to use the ser software under conditions
13
+ * other than those described here, or to purchase support for this
14
+ * software, please contact iptel.org by e-mail at the following addresses:
15
+ *    info@iptel.org
16
+ *
17
+ * ser is distributed in the hope that it will be useful,
18
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
+ * GNU General Public License for more details.
21
+ *
22
+ * You should have received a copy of the GNU General Public License
23
+ * along with this program; if not, write to the Free Software
24
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
25
+ *
26
+ * History
27
+ * -------
28
+ *  2007-12-03	Initial version (Miklos)
29
+ */
30
+
31
+#ifndef _CFG_H
32
+#define _CFG_H
33
+
34
+#include "../str.h"
35
+
36
+#define CFG_VAR_INT		1U
37
+#define CFG_VAR_STRING		2U
38
+#define CFG_VAR_STR		3U
39
+#define CFG_VAR_POINTER		4U
40
+
41
+#define CFG_INPUT_INT		(CFG_VAR_INT << 3)
42
+#define CFG_INPUT_STRING	(CFG_VAR_STRING << 3)
43
+#define CFG_INPUT_STR		(CFG_VAR_STR << 3)
44
+
45
+#define CFG_VAR_MASK(x)		((x)&(CFG_INPUT_INT-1))
46
+#define CFG_INPUT_MASK(x)	((x)&(~(CFG_INPUT_INT-1)))
47
+
48
+typedef int (*cfg_on_change)(void *, str *, void **);
49
+typedef void (*cfg_on_set_child)(str *);
50
+
51
+/* strutrure to be used buy the module interface */
52
+typedef struct _cfg_def {
53
+	char	*name;
54
+	unsigned int	type;
55
+	int	min;
56
+	int	max;
57
+	cfg_on_change	on_change_cb;
58
+	cfg_on_set_child	on_set_child_cb;
59
+	char	*descr;
60
+} cfg_def_t;
61
+
62
+/* declares a new cfg group
63
+ * handler is set to the memory area where the variables are stored
64
+ * return value is -1 on error
65
+ */
66
+int cfg_declare(char *group_name, cfg_def_t *def, void *values, int def_size,
67
+			void **handler);
68
+
69
+#define cfg_size(gname) \
70
+	sizeof(struct cfg_group_##gname)
71
+
72
+#define cfg_get(gname, handle, var) \
73
+	((struct cfg_group_##gname *)handle)->var
74
+
75
+#endif /* _CFG_H */
0 76
new file mode 100644
... ...
@@ -0,0 +1,773 @@
0
+/*
1
+ * $Id$
2
+ *
3
+ * Copyright (C) 2007 iptelorg GmbH
4
+ *
5
+ * This file is part of ser, a free SIP server.
6
+ *
7
+ * ser is free software; you can redistribute it and/or modify
8
+ * it under the terms of the GNU General Public License as published by
9
+ * the Free Software Foundation; either version 2 of the License, or
10
+ * (at your option) any later version
11
+ *
12
+ * For a license to use the ser software under conditions
13
+ * other than those described here, or to purchase support for this
14
+ * software, please contact iptel.org by e-mail at the following addresses:
15
+ *    info@iptel.org
16
+ *
17
+ * ser is distributed in the hope that it will be useful,
18
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
+ * GNU General Public License for more details.
21
+ *
22
+ * You should have received a copy of the GNU General Public License
23
+ * along with this program; if not, write to the Free Software
24
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
25
+ *
26
+ * History
27
+ * -------
28
+ *  2007-12-03	Initial version (Miklos)
29
+ */
30
+
31
+#include <string.h>
32
+
33
+#include "cfg_struct.h"
34
+#include "cfg_ctx.h"
35
+
36
+/* linked list of all the registered cfg contexts */
37
+static cfg_ctx_t	*cfg_ctx_list = NULL;
38
+
39
+/* creates a new config context that is an interface to the
40
+ * cfg variables with write permission
41
+ */
42
+cfg_ctx_t *cfg_register_ctx(cfg_on_declare on_declare_cb)
43
+{
44
+	cfg_ctx_t	*ctx;
45
+	cfg_group_t	*group;
46
+	str		gname;
47
+
48
+	/* allocate memory for the new context
49
+	Better to use shm mem, because 'changed' and 'lock'
50
+	must be in shm mem anyway */
51
+	ctx = (cfg_ctx_t *)shm_malloc(sizeof(cfg_ctx_t));
52
+	if (!ctx) {
53
+		LOG(L_ERR, "ERROR: cfg_register_ctx(): not enough shm memory\n");
54
+		return NULL;
55
+	}
56
+	memset(ctx, 0, sizeof(cfg_ctx_t));
57
+	if (lock_init(&ctx->lock) == 0) {
58
+		LOG(L_ERR, "ERROR: cfg_register_ctx(): failed to init lock\n");
59
+		shm_free(ctx);
60
+		return NULL;
61
+	}
62
+
63
+	/* add the new ctx to the beginning of the list */
64
+	ctx->next = cfg_ctx_list;
65
+	cfg_ctx_list = ctx;
66
+
67
+	/* let the driver know about the already registered groups */
68
+	if (on_declare_cb) {
69
+		ctx->on_declare_cb = on_declare_cb;
70
+
71
+		for (	group = cfg_group;
72
+			group;
73
+			group = group->next
74
+		) {
75
+			gname.s = group->name;
76
+			gname.len = group->name_len;
77
+			on_declare_cb(&gname, group->mapping->def);
78
+		}
79
+	}
80
+
81
+	return ctx;
82
+}
83
+
84
+/* free the memory allocated for the contexts */
85
+void cfg_ctx_destroy(void)
86
+{
87
+	cfg_ctx_t	*ctx, *ctx2;
88
+
89
+	for (	ctx = cfg_ctx_list;
90
+		ctx;
91
+		ctx = ctx2
92
+	) {
93
+		ctx2 = ctx->next;
94
+		shm_free(ctx);
95
+	}
96
+	cfg_ctx_list = NULL;
97
+}
98
+
99
+/* notify the drivers about the new config definition */
100
+void cfg_notify_drivers(char *group_name, cfg_def_t *def)
101
+{
102
+	cfg_ctx_t	*ctx;
103
+	str		gname;
104
+
105
+	gname.s = group_name;
106
+	gname.len = strlen(group_name);
107
+
108
+	for (	ctx = cfg_ctx_list;
109
+		ctx;
110
+		ctx = ctx->next
111
+	)
112
+		if (ctx->on_declare_cb)
113
+			ctx->on_declare_cb(&gname, def);
114
+}
115
+
116
+/* convert the value to the requested type
117
+ * (only string->str is implemented currently) */
118
+static int convert_val(unsigned int val_type, void *val,
119
+			unsigned int var_type, void **new_val)
120
+{
121
+	static str	s;
122
+
123
+	switch (val_type) {
124
+		case CFG_VAR_INT:
125
+			if (CFG_INPUT_MASK(var_type) != CFG_INPUT_INT)
126
+				goto error;
127
+			*new_val = val;
128
+			break;
129
+
130
+		case CFG_VAR_STRING:
131
+			if (CFG_INPUT_MASK(var_type) == CFG_INPUT_STR) {
132
+				s.s = val;
133
+				s.len = strlen(s.s);
134
+				*new_val = (void *)&s;
135
+				break;
136
+			}
137
+			if (CFG_INPUT_MASK(var_type) != CFG_INPUT_STRING)
138
+				goto error;
139
+			*new_val = val;
140
+			break;
141
+		default:
142
+			goto error;
143
+	}
144
+
145
+	return 0;
146
+
147
+error:
148
+	LOG(L_ERR, "ERROR: convert_val(): got a value with type %u, but expected %u\n",
149
+			val_type, CFG_INPUT_MASK(var_type));
150
+	return -1;
151
+}
152
+
153
+/* sets the value of a variable without the need of commit */
154
+int cfg_set_now(cfg_ctx_t *ctx, str *group_name, str *var_name,
155
+			void *val, unsigned int val_type)
156
+{
157
+	cfg_group_t	*group;
158
+	cfg_mapping_t	*var;
159
+	void		*p, *v;
160
+	cfg_block_t	*block = NULL;
161
+	str		s;
162
+	char		*old_string = NULL;
163
+	char		**replaced = NULL;
164
+	cfg_child_cb_t	*child_cb = NULL;
165
+	int		i;
166
+
167
+	/* verify the context even if we do not need it now
168
+	to make sure that a cfg driver has called the function
169
+	(very very weak security) */
170
+	if (!ctx) {
171
+		LOG(L_ERR, "ERROR: cfg_set_now(): context is undefined\n");
172
+		return -1;
173
+	}
174
+
175
+	/* look-up the group and the variable */
176
+	if (cfg_lookup_var(group_name, var_name, &group, &var))
177
+		return -1;
178
+
179
+	/* check whether we have to convert the type */
180
+	if (convert_val(val_type, val, CFG_INPUT_TYPE(var), &v))
181
+		goto error0;
182
+	
183
+	if (var->def->on_change_cb) {
184
+		/* Call the fixup function.
185
+		There is no need to set a temporary cfg handle,
186
+		becaue a single variable is changed */
187
+		if (var->def->on_change_cb(*(group->handle),
188
+						var_name,
189
+						&v) < 0) {
190
+			LOG(L_ERR, "ERROR: cfg_set_now(): fixup failed\n");
191
+			goto error0;
192
+		}
193
+
194
+	} else if ((CFG_VAR_TYPE(var) == CFG_VAR_INT) 
195
+	&& (var->def->min != var->def->max)) {
196
+		/* perform a simple min-max check for integers */
197
+		if (((int)(long)v < var->def->min)
198
+		|| ((int)(long)v > var->def->max)) {
199
+			LOG(L_ERR, "ERROR: cfg_set_now(): integer value is out of range\n");
200
+			goto error0;
201
+		}
202
+	}
203
+
204
+	if (cfg_shmized) {
205
+		if (var->def->on_set_child_cb) {
206
+			child_cb = cfg_child_cb_new(var_name,
207
+						var->def->on_set_child_cb);
208
+			if (!child_cb) {
209
+				LOG(L_ERR, "ERROR: cfg_set_now(): not enough shm memory\n");
210
+				goto error0;
211
+			}
212
+		}
213
+
214
+		/* make sure that nobody else replaces the global config
215
+		while the new one is prepared */
216
+		CFG_WRITER_LOCK();
217
+
218
+		/* clone the memory block, and prepare the modification */
219
+		if (!(block = cfg_clone_global())) goto error;
220
+
221
+		p = block->vars+group->offset+var->offset;
222
+	} else {
223
+		/* we are allowed to rewrite the value on-the-fly */
224
+		p = group->vars + var->offset;
225
+	}
226
+
227
+	/* set the new value */
228
+	switch (CFG_VAR_TYPE(var)) {
229
+	case CFG_VAR_INT:
230
+		i = (int)(long)v;
231
+		memcpy(p, &i, sizeof(int));
232
+		break;
233
+
234
+	case CFG_VAR_STRING:
235
+		/* clone the string to shm mem */
236
+		s.s = v;
237
+		s.len = strlen(v);
238
+		if (!(s.s = cfg_clone_str(s))) goto error;
239
+		memcpy(&old_string, p, sizeof(char *));
240
+		memcpy(p, &s.s, sizeof(char *));
241
+		break;
242
+
243
+	case CFG_VAR_STR:
244
+		/* clone the string to shm mem */
245
+		s = *(str *)v;
246
+		if (!(s.s = cfg_clone_str(s))) goto error;
247
+		memcpy(&old_string, p, sizeof(char *));
248
+		memcpy(p, &s, sizeof(str));
249
+		break;
250
+
251
+	case CFG_VAR_POINTER:
252
+		memcpy(p, &v, sizeof(void *));
253
+		break;
254
+
255
+	}
256
+
257
+	if (cfg_shmized) {
258
+		if (old_string) {
259
+			/* prepare the array of the replaced strings,
260
+			they will be freed when the old block is freed */
261
+			replaced = (char **)shm_malloc(sizeof(char *)*2);
262
+			if (!replaced) {
263
+				LOG(L_ERR, "ERROR: cfg_set_now(): not enough shm memory\n");
264
+				goto error;
265
+			}
266
+			replaced[0] = old_string;
267
+			replaced[1] = NULL;
268
+		}
269
+		/* replace the global config with the new one */
270
+		cfg_install_global(block, replaced, child_cb, child_cb);
271
+		CFG_WRITER_UNLOCK();
272
+	} else {
273
+		/* flag the variable because there is no need
274
+		to shmize it again */
275
+		var->flag |= cfg_var_shmized;
276
+	}
277
+
278
+	if (val_type == CFG_VAR_INT)
279
+		LOG(L_INFO, "INFO: cfg_set_now(): %.*s.%.*s "
280
+			"has been changed to %d\n",
281
+			group_name->len, group_name->s,
282
+			var_name->len, var_name->s,
283
+			(int)(long)val);
284
+	else
285
+		LOG(L_INFO, "INFO: cfg_set_now(): %.*s.%.*s "
286
+			"has been changed to \"%s\"\n",
287
+			group_name->len, group_name->s,
288
+			var_name->len, var_name->s,
289
+			(char *)val);
290
+
291
+	return 0;
292
+
293
+error:
294
+	if (cfg_shmized) CFG_WRITER_UNLOCK();
295
+	if (block) cfg_block_free(block);
296
+	if (child_cb) cfg_child_cb_free(child_cb);
297
+
298
+error0:
299
+	LOG(L_ERR, "ERROR: cfg_set_now(): failed to set the variable: %.*s.%.*s\n",
300
+			group_name->len, group_name->s,
301
+			var_name->len, var_name->s);
302
+
303
+
304
+	return -1;
305
+}
306
+
307
+/* wrapper function for cfg_set_now */
308
+int cfg_set_now_int(cfg_ctx_t *ctx, str *group_name, str *var_name, int val)
309
+{
310
+	return cfg_set_now(ctx, group_name, var_name, (void *)(long)val, CFG_VAR_INT);
311
+}
312
+
313
+/* wrapper function for cfg_set_now */
314
+int cfg_set_now_string(cfg_ctx_t *ctx, str *group_name, str *var_name, char *val)
315
+{
316
+	return cfg_set_now(ctx, group_name, var_name, (void *)val, CFG_VAR_STRING);
317
+}
318
+
319
+/* returns the size of the variable */
320
+static int cfg_var_size(cfg_mapping_t *var)
321
+{
322
+	switch (CFG_VAR_TYPE(var)) {
323
+
324
+	case CFG_VAR_INT:
325
+		return sizeof(int);
326
+
327
+	case CFG_VAR_STRING:
328
+		return sizeof(char *);
329
+
330
+	case CFG_VAR_STR:
331
+		return sizeof(str);
332
+
333
+	case CFG_VAR_POINTER:
334
+		return sizeof(void *);
335
+
336
+	default:
337
+		LOG(L_CRIT, "BUG: cfg_var_sizeK(): unknown type: %u\n",
338
+			CFG_VAR_TYPE(var));
339
+		return 0;
340
+	}
341
+}
342
+
343
+/* sets the value of a variable but does not commit the change */
344
+int cfg_set_delayed(cfg_ctx_t *ctx, str *group_name, str *var_name,
345
+			void *val, unsigned int val_type)
346
+{
347
+	cfg_group_t	*group;
348
+	cfg_mapping_t	*var;
349
+	void		*v;
350
+	char		*temp_handle;
351
+	int		temp_handle_created;
352
+	cfg_changed_var_t	*changed = NULL;
353
+	int		i, size;
354
+	str		s;
355
+
356
+	if (!cfg_shmized)
357
+		/* the cfg has not been shmized yet, there is no
358
+		point in registering the change and committing it later */
359
+		return cfg_set_now(ctx, group_name, var_name,
360
+					val, val_type);
361
+
362
+	if (!ctx) {
363
+		LOG(L_ERR, "ERROR: cfg_set_delayed(): context is undefined\n");
364
+		return -1;
365
+	}
366
+
367
+	/* look-up the group and the variable */
368
+	if (cfg_lookup_var(group_name, var_name, &group, &var))
369
+		return -1;
370
+
371
+	/* check whether we have to convert the type */
372
+	if (convert_val(val_type, val, CFG_INPUT_TYPE(var), &v))
373
+		goto error0;
374
+
375
+	/* the ctx must be locked while reading and writing
376
+	the list of changed variables */
377
+	CFG_CTX_LOCK(ctx);
378
+
379
+	if (var->def->on_change_cb) {
380
+		/* The fixup function must see also the
381
+		not yet committed values, so a temporary handle
382
+		must be prepared that points to the new config.
383
+		Only the values within the group are applied,
384
+		other modifications are not visible to the callback.
385
+		The local config is the base. */
386
+
387
+		if (ctx->changed_first) {
388
+			temp_handle = (char *)pkg_malloc(group->size);
389
+			if (!temp_handle) {
390
+				LOG(L_ERR, "ERROR: cfg_set_delayed(): "
391
+					"not enough memory\n");
392
+				goto error;
393
+			}
394
+			temp_handle_created = 1;
395
+			memcpy(temp_handle, *(group->handle), group->size);
396
+
397
+			/* apply the changes */
398
+			for (	changed = ctx->changed_first;
399
+				changed;
400
+				changed = changed->next
401
+			) {
402
+				if (changed->group != group) continue;
403
+
404
+				memcpy(	temp_handle + changed->var->offset,
405
+					changed->new_val,
406
+					cfg_var_size(changed->var));
407
+			}
408
+		} else {
409
+			/* there is not any change */
410
+			temp_handle = *(group->handle);
411
+			temp_handle_created = 0;
412
+		}
413
+			
414
+		if (var->def->on_change_cb(temp_handle,
415
+						var_name,
416
+						&v) < 0) {
417
+			LOG(L_ERR, "ERROR: cfg_set_delayed(): fixup failed\n");
418
+			if (temp_handle_created) pkg_free(temp_handle);
419
+			goto error;
420
+		}
421
+		if (temp_handle_created) pkg_free(temp_handle);
422
+
423
+	} else if ((CFG_VAR_TYPE(var) == CFG_VAR_INT) 
424
+	&& (var->def->min != var->def->max)) {
425
+		/* perform a simple min-max check for integers */
426
+		if (((int)(long)v < var->def->min)
427
+		|| ((int)(long)v > var->def->max)) {
428
+			LOG(L_ERR, "ERROR: cfg_set_delayed(): integer value is out of range\n");
429
+			goto error;
430
+		}
431
+	}
432
+
433
+	/* everything went ok, we can add the new value to the list */
434
+	size = sizeof(cfg_changed_var_t) + cfg_var_size(var) - 1;
435
+	changed = (cfg_changed_var_t *)shm_malloc(size);
436
+	if (!changed) {
437
+		LOG(L_ERR, "ERROR: cfg_set_delayed(): not enough shm memory\n");
438
+		goto error;
439
+	}
440
+	memset(changed, 0, size);
441
+	changed->group = group;
442
+	changed->var = var;
443
+
444
+	switch (CFG_VAR_TYPE(var)) {
445
+
446
+	case CFG_VAR_INT:
447
+		i = (int)(long)v;
448
+		memcpy(changed->new_val, &i, sizeof(int));
449
+		break;
450
+
451
+	case CFG_VAR_STRING:
452
+		/* clone the string to shm mem */
453
+		s.s = v;
454
+		s.len = strlen(v);
455
+		if (!(s.s = cfg_clone_str(s))) goto error;
456
+		memcpy(changed->new_val, &s.s, sizeof(char *));
457
+		break;
458
+
459
+	case CFG_VAR_STR:
460
+		/* clone the string to shm mem */
461
+		s = *(str *)v;
462
+		if (!(s.s = cfg_clone_str(s))) goto error;
463
+		memcpy(changed->new_val, &s, sizeof(str));
464
+		break;
465
+
466
+	case CFG_VAR_POINTER:
467
+		memcpy(changed->new_val, &v, sizeof(void *));
468
+		break;
469
+
470
+	}
471
+
472
+	/* Add the new item to the end of the linked list,
473
+	The commit will go though the list from the first item,
474
+	so the list is kept in order */
475
+	if (ctx->changed_first)
476
+		ctx->changed_last->next = changed;
477
+	else
478
+		ctx->changed_first = changed;
479
+
480
+	ctx->changed_last = changed;
481
+
482
+	CFG_CTX_UNLOCK(ctx);
483
+
484
+	if (val_type == CFG_VAR_INT)
485
+		LOG(L_INFO, "INFO: cfg_set_delayed(): %.*s.%.*s "
486
+			"is going to be changed to %d "
487
+			"[context=%p]\n",
488
+			group_name->len, group_name->s,
489
+			var_name->len, var_name->s,
490
+			(int)(long)val,
491
+			ctx);
492
+	else
493
+		LOG(L_INFO, "INFO: cfg_set_delayed(): %.*s.%.*s "
494
+			"is going to be changed to \"%s\" "
495
+			"[context=%p]\n",
496
+			group_name->len, group_name->s,
497
+			var_name->len, var_name->s,
498
+			(char *)val,
499
+			ctx);
500
+
501
+	return 0;
502
+
503
+error:
504
+	CFG_CTX_UNLOCK(ctx);
505
+	if (changed) shm_free(changed);
506
+error0:
507
+	LOG(L_ERR, "ERROR: cfg_set_delayed(): failed to set the variable: %.*s.%.*s\n",
508
+			group_name->len, group_name->s,
509
+			var_name->len, var_name->s);
510
+
511
+	return -1;
512
+}
513
+
514
+/* wrapper function for cfg_set_delayed */
515
+int cfg_set_delayed_int(cfg_ctx_t *ctx, str *group_name, str *var_name, int val)
516
+{
517
+	return cfg_set_delayed(ctx, group_name, var_name, (void *)(long)val, CFG_VAR_INT);
518
+}
519
+
520
+/* wrapper function for cfg_set_delayed */
521
+int cfg_set_delayed_string(cfg_ctx_t *ctx, str *group_name, str *var_name, char *val)
522
+{
523
+	return cfg_set_delayed(ctx, group_name, var_name, (void *)val, CFG_VAR_STRING);
524
+}
525
+
526
+/* commits the previously prepared changes within the context */
527
+int cfg_commit(cfg_ctx_t *ctx)
528
+{
529
+	int	replaced_num = 0;
530
+	cfg_changed_var_t	*changed, *changed2;
531
+	cfg_block_t	*block;
532
+	char	**replaced = NULL;
533
+	cfg_child_cb_t	*child_cb;
534
+	cfg_child_cb_t	*child_cb_first = NULL;
535
+	cfg_child_cb_t	*child_cb_last = NULL;
536
+	int	size;
537
+	void	*p;
538
+	str	s;
539
+
540
+	if (!ctx) {
541
+		LOG(L_ERR, "ERROR: cfg_commit(): context is undefined\n");
542
+		return -1;
543
+	}
544
+
545
+	if (!cfg_shmized) return 0; /* nothing to do */
546
+
547
+	/* the ctx must be locked while reading and writing
548
+	the list of changed variables */
549
+	CFG_CTX_LOCK(ctx);
550
+
551
+	/* is there any change? */
552
+	if (!ctx->changed_first) goto done;
553
+
554
+	/* count the number of replaced strings,
555
+	and prepare the linked list of per-child process
556
+	callbacks, that will be added to the global list */
557
+	for (	changed = ctx->changed_first;
558
+		changed;
559
+		changed = changed->next
560
+	) {
561
+		if ((CFG_VAR_TYPE(changed->var) == CFG_VAR_STRING)
562
+		|| (CFG_VAR_TYPE(changed->var) == CFG_VAR_STR))
563
+			replaced_num++;
564
+
565
+
566
+		if (changed->var->def->on_set_child_cb) {
567
+			s.s = changed->var->def->name;
568
+			s.len = changed->var->name_len;
569
+			child_cb = cfg_child_cb_new(&s,
570
+					changed->var->def->on_set_child_cb);
571
+			if (!child_cb) goto error0;
572
+
573
+			if (child_cb_last)
574
+				child_cb_last->next = child_cb;
575
+			else
576
+				child_cb_first = child_cb;
577
+			child_cb_last = child_cb;
578
+		}
579
+	}
580
+
581
+	/* allocate memory for the replaced string array */
582
+	size = sizeof(char *)*(replaced_num + 1);
583
+	replaced = (char **)shm_malloc(size);
584
+	if (!replaced) {
585
+		LOG(L_ERR, "ERROR: cfg_commit(): not enough shm memory\n");
586
+		goto error;
587
+	}
588
+	memset(replaced, 0 , size);
589
+
590
+	/* make sure that nobody else replaces the global config
591
+	while the new one is prepared */
592
+	CFG_WRITER_LOCK();
593
+
594
+	/* clone the memory block, and prepare the modification */
595
+	if (!(block = cfg_clone_global())) {
596
+		CFG_WRITER_UNLOCK();
597
+		goto error;
598
+	}
599
+
600
+	/* apply the modifications to the buffer */
601
+	replaced_num = 0;
602
+	for (	changed = ctx->changed_first;
603
+		changed;
604
+		changed = changed->next
605
+	) {
606
+		p = block->vars
607
+			+ changed->group->offset
608
+			+ changed->var->offset;
609
+
610
+		if ((CFG_VAR_TYPE(changed->var) == CFG_VAR_STRING)
611
+		|| (CFG_VAR_TYPE(changed->var) == CFG_VAR_STR)) {
612
+			memcpy(&(replaced[replaced_num]), p, sizeof(char *));
613
+			replaced_num++;
614
+		}
615
+
616
+		memcpy(	p,
617
+			changed->new_val,
618
+			cfg_var_size(changed->var));
619
+	}
620
+
621
+	/* replace the global config with the new one */
622
+	cfg_install_global(block, replaced, child_cb_first, child_cb_last);
623
+	CFG_WRITER_UNLOCK();
624
+
625
+	/* free the changed list */	
626
+	for (	changed = ctx->changed_first;
627
+		changed;
628
+		changed = changed2
629
+	) {
630
+		changed2 = changed->next;
631
+		shm_free(changed);
632
+	}
633
+	ctx->changed_first = NULL;
634
+	ctx->changed_last = NULL;
635
+
636
+done:
637
+	LOG(L_INFO, "INFO: cfg_commit(): config changes have been applied "
638
+			"[context=%p]\n",
639
+			ctx);
640
+
641
+	CFG_CTX_UNLOCK(ctx);
642
+	return 0;
643
+
644
+error:
645
+	CFG_CTX_UNLOCK(ctx);
646
+
647
+error0:
648
+
649
+	if (child_cb_first) cfg_child_cb_free(child_cb_first);
650
+	if (replaced) shm_free(replaced);
651
+
652
+	return -1;
653
+}
654
+
655
+/* drops the not yet committed changes within the context */
656
+int cfg_rollback(cfg_ctx_t *ctx)
657
+{
658
+	cfg_changed_var_t	*changed, *changed2;
659
+	char	*new_string;
660
+
661
+	if (!ctx) {
662
+		LOG(L_ERR, "ERROR: cfg_rollback(): context is undefined\n");
663
+		return -1;
664
+	}
665
+
666
+	if (!cfg_shmized) return 0; /* nothing to do */
667
+
668
+	LOG(L_INFO, "INFO: cfg_rollback(): deleting the config changes "
669
+			"[context=%p]\n",
670
+			ctx);
671
+
672
+	/* the ctx must be locked while reading and writing
673
+	the list of changed variables */
674
+	CFG_CTX_LOCK(ctx);
675
+
676
+	for (	changed = ctx->changed_first;
677
+		changed;
678
+		changed = changed2
679
+	) {
680
+		changed2 = changed->next;
681
+
682
+		if ((CFG_VAR_TYPE(changed->var) == CFG_VAR_STRING)
683
+		|| (CFG_VAR_TYPE(changed->var) == CFG_VAR_STR)) {
684
+			memcpy(&new_string, changed->new_val, sizeof(char *));
685
+			shm_free(new_string);
686
+		}
687
+		shm_free(changed);
688
+	}
689
+	ctx->changed_first = NULL;
690
+	ctx->changed_last = NULL;
691
+
692
+	CFG_CTX_UNLOCK(ctx);
693
+
694
+	return 0;
695
+}
696
+
697
+/* returns the value of a variable */
698
+int cfg_get_by_name(cfg_ctx_t *ctx, str *group_name, str *var_name,
699
+			void **val, unsigned int *val_type)
700
+{
701
+	cfg_group_t	*group;
702
+	cfg_mapping_t	*var;
703
+	void		*p;
704
+	static str	s;	/* we need the value even
705
+				after the function returns */
706
+	int		i;
707
+	char		*ch;
708
+
709
+	/* verify the context even if we do not need it now
710
+	to make sure that a cfg driver has called the function
711
+	(very very weak security) */
712
+	if (!ctx) {
713
+		LOG(L_ERR, "ERROR: cfg_get_by_name(): context is undefined\n");
714
+		return -1;
715
+	}
716
+
717
+	/* look-up the group and the variable */
718
+	if (cfg_lookup_var(group_name, var_name, &group, &var))
719
+		return -1;
720
+
721
+	/* use the module's handle to access the variable
722
+	It means that the variable is read from the local config
723
+	after forking */
724
+	p = *(group->handle) + var->offset;
725
+
726
+	switch (CFG_VAR_TYPE(var)) {
727
+	case CFG_VAR_INT:
728
+		memcpy(&i, p, sizeof(int));
729
+		*val = (void *)(long)i;
730
+		break;
731
+
732
+	case CFG_VAR_STRING:
733
+		memcpy(&ch, p, sizeof(char *));
734
+		*val = (void *)ch;
735
+		break;
736
+
737
+	case CFG_VAR_STR:
738
+		memcpy(&s, p, sizeof(str));
739
+		*val = (void *)&s;
740
+		break;
741
+
742
+	case CFG_VAR_POINTER:
743
+		memcpy(val, &p, sizeof(void *));
744
+		break;
745
+
746
+	}
747
+	*val_type = CFG_VAR_TYPE(var);
748
+
749
+	return 0;
750
+}
751
+
752
+/* returns the description of a variable */
753
+int cfg_help(cfg_ctx_t *ctx, str *group_name, str *var_name,
754
+			char **ch)
755
+{
756
+	cfg_mapping_t	*var;
757
+
758
+	/* verify the context even if we do not need it now
759
+	to make sure that a cfg driver has called the function
760
+	(very very weak security) */
761
+	if (!ctx) {
762
+		LOG(L_ERR, "ERROR: cfg_help(): context is undefined\n");
763
+		return -1;
764
+	}
765
+
766
+	/* look-up the group and the variable */
767
+	if (cfg_lookup_var(group_name, var_name, NULL, &var))
768
+		return -1;
769
+
770
+	*ch = var->def->descr;
771
+	return 0;
772
+}
0 773
new file mode 100644
... ...
@@ -0,0 +1,110 @@
0
+/*
1
+ * $Id$
2
+ *
3
+ * Copyright (C) 2007 iptelorg GmbH
4
+ *
5
+ * This file is part of ser, a free SIP server.
6
+ *
7
+ * ser is free software; you can redistribute it and/or modify
8
+ * it under the terms of the GNU General Public License as published by
9
+ * the Free Software Foundation; either version 2 of the License, or
10
+ * (at your option) any later version
11
+ *
12
+ * For a license to use the ser software under conditions
13
+ * other than those described here, or to purchase support for this
14
+ * software, please contact iptel.org by e-mail at the following addresses:
15
+ *    info@iptel.org
16
+ *
17
+ * ser is distributed in the hope that it will be useful,
18
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
+ * GNU General Public License for more details.
21
+ *
22
+ * You should have received a copy of the GNU General Public License
23
+ * along with this program; if not, write to the Free Software
24
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
25
+ *
26
+ * History
27
+ * -------
28
+ *  2007-12-03	Initial version (Miklos)
29
+ */
30
+
31
+#ifndef _CFG_CTX_H
32
+#define _CFG_CTX_H
33
+
34
+#include "../str.h"
35
+#include "../locking.h"
36
+#include "cfg.h"
37
+#include "cfg_struct.h"
38
+
39
+/* linked list of variables with their new values */
40
+typedef struct _cfg_changed_var {
41
+	cfg_group_t	*group;
42
+	cfg_mapping_t	*var;
43
+	struct _cfg_changed_var	*next;
44
+
45
+	/* blob that contains the new value */
46
+	unsigned char	new_val[1];
47
+} cfg_changed_var_t;
48
+
49
+/* callback that is called when a new group is declared */
50
+typedef void (*cfg_on_declare)(str *, cfg_def_t *);
51
+
52
+/* linked list of registered contexts */
53
+typedef struct _cfg_ctx {
54
+	/* variables that are already changed
55
+	but have not been committed yet */
56
+	cfg_changed_var_t	*changed_first;
57
+	cfg_changed_var_t	*changed_last;
58
+	/* lock potecting the the linked-list of
59
+	changed variables */
60
+	gen_lock_t		lock;
61
+
62
+	/* callback that is called when a new
63
+	group is registered */
64
+	cfg_on_declare		on_declare_cb;
65
+
66
+	struct _cfg_ctx	*next;
67
+} cfg_ctx_t;
68
+
69
+#define CFG_CTX_LOCK(ctx)	lock_get(&(ctx)->lock)
70
+#define CFG_CTX_UNLOCK(ctx)	lock_release(&(ctx)->lock)
71
+
72
+/* creates a new config context that is an interface to the
73
+ * cfg variables with write permission */
74
+cfg_ctx_t *cfg_register_ctx(cfg_on_declare on_declare_cb);
75
+
76
+/* free the memory allocated for the contexts */
77
+void cfg_ctx_destroy(void);
78
+
79
+/* set the value of a variable without the need of explicit commit */
80
+int cfg_set_now(cfg_ctx_t *ctx, str *group_name, str *var_name,
81
+			void *val, unsigned int val_type);
82
+int cfg_set_now_int(cfg_ctx_t *ctx, str *group_name, str *var_name, int val);
83
+int cfg_set_now_string(cfg_ctx_t *ctx, str *group_name, str *var_name, char *val);
84
+
85
+/* sets the value of a variable but does not commit the change */
86
+int cfg_set_delayed(cfg_ctx_t *ctx, str *group_name, str *var_name,
87
+			void *val, unsigned int val_type);
88
+int cfg_set_delayed_int(cfg_ctx_t *ctx, str *group_name, str *var_name, int val);
89
+int cfg_set_delayed_string(cfg_ctx_t *ctx, str *group_name, str *var_name, char *val);
90
+
91
+/* commits the previously prepared changes within the context */
92
+int cfg_commit(cfg_ctx_t *ctx);
93
+
94
+/* drops the not yet committed changes within the context */
95
+int cfg_rollback(cfg_ctx_t *ctx);
96
+
97
+/* returns the value of a variable */
98
+int cfg_get_by_name(cfg_ctx_t *ctx, str *group_name, str *var_name,
99
+			void **val, unsigned int *val_type);
100
+
101
+/* returns the description of a variable */
102
+int cfg_help(cfg_ctx_t *ctx, str *group_name, str *var_name,
103
+			char **ch);
104
+
105
+/* notify the drivers about the new config definition */
106
+void cfg_notify_drivers(char *group_name, cfg_def_t *def);
107
+
108
+
109
+#endif /* _CFG_CTX_H */
0 110
new file mode 100644
... ...
@@ -0,0 +1,421 @@
0
+/*
1
+ * $Id$
2
+ *
3
+ * Copyright (C) 2007 iptelorg GmbH
4
+ *
5
+ * This file is part of ser, a free SIP server.
6
+ *
7
+ * ser is free software; you can redistribute it and/or modify
8
+ * it under the terms of the GNU General Public License as published by
9
+ * the Free Software Foundation; either version 2 of the License, or
10
+ * (at your option) any later version
11
+ *
12
+ * For a license to use the ser software under conditions
13
+ * other than those described here, or to purchase support for this
14
+ * software, please contact iptel.org by e-mail at the following addresses:
15
+ *    info@iptel.org
16
+ *
17
+ * ser is distributed in the hope that it will be useful,
18
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
+ * GNU General Public License for more details.
21
+ *
22
+ * You should have received a copy of the GNU General Public License
23
+ * along with this program; if not, write to the Free Software
24
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
25
+ *
26
+ * History
27
+ * -------
28
+ *  2007-12-03	Initial version (Miklos)
29
+ */
30
+
31
+#include <string.h>
32
+
33
+#include "../mem/mem.h"
34
+#include "../mem/shm_mem.h"
35
+#include "../ut.h"
36
+#include "../locking.h"
37
+#include "cfg_ctx.h"
38
+#include "cfg_struct.h"
39
+
40
+cfg_group_t	*cfg_group = NULL;	/* linked list of registered cfg groups */
41
+cfg_block_t	**cfg_global = NULL;	/* pointer to the active cfg block */
42
+cfg_block_t	*cfg_local = NULL;	/* per-process pointer to the active cfg block.
43
+					Updated only when the child process
44
+					finishes working on the SIP message */
45
+static int	cfg_block_size = 0;	/* size of the cfg block (constant) */
46
+gen_lock_t	*cfg_global_lock = 0;	/* protects *cfg_global */
47
+gen_lock_t	*cfg_writer_lock = 0;	/* This lock makes sure that two processes do not
48
+					try to clone *cfg_global at the same time.
49
+					Never try to get cfg_writer_lock when
50
+					cfg_global_lock is held */
51
+int		cfg_shmized = 0;	/* indicates whether the cfg block has been
52
+					already shmized */
53
+
54
+cfg_child_cb_t	**cfg_child_cb_first = NULL;	/* first item of the per-child process
55
+						callback list */
56
+cfg_child_cb_t	**cfg_child_cb_last = NULL;	/* last item of the above list */
57
+cfg_child_cb_t	*cfg_child_cb = NULL;	/* pointer to the previously executed cb */	
58
+
59
+/* creates a new cfg group, and adds it to the linked list */
60
+int cfg_new_group(char *name, int num, cfg_mapping_t *mapping,
61
+		char *vars, int size, void **handle)
62
+{
63
+	cfg_group_t	*group;
64
+	int		len;
65
+
66
+	if (cfg_shmized) {
67
+		LOG(L_ERR, "ERROR: cfg_new_group(): too late config declaration\n");
68
+		return -1;
69
+	}
70
+
71
+	len = strlen(name);
72
+	group = (cfg_group_t *)pkg_malloc(sizeof(cfg_group_t)+len-1);
73
+	if (!group) {
74
+		LOG(L_ERR, "ERROR: cfg_new_group(): not enough memory\n");
75
+		return -1;
76
+	}
77
+	memset(group, 0, sizeof(cfg_group_t)+len-1);
78
+
79
+	group->num = num;
80
+	group->mapping = mapping;
81
+	group->vars = vars;
82
+	group->size = size;
83
+	group->handle = handle;
84
+	group->name_len = len;
85
+	memcpy(&group->name, name, len);
86
+
87
+	/* add the new group to the beginning of the list */
88
+	group->next = cfg_group;
89
+	cfg_group = group;
90
+
91
+	return 0;
92
+}
93
+
94
+/* clones a string to shared memory */
95
+char *cfg_clone_str(str s)
96
+{
97
+	char	*c;
98
+
99
+	c = (char *)shm_malloc(sizeof(char)*(s.len+1));
100
+	if (!c) {
101
+		LOG(L_ERR, "ERROR: cfg_clone_str(): not enough shm memory\n");
102
+		return NULL;
103
+	}
104
+	memcpy(c, s.s, s.len);
105
+	c[s.len] = '\0';
106
+
107
+	return c;
108
+}
109
+
110
+/* copies the strings to shared memory */
111
+static int cfg_shmize_strings(cfg_group_t *group)
112
+{
113
+	cfg_mapping_t	*mapping;
114
+	int	i;
115
+	str	s;
116
+
117
+	/* We do not know in advance whether the variable will be changed or not,
118
+	and it can happen that we try to free the shm memory area when the variable
119
+	is changed, hence, it must be already in shm mem */
120
+	mapping = group->mapping;
121
+	for (i=0; i<group->num; i++) {
122
+		/* the cfg driver module may have already shmized the variable */
123
+		if (mapping[i].flag & cfg_var_shmized) continue;
124
+
125
+		if (CFG_VAR_TYPE(&mapping[i]) == CFG_VAR_STRING) {
126
+			memcpy(&s.s, group->vars + mapping[i].offset, sizeof(char *));
127
+			s.len = strlen(s.s);
128
+
129
+		} else if (CFG_VAR_TYPE(&mapping[i]) == CFG_VAR_STR) {
130
+			memcpy(&s, group->vars + mapping[i].offset, sizeof(str));
131
+
132
+		} else {
133
+			continue;
134
+		}
135
+		if (!(s.s = cfg_clone_str(s))) return -1;
136
+		memcpy(group->vars + mapping[i].offset, &s.s, sizeof(char *));
137
+		mapping[i].flag |= cfg_var_shmized;
138
+	}
139
+
140
+	return 0;
141
+}
142
+
143
+/* copy the variables to shm mem */
144
+int cfg_shmize(void)
145
+{
146
+	cfg_group_t	*group;
147
+	cfg_block_t	*block = NULL;
148
+	int	size;
149
+
150
+	if (!cfg_group) return 0;
151
+
152
+	/* Let us allocate one memory block that
153
+	will contain all the variables */
154
+	for (	size=0, group = cfg_group;
155
+		group;
156
+		group=group->next
157
+	) {
158
+		size = ROUND_POINTER(size);
159
+		group->offset = size;
160
+		size += group->size;
161
+	}
162
+
163
+	block = (cfg_block_t*)shm_malloc(sizeof(cfg_block_t)+size-1);
164
+	if (!block) {
165
+		LOG(L_ERR, "ERROR: cfg_clone_str(): not enough shm memory\n");
166
+		goto error;
167
+	}
168
+	memset(block, 0, sizeof(cfg_block_t)+size-1);
169
+	cfg_block_size = size;
170
+
171
+	/* copy the memory fragments to the single block */
172
+	for (	group = cfg_group;
173
+		group;
174
+		group=group->next
175
+	) {
176
+		/* clone the strings to shm mem */
177
+		if (cfg_shmize_strings(group)) goto error;
178
+
179
+		/* copy the values to the new block,
180
+		and update the module's handle */
181
+		memcpy(block->vars+group->offset, group->vars, group->size);
182
+		*(group->handle) = block->vars+group->offset;
183
+	}
184
+
185
+	/* install the new config */
186
+	cfg_install_global(block, NULL, NULL, NULL);
187
+	cfg_shmized = 1;
188
+
189
+	return 0;
190
+
191
+error:
192
+	if (block) shm_free(block);
193
+	return -1;
194
+}
195
+
196
+/* initiate the cfg framework */
197
+int cfg_init(void)
198
+{
199
+	cfg_global_lock = lock_alloc();
200
+	if (!cfg_global_lock) {
201
+		LOG(L_ERR, "ERROR: cfg_init(): not enough shm memory\n");
202
+		goto error;
203
+	}
204
+	if (lock_init(cfg_global_lock) == 0) {
205
+		LOG(L_ERR, "ERROR: cfg_init(): failed to init lock\n");
206
+		lock_dealloc(cfg_global_lock);
207
+		cfg_global_lock = 0;
208
+		goto error;
209
+	}
210
+
211
+	cfg_writer_lock = lock_alloc();
212
+	if (!cfg_writer_lock) {
213
+		LOG(L_ERR, "ERROR: cfg_init(): not enough shm memory\n");
214
+		goto error;
215
+	}
216
+	if (lock_init(cfg_writer_lock) == 0) {
217
+		LOG(L_ERR, "ERROR: cfg_init(): failed to init lock\n");
218
+		lock_dealloc(cfg_writer_lock);
219
+		cfg_writer_lock = 0;
220
+		goto error;
221
+	}
222
+
223
+	cfg_global = (cfg_block_t **)shm_malloc(sizeof(cfg_block_t *));
224
+	if (!cfg_global) {
225
+		LOG(L_ERR, "ERROR: cfg_init(): not enough shm memory\n");
226
+		goto error;
227
+	}
228
+	*cfg_global = NULL;
229
+
230
+	cfg_child_cb_first = (cfg_child_cb_t **)shm_malloc(sizeof(cfg_child_cb_t *));
231
+	if (!cfg_child_cb_first) {
232
+		LOG(L_ERR, "ERROR: cfg_init(): not enough shm memory\n");
233
+		goto error;
234
+	}
235
+	*cfg_child_cb_first = NULL;
236
+
237
+	cfg_child_cb_last = (cfg_child_cb_t **)shm_malloc(sizeof(cfg_child_cb_t *));
238
+	if (!cfg_child_cb_last) {
239
+		LOG(L_ERR, "ERROR: cfg_init(): not enough shm memory\n");
240
+		goto error;
241
+	}
242
+	*cfg_child_cb_last = NULL;
243
+
244
+	/* A new cfg_child_cb struct must be created with a NULL callback function.
245
+	This stucture will be the entry point for the child processes, and
246
+	will be freed later, when none of the processes refers to it */
247
+	*cfg_child_cb_first = *cfg_child_cb_last =
248
+		cfg_child_cb_new(NULL, NULL);
249
+
250
+	if (!*cfg_child_cb_first) goto error;
251
+
252
+	return 0;
253
+
254
+error:
255
+	cfg_destroy();
256
+
257
+	return -1;
258
+}
259
+
260
+/* destroy the memory allocated for the cfg framework */
261
+void cfg_destroy(void)
262
+{
263
+	/* free the contexts */
264
+	cfg_ctx_destroy();
265
+
266
+	if (cfg_child_cb_first) {
267
+		if (*cfg_child_cb_first) cfg_child_cb_free(*cfg_child_cb_first);
268
+		shm_free(cfg_child_cb_first);
269
+		cfg_child_cb_first = NULL;
270
+	}
271
+
272
+	if (cfg_child_cb_last) {
273
+		shm_free(cfg_child_cb_last);
274
+		cfg_child_cb_last = NULL;
275
+	}
276
+
277
+	if (cfg_global) {
278
+		if (*cfg_global) cfg_block_free(*cfg_global);
279
+		shm_free(cfg_global);
280
+		cfg_global = NULL;
281
+	}
282
+	if (cfg_global_lock) {
283
+		lock_destroy(cfg_global_lock);
284
+		lock_dealloc(cfg_global_lock);
285
+		cfg_global_lock = 0;
286
+	}
287
+	if (cfg_writer_lock) {
288
+		lock_destroy(cfg_writer_lock);
289
+		lock_dealloc(cfg_writer_lock);
290
+		cfg_writer_lock = 0;
291
+	}
292
+}
293
+
294
+/* per-child process init function */
295
+int cfg_child_init(void)
296
+{
297
+	/* set the callback list pointer to the beginning of the list */