- configuration variables can be declared in the script:
[sip-router] / cfg / cfg_ctx.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 "cfg_struct.h"
35 #include "cfg_ctx.h"
36
37 /* linked list of all the registered cfg contexts */
38 static cfg_ctx_t        *cfg_ctx_list = NULL;
39
40 /* creates a new config context that is an interface to the
41  * cfg variables with write permission
42  */
43 cfg_ctx_t *cfg_register_ctx(cfg_on_declare on_declare_cb)
44 {
45         cfg_ctx_t       *ctx;
46         cfg_group_t     *group;
47         str             gname;
48
49         /* allocate memory for the new context
50         Better to use shm mem, because 'changed' and 'lock'
51         must be in shm mem anyway */
52         ctx = (cfg_ctx_t *)shm_malloc(sizeof(cfg_ctx_t));
53         if (!ctx) {
54                 LOG(L_ERR, "ERROR: cfg_register_ctx(): not enough shm memory\n");
55                 return NULL;
56         }
57         memset(ctx, 0, sizeof(cfg_ctx_t));
58         if (lock_init(&ctx->lock) == 0) {
59                 LOG(L_ERR, "ERROR: cfg_register_ctx(): failed to init lock\n");
60                 shm_free(ctx);
61                 return NULL;
62         }
63
64         /* add the new ctx to the beginning of the list */
65         ctx->next = cfg_ctx_list;
66         cfg_ctx_list = ctx;
67
68         /* let the driver know about the already registered groups */
69         if (on_declare_cb) {
70                 ctx->on_declare_cb = on_declare_cb;
71
72                 for (   group = cfg_group;
73                         group;
74                         group = group->next
75                 ) {
76                         /* dynamic groups are not ready, the callback
77                         will be called later when the group is fixed-up */
78                         if (group->dynamic) continue;
79
80                         gname.s = group->name;
81                         gname.len = group->name_len;
82                         on_declare_cb(&gname, group->mapping->def);
83                 }
84         }
85
86         return ctx;
87 }
88
89 /* free the memory allocated for the contexts */
90 void cfg_ctx_destroy(void)
91 {
92         cfg_ctx_t       *ctx, *ctx2;
93
94         for (   ctx = cfg_ctx_list;
95                 ctx;
96                 ctx = ctx2
97         ) {
98                 ctx2 = ctx->next;
99                 shm_free(ctx);
100         }
101         cfg_ctx_list = NULL;
102 }
103
104 /* notify the drivers about the new config definition */
105 void cfg_notify_drivers(char *group_name, int group_name_len, cfg_def_t *def)
106 {
107         cfg_ctx_t       *ctx;
108         str             gname;
109
110         gname.s = group_name;
111         gname.len = group_name_len;
112
113         for (   ctx = cfg_ctx_list;
114                 ctx;
115                 ctx = ctx->next
116         )
117                 if (ctx->on_declare_cb)
118                         ctx->on_declare_cb(&gname, def);
119 }
120
121 /* convert the value to the requested type
122  * (only string->str is implemented currently) */
123 static int convert_val(unsigned int val_type, void *val,
124                         unsigned int var_type, void **new_val)
125 {
126         static str      s;
127
128         switch (val_type) {
129                 case CFG_VAR_INT:
130                         if (CFG_INPUT_MASK(var_type) != CFG_INPUT_INT)
131                                 goto error;
132                         *new_val = val;
133                         break;
134
135                 case CFG_VAR_STRING:
136                         if (CFG_INPUT_MASK(var_type) == CFG_INPUT_STR) {
137                                 s.s = val;
138                                 s.len = strlen(s.s);
139                                 *new_val = (void *)&s;
140                                 break;
141                         }
142                         if (CFG_INPUT_MASK(var_type) != CFG_INPUT_STRING)
143                                 goto error;
144                         *new_val = val;
145                         break;
146                 default:
147                         goto error;
148         }
149
150         return 0;
151
152 error:
153         LOG(L_ERR, "ERROR: convert_val(): got a value with type %u, but expected %u\n",
154                         val_type, CFG_INPUT_MASK(var_type));
155         return -1;
156 }
157
158 /* sets the value of a variable without the need of commit
159  *
160  * return value:
161  *   0: success
162  *  -1: error
163  *   1: variable has not been found
164  */
165 int cfg_set_now(cfg_ctx_t *ctx, str *group_name, str *var_name,
166                         void *val, unsigned int val_type)
167 {
168         cfg_group_t     *group;
169         cfg_mapping_t   *var;
170         void            *p, *v;
171         cfg_block_t     *block = NULL;
172         str             s;
173         char            *old_string = NULL;
174         char            **replaced = NULL;
175         cfg_child_cb_t  *child_cb = NULL;
176         int             i;
177
178         /* verify the context even if we do not need it now
179         to make sure that a cfg driver has called the function
180         (very very weak security) */
181         if (!ctx) {
182                 LOG(L_ERR, "ERROR: cfg_set_now(): context is undefined\n");
183                 return -1;
184         }
185
186         /* look-up the group and the variable */
187         if (cfg_lookup_var(group_name, var_name, &group, &var))
188                 return 1;
189
190         /* check whether we have to convert the type */
191         if (convert_val(val_type, val, CFG_INPUT_TYPE(var), &v))
192                 goto error0;
193         
194         if (var->def->on_change_cb) {
195                 /* Call the fixup function.
196                 There is no need to set a temporary cfg handle,
197                 becaue a single variable is changed */
198                 if (var->def->on_change_cb(*(group->handle),
199                                                 var_name,
200                                                 &v) < 0) {
201                         LOG(L_ERR, "ERROR: cfg_set_now(): fixup failed\n");
202                         goto error0;
203                 }
204
205         } else if ((CFG_VAR_TYPE(var) == CFG_VAR_INT) 
206         && (var->def->min != var->def->max)) {
207                 /* perform a simple min-max check for integers */
208                 if (((int)(long)v < var->def->min)
209                 || ((int)(long)v > var->def->max)) {
210                         LOG(L_ERR, "ERROR: cfg_set_now(): integer value is out of range\n");
211                         goto error0;
212                 }
213         }
214
215         if (cfg_shmized) {
216                 if (var->def->on_set_child_cb) {
217                         child_cb = cfg_child_cb_new(var_name,
218                                                 var->def->on_set_child_cb);
219                         if (!child_cb) {
220                                 LOG(L_ERR, "ERROR: cfg_set_now(): not enough shm memory\n");
221                                 goto error0;
222                         }
223                 }
224
225                 /* make sure that nobody else replaces the global config
226                 while the new one is prepared */
227                 CFG_WRITER_LOCK();
228
229                 /* clone the memory block, and prepare the modification */
230                 if (!(block = cfg_clone_global())) goto error;
231
232                 p = block->vars+group->offset+var->offset;
233         } else {
234                 /* we are allowed to rewrite the value on-the-fly
235                 The handle either points to group->vars, or to the
236                 shared memory block (dynamic group) */
237                 p = *(group->handle) + var->offset;
238         }
239
240         /* set the new value */
241         switch (CFG_VAR_TYPE(var)) {
242         case CFG_VAR_INT:
243                 i = (int)(long)v;
244                 memcpy(p, &i, sizeof(int));
245                 break;
246
247         case CFG_VAR_STRING:
248                 /* clone the string to shm mem */
249                 s.s = v;
250                 s.len = strlen(v);
251                 if (!(s.s = cfg_clone_str(s))) goto error;
252                 memcpy(&old_string, p, sizeof(char *));
253                 memcpy(p, &s.s, sizeof(char *));
254                 break;
255
256         case CFG_VAR_STR:
257                 /* clone the string to shm mem */
258                 s = *(str *)v;
259                 if (!(s.s = cfg_clone_str(s))) goto error;
260                 memcpy(&old_string, p, sizeof(char *));
261                 memcpy(p, &s, sizeof(str));
262                 break;
263
264         case CFG_VAR_POINTER:
265                 memcpy(p, &v, sizeof(void *));
266                 break;
267
268         }
269
270         if (cfg_shmized) {
271                 if (old_string) {
272                         /* prepare the array of the replaced strings,
273                         they will be freed when the old block is freed */
274                         replaced = (char **)shm_malloc(sizeof(char *)*2);
275                         if (!replaced) {
276                                 LOG(L_ERR, "ERROR: cfg_set_now(): not enough shm memory\n");
277                                 goto error;
278                         }
279                         replaced[0] = old_string;
280                         replaced[1] = NULL;
281                 }
282                 /* replace the global config with the new one */
283                 cfg_install_global(block, replaced, child_cb, child_cb);
284                 CFG_WRITER_UNLOCK();
285         } else {
286                 /* cfg_set() may be called more than once before forking */
287                 if (old_string && (var->flag & cfg_var_shmized))
288                         shm_free(old_string);
289
290                 /* flag the variable because there is no need
291                 to shmize it again */
292                 var->flag |= cfg_var_shmized;
293         }
294
295         if (val_type == CFG_VAR_INT)
296                 LOG(L_INFO, "INFO: cfg_set_now(): %.*s.%.*s "
297                         "has been changed to %d\n",
298                         group_name->len, group_name->s,
299                         var_name->len, var_name->s,
300                         (int)(long)val);
301         else
302                 LOG(L_INFO, "INFO: cfg_set_now(): %.*s.%.*s "
303                         "has been changed to \"%s\"\n",
304                         group_name->len, group_name->s,
305                         var_name->len, var_name->s,
306                         (char *)val);
307
308         return 0;
309
310 error:
311         if (cfg_shmized) CFG_WRITER_UNLOCK();
312         if (block) cfg_block_free(block);
313         if (child_cb) cfg_child_cb_free(child_cb);
314
315 error0:
316         LOG(L_ERR, "ERROR: cfg_set_now(): failed to set the variable: %.*s.%.*s\n",
317                         group_name->len, group_name->s,
318                         var_name->len, var_name->s);
319
320
321         return -1;
322 }
323
324 /* wrapper function for cfg_set_now */
325 int cfg_set_now_int(cfg_ctx_t *ctx, str *group_name, str *var_name, int val)
326 {
327         return cfg_set_now(ctx, group_name, var_name, (void *)(long)val, CFG_VAR_INT);
328 }
329
330 /* wrapper function for cfg_set_now */
331 int cfg_set_now_string(cfg_ctx_t *ctx, str *group_name, str *var_name, char *val)
332 {
333         return cfg_set_now(ctx, group_name, var_name, (void *)val, CFG_VAR_STRING);
334 }
335
336 /* returns the size of the variable */
337 static int cfg_var_size(cfg_mapping_t *var)
338 {
339         switch (CFG_VAR_TYPE(var)) {
340
341         case CFG_VAR_INT:
342                 return sizeof(int);
343
344         case CFG_VAR_STRING:
345                 return sizeof(char *);
346
347         case CFG_VAR_STR:
348                 return sizeof(str);
349
350         case CFG_VAR_POINTER:
351                 return sizeof(void *);
352
353         default:
354                 LOG(L_CRIT, "BUG: cfg_var_sizeK(): unknown type: %u\n",
355                         CFG_VAR_TYPE(var));
356                 return 0;
357         }
358 }
359
360 /* sets the value of a variable but does not commit the change
361  *
362  * return value:
363  *   0: success
364  *  -1: error
365  *   1: variable has not been found
366  */
367 int cfg_set_delayed(cfg_ctx_t *ctx, str *group_name, str *var_name,
368                         void *val, unsigned int val_type)
369 {
370         cfg_group_t     *group;
371         cfg_mapping_t   *var;
372         void            *v;
373         char            *temp_handle;
374         int             temp_handle_created;
375         cfg_changed_var_t       *changed = NULL;
376         int             i, size;
377         str             s;
378
379         if (!cfg_shmized)
380                 /* the cfg has not been shmized yet, there is no
381                 point in registering the change and committing it later */
382                 return cfg_set_now(ctx, group_name, var_name,
383                                         val, val_type);
384
385         if (!ctx) {
386                 LOG(L_ERR, "ERROR: cfg_set_delayed(): context is undefined\n");
387                 return -1;
388         }
389
390         /* look-up the group and the variable */
391         if (cfg_lookup_var(group_name, var_name, &group, &var))
392                 return 1;
393
394         /* check whether we have to convert the type */
395         if (convert_val(val_type, val, CFG_INPUT_TYPE(var), &v))
396                 goto error0;
397
398         /* the ctx must be locked while reading and writing
399         the list of changed variables */
400         CFG_CTX_LOCK(ctx);
401
402         if (var->def->on_change_cb) {
403                 /* The fixup function must see also the
404                 not yet committed values, so a temporary handle
405                 must be prepared that points to the new config.
406                 Only the values within the group are applied,
407                 other modifications are not visible to the callback.
408                 The local config is the base. */
409
410                 if (ctx->changed_first) {
411                         temp_handle = (char *)pkg_malloc(group->size);
412                         if (!temp_handle) {
413                                 LOG(L_ERR, "ERROR: cfg_set_delayed(): "
414                                         "not enough memory\n");
415                                 goto error;
416                         }
417                         temp_handle_created = 1;
418                         memcpy(temp_handle, *(group->handle), group->size);
419
420                         /* apply the changes */
421                         for (   changed = ctx->changed_first;
422                                 changed;
423                                 changed = changed->next
424                         ) {
425                                 if (changed->group != group) continue;
426
427                                 memcpy( temp_handle + changed->var->offset,
428                                         changed->new_val,
429                                         cfg_var_size(changed->var));
430                         }
431                 } else {
432                         /* there is not any change */
433                         temp_handle = *(group->handle);
434                         temp_handle_created = 0;
435                 }
436                         
437                 if (var->def->on_change_cb(temp_handle,
438                                                 var_name,
439                                                 &v) < 0) {
440                         LOG(L_ERR, "ERROR: cfg_set_delayed(): fixup failed\n");
441                         if (temp_handle_created) pkg_free(temp_handle);
442                         goto error;
443                 }
444                 if (temp_handle_created) pkg_free(temp_handle);
445
446         } else if ((CFG_VAR_TYPE(var) == CFG_VAR_INT) 
447         && (var->def->min != var->def->max)) {
448                 /* perform a simple min-max check for integers */
449                 if (((int)(long)v < var->def->min)
450                 || ((int)(long)v > var->def->max)) {
451                         LOG(L_ERR, "ERROR: cfg_set_delayed(): integer value is out of range\n");
452                         goto error;
453                 }
454         }
455
456         /* everything went ok, we can add the new value to the list */
457         size = sizeof(cfg_changed_var_t) + cfg_var_size(var) - 1;
458         changed = (cfg_changed_var_t *)shm_malloc(size);
459         if (!changed) {
460                 LOG(L_ERR, "ERROR: cfg_set_delayed(): not enough shm memory\n");
461                 goto error;
462         }
463         memset(changed, 0, size);
464         changed->group = group;
465         changed->var = var;
466
467         switch (CFG_VAR_TYPE(var)) {
468
469         case CFG_VAR_INT:
470                 i = (int)(long)v;
471                 memcpy(changed->new_val, &i, sizeof(int));
472                 break;
473
474         case CFG_VAR_STRING:
475                 /* clone the string to shm mem */
476                 s.s = v;
477                 s.len = strlen(v);
478                 if (!(s.s = cfg_clone_str(s))) goto error;
479                 memcpy(changed->new_val, &s.s, sizeof(char *));
480                 break;
481
482         case CFG_VAR_STR:
483                 /* clone the string to shm mem */
484                 s = *(str *)v;
485                 if (!(s.s = cfg_clone_str(s))) goto error;
486                 memcpy(changed->new_val, &s, sizeof(str));
487                 break;
488
489         case CFG_VAR_POINTER:
490                 memcpy(changed->new_val, &v, sizeof(void *));
491                 break;
492
493         }
494
495         /* Add the new item to the end of the linked list,
496         The commit will go though the list from the first item,
497         so the list is kept in order */
498         if (ctx->changed_first)
499                 ctx->changed_last->next = changed;
500         else
501                 ctx->changed_first = changed;
502
503         ctx->changed_last = changed;
504
505         CFG_CTX_UNLOCK(ctx);
506
507         if (val_type == CFG_VAR_INT)
508                 LOG(L_INFO, "INFO: cfg_set_delayed(): %.*s.%.*s "
509                         "is going to be changed to %d "
510                         "[context=%p]\n",
511                         group_name->len, group_name->s,
512                         var_name->len, var_name->s,
513                         (int)(long)val,
514                         ctx);
515         else
516                 LOG(L_INFO, "INFO: cfg_set_delayed(): %.*s.%.*s "
517                         "is going to be changed to \"%s\" "
518                         "[context=%p]\n",
519                         group_name->len, group_name->s,
520                         var_name->len, var_name->s,
521                         (char *)val,
522                         ctx);
523
524         return 0;
525
526 error:
527         CFG_CTX_UNLOCK(ctx);
528         if (changed) shm_free(changed);
529 error0:
530         LOG(L_ERR, "ERROR: cfg_set_delayed(): failed to set the variable: %.*s.%.*s\n",
531                         group_name->len, group_name->s,
532                         var_name->len, var_name->s);
533
534         return -1;
535 }
536
537 /* wrapper function for cfg_set_delayed */
538 int cfg_set_delayed_int(cfg_ctx_t *ctx, str *group_name, str *var_name, int val)
539 {
540         return cfg_set_delayed(ctx, group_name, var_name, (void *)(long)val, CFG_VAR_INT);
541 }
542
543 /* wrapper function for cfg_set_delayed */
544 int cfg_set_delayed_string(cfg_ctx_t *ctx, str *group_name, str *var_name, char *val)
545 {
546         return cfg_set_delayed(ctx, group_name, var_name, (void *)val, CFG_VAR_STRING);
547 }
548
549 /* commits the previously prepared changes within the context */
550 int cfg_commit(cfg_ctx_t *ctx)
551 {
552         int     replaced_num = 0;
553         cfg_changed_var_t       *changed, *changed2;
554         cfg_block_t     *block;
555         char    **replaced = NULL;
556         cfg_child_cb_t  *child_cb;
557         cfg_child_cb_t  *child_cb_first = NULL;
558         cfg_child_cb_t  *child_cb_last = NULL;
559         int     size;
560         void    *p;
561         str     s;
562
563         if (!ctx) {
564                 LOG(L_ERR, "ERROR: cfg_commit(): context is undefined\n");
565                 return -1;
566         }
567
568         if (!cfg_shmized) return 0; /* nothing to do */
569
570         /* the ctx must be locked while reading and writing
571         the list of changed variables */
572         CFG_CTX_LOCK(ctx);
573
574         /* is there any change? */
575         if (!ctx->changed_first) goto done;
576
577         /* count the number of replaced strings,
578         and prepare the linked list of per-child process
579         callbacks, that will be added to the global list */
580         for (   changed = ctx->changed_first;
581                 changed;
582                 changed = changed->next
583         ) {
584                 if ((CFG_VAR_TYPE(changed->var) == CFG_VAR_STRING)
585                 || (CFG_VAR_TYPE(changed->var) == CFG_VAR_STR))
586                         replaced_num++;
587
588
589                 if (changed->var->def->on_set_child_cb) {
590                         s.s = changed->var->def->name;
591                         s.len = changed->var->name_len;
592                         child_cb = cfg_child_cb_new(&s,
593                                         changed->var->def->on_set_child_cb);
594                         if (!child_cb) goto error0;
595
596                         if (child_cb_last)
597                                 child_cb_last->next = child_cb;
598                         else
599                                 child_cb_first = child_cb;
600                         child_cb_last = child_cb;
601                 }
602         }
603
604         /* allocate memory for the replaced string array */
605         size = sizeof(char *)*(replaced_num + 1);
606         replaced = (char **)shm_malloc(size);
607         if (!replaced) {
608                 LOG(L_ERR, "ERROR: cfg_commit(): not enough shm memory\n");
609                 goto error;
610         }
611         memset(replaced, 0 , size);
612
613         /* make sure that nobody else replaces the global config
614         while the new one is prepared */
615         CFG_WRITER_LOCK();
616
617         /* clone the memory block, and prepare the modification */
618         if (!(block = cfg_clone_global())) {
619                 CFG_WRITER_UNLOCK();
620                 goto error;
621         }
622
623         /* apply the modifications to the buffer */
624         replaced_num = 0;
625         for (   changed = ctx->changed_first;
626                 changed;
627                 changed = changed->next
628         ) {
629                 p = block->vars
630                         + changed->group->offset
631                         + changed->var->offset;
632
633                 if ((CFG_VAR_TYPE(changed->var) == CFG_VAR_STRING)
634                 || (CFG_VAR_TYPE(changed->var) == CFG_VAR_STR)) {
635                         memcpy(&(replaced[replaced_num]), p, sizeof(char *));
636                         replaced_num++;
637                 }
638
639                 memcpy( p,
640                         changed->new_val,
641                         cfg_var_size(changed->var));
642         }
643
644         /* replace the global config with the new one */
645         cfg_install_global(block, replaced, child_cb_first, child_cb_last);
646         CFG_WRITER_UNLOCK();
647
648         /* free the changed list */     
649         for (   changed = ctx->changed_first;
650                 changed;
651                 changed = changed2
652         ) {
653                 changed2 = changed->next;
654                 shm_free(changed);
655         }
656         ctx->changed_first = NULL;
657         ctx->changed_last = NULL;
658
659 done:
660         LOG(L_INFO, "INFO: cfg_commit(): config changes have been applied "
661                         "[context=%p]\n",
662                         ctx);
663
664         CFG_CTX_UNLOCK(ctx);
665         return 0;
666
667 error:
668         CFG_CTX_UNLOCK(ctx);
669
670 error0:
671
672         if (child_cb_first) cfg_child_cb_free(child_cb_first);
673         if (replaced) shm_free(replaced);
674
675         return -1;
676 }
677
678 /* drops the not yet committed changes within the context */
679 int cfg_rollback(cfg_ctx_t *ctx)
680 {
681         cfg_changed_var_t       *changed, *changed2;
682         char    *new_string;
683
684         if (!ctx) {
685                 LOG(L_ERR, "ERROR: cfg_rollback(): context is undefined\n");
686                 return -1;
687         }
688
689         if (!cfg_shmized) return 0; /* nothing to do */
690
691         LOG(L_INFO, "INFO: cfg_rollback(): deleting the config changes "
692                         "[context=%p]\n",
693                         ctx);
694
695         /* the ctx must be locked while reading and writing
696         the list of changed variables */
697         CFG_CTX_LOCK(ctx);
698
699         for (   changed = ctx->changed_first;
700                 changed;
701                 changed = changed2
702         ) {
703                 changed2 = changed->next;
704
705                 if ((CFG_VAR_TYPE(changed->var) == CFG_VAR_STRING)
706                 || (CFG_VAR_TYPE(changed->var) == CFG_VAR_STR)) {
707                         memcpy(&new_string, changed->new_val, sizeof(char *));
708                         shm_free(new_string);
709                 }
710                 shm_free(changed);
711         }
712         ctx->changed_first = NULL;
713         ctx->changed_last = NULL;
714
715         CFG_CTX_UNLOCK(ctx);
716
717         return 0;
718 }
719
720 /* returns the value of a variable */
721 int cfg_get_by_name(cfg_ctx_t *ctx, str *group_name, str *var_name,
722                         void **val, unsigned int *val_type)
723 {
724         cfg_group_t     *group;
725         cfg_mapping_t   *var;
726         void            *p;
727         static str      s;      /* we need the value even
728                                 after the function returns */
729         int             i;
730         char            *ch;
731
732         /* verify the context even if we do not need it now
733         to make sure that a cfg driver has called the function
734         (very very weak security) */
735         if (!ctx) {
736                 LOG(L_ERR, "ERROR: cfg_get_by_name(): context is undefined\n");
737                 return -1;
738         }
739
740         /* look-up the group and the variable */
741         if (cfg_lookup_var(group_name, var_name, &group, &var))
742                 return -1;
743
744         if (var->def->on_change_cb) {
745                 /* The variable cannot be retrieved, because the fixup
746                 function may have changed it, and it is better to return
747                 an error than an incorrect value */
748                 return -1;
749         }
750
751         /* use the module's handle to access the variable
752         It means that the variable is read from the local config
753         after forking */
754         p = *(group->handle) + var->offset;
755
756         switch (CFG_VAR_TYPE(var)) {
757         case CFG_VAR_INT:
758                 memcpy(&i, p, sizeof(int));
759                 *val = (void *)(long)i;
760                 break;
761
762         case CFG_VAR_STRING:
763                 memcpy(&ch, p, sizeof(char *));
764                 *val = (void *)ch;
765                 break;
766
767         case CFG_VAR_STR:
768                 memcpy(&s, p, sizeof(str));
769                 *val = (void *)&s;
770                 break;
771
772         case CFG_VAR_POINTER:
773                 memcpy(val, &p, sizeof(void *));
774                 break;
775
776         }
777         *val_type = CFG_VAR_TYPE(var);
778
779         return 0;
780 }
781
782 /* returns the description of a variable */
783 int cfg_help(cfg_ctx_t *ctx, str *group_name, str *var_name,
784                         char **ch, unsigned int *input_type)
785 {
786         cfg_mapping_t   *var;
787
788         /* verify the context even if we do not need it now
789         to make sure that a cfg driver has called the function
790         (very very weak security) */
791         if (!ctx) {
792                 LOG(L_ERR, "ERROR: cfg_help(): context is undefined\n");
793                 return -1;
794         }
795
796         /* look-up the group and the variable */
797         if (cfg_lookup_var(group_name, var_name, NULL, &var))
798                 return -1;
799
800         *ch = var->def->descr;
801         if (input_type)
802                 *input_type = CFG_INPUT_TYPE(var);
803         return 0;
804 }
805
806 /* return the group name and the cfg structure definition,
807  * and moves the handle to the next group
808  * Return value:
809  *      0: no more group
810  *      1: group exists
811  */
812 int cfg_get_group_next(void **h,
813                         str *gname, cfg_def_t **def)
814 {
815         cfg_group_t     *group;
816
817         group = (cfg_group_t *)(*h);
818         if (group == NULL) return 0;
819
820         gname->s = group->name;
821         gname->len = group->name_len;
822         (*def) = group->mapping->def;
823
824         (*h) = (void *)group->next;
825         return 1;
826 }
827
828 /* Initialize the handle for cfg_diff_next() */
829 int cfg_diff_init(cfg_ctx_t *ctx,
830                 void **h)
831 {
832         if (!ctx) {
833                 LOG(L_ERR, "ERROR: cfg_diff_init(): context is undefined\n");
834                 return -1;
835         }
836
837         CFG_CTX_LOCK(ctx);
838         (*h) = (void *)ctx->changed_first;
839
840         return 0;
841 }
842
843 /* return the pending changes that have not been
844  * committed yet
845  */
846 int cfg_diff_next(void **h,
847                         str *gname, str *vname,
848                         void **old_val, void **new_val,
849                         unsigned int *val_type)
850 {
851         cfg_changed_var_t       *changed;
852         void    *p;
853         static str      old_s, new_s;   /* we need the value even
854                                         after the function returns */
855         int             i;
856         char            *ch;
857
858         changed = (cfg_changed_var_t *)(*h);
859         if (changed == NULL) return 0;
860
861         gname->s = changed->group->name;
862         gname->len = changed->group->name_len;
863         vname->s = changed->var->def->name;
864         vname->len = changed->var->name_len;
865
866         /* use the module's handle to access the variable
867         It means that the variable is read from the local config
868         after forking */
869         p = *(changed->group->handle) + changed->var->offset;
870
871         switch (CFG_VAR_TYPE(changed->var)) {
872         case CFG_VAR_INT:
873                 memcpy(&i, p, sizeof(int));
874                 *old_val = (void *)(long)i;
875                 memcpy(&i, changed->new_val, sizeof(int));
876                 *new_val = (void *)(long)i;
877                 break;
878
879         case CFG_VAR_STRING:
880                 memcpy(&ch, p, sizeof(char *));
881                 *old_val = (void *)ch;
882                 memcpy(&ch, changed->new_val, sizeof(char *));
883                 *new_val = (void *)ch;
884                 break;
885
886         case CFG_VAR_STR:
887                 memcpy(&old_s, p, sizeof(str));
888                 *old_val = (void *)&old_s;
889                 memcpy(&new_s, changed->new_val, sizeof(str));
890                 *new_val = (void *)&new_s;
891                 break;
892
893         case CFG_VAR_POINTER:
894                 memcpy(old_val, &p, sizeof(void *));
895                 memcpy(new_val, &changed->new_val, sizeof(void *));
896                 break;
897
898         }
899         *val_type = CFG_VAR_TYPE(changed->var);
900
901         (*h) = (void *)changed->next;
902         return 1;
903 }
904
905 /* release the handle of cfg_diff_next() */
906 void cfg_diff_release(cfg_ctx_t *ctx)
907 {
908         if (!ctx) {
909                 LOG(L_ERR, "ERROR: cfg_diff_release(): context is undefined\n");
910                 return;
911         }
912
913         CFG_CTX_UNLOCK(ctx);
914 }