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