4 * Copyright (C) 2007 iptelorg GmbH
6 * This file is part of ser, a free SIP server.
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
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:
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.
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
29 * 2007-12-03 Initial version (Miklos)
37 #include "cfg_struct.h"
41 /* linked list of all the registered cfg contexts */
42 static cfg_ctx_t *cfg_ctx_list = NULL;
44 /* creates a new config context that is an interface to the
45 * cfg variables with write permission
47 int cfg_register_ctx(cfg_ctx_t **handle, cfg_on_declare on_declare_cb)
53 /* allocate memory for the new context
54 Better to use shm mem, because 'changed' and 'lock'
55 must be in shm mem anyway */
56 ctx = (cfg_ctx_t *)shm_malloc(sizeof(cfg_ctx_t));
58 LOG(L_ERR, "ERROR: cfg_register_ctx(): not enough shm memory\n");
61 memset(ctx, 0, sizeof(cfg_ctx_t));
62 if (lock_init(&ctx->lock) == 0) {
63 LOG(L_ERR, "ERROR: cfg_register_ctx(): failed to init lock\n");
68 /* add the new ctx to the beginning of the list */
69 ctx->next = cfg_ctx_list;
72 /* let the driver know about the already registered groups
73 * The handle of the context must be set before calling the
74 * on_declare callbacks. */
77 ctx->on_declare_cb = on_declare_cb;
79 for ( group = cfg_group;
83 /* dynamic groups are not ready, the callback
84 will be called later when the group is fixed-up */
85 if (group->dynamic) continue;
87 gname.s = group->name;
88 gname.len = group->name_len;
89 on_declare_cb(&gname, group->mapping->def);
96 /* free the memory allocated for the contexts */
97 void cfg_ctx_destroy(void)
99 cfg_ctx_t *ctx, *ctx2;
101 for ( ctx = cfg_ctx_list;
111 /* notify the drivers about the new config definition */
112 void cfg_notify_drivers(char *group_name, int group_name_len, cfg_def_t *def)
117 gname.s = group_name;
118 gname.len = group_name_len;
120 for ( ctx = cfg_ctx_list;
124 if (ctx->on_declare_cb)
125 ctx->on_declare_cb(&gname, def);
128 /* placeholder for a temporary string */
129 static char *temp_string = NULL;
131 /* convert the value to the requested type */
132 static int convert_val(unsigned int val_type, void *val,
133 unsigned int var_type, void **new_val)
138 static char buf[INT2STR_MAX_LEN];
140 /* we have to convert from val_type to var_type */
141 switch (CFG_INPUT_MASK(var_type)) {
143 if (val_type == CFG_VAR_INT) {
147 } else if (val_type == CFG_VAR_STRING) {
148 if (!val || (((char *)val)[0] == '\0')) {
149 LOG(L_ERR, "ERROR: convert_val(): "
150 "cannot convert NULL string value to integer\n");
153 *new_val = (void *)(long)strtol((char *)val, &end, 10);
155 LOG(L_ERR, "ERROR: convert_val(): "
156 "cannot convert string to integer '%s'\n",
162 } else if (val_type == CFG_VAR_STR) {
163 if (!((str *)val)->len || !((str *)val)->s) {
164 LOG(L_ERR, "ERROR: convert_val(): "
165 "cannot convert NULL str value to integer\n");
168 if (str2sint((str *)val, &i)) {
169 LOG(L_ERR, "ERROR: convert_val(): "
170 "cannot convert string to integer '%.*s'\n",
171 ((str *)val)->len, ((str *)val)->s);
174 *new_val = (void *)(long)i;
179 case CFG_INPUT_STRING:
180 if (val_type == CFG_VAR_INT) {
181 buf[snprintf(buf, sizeof(buf)-1, "%ld", (long)val)] = '\0';
185 } else if (val_type == CFG_VAR_STRING) {
189 } else if (val_type == CFG_VAR_STR) {
190 if (!((str *)val)->s) {
194 /* the value may not be zero-terminated, thus,
195 a new variable has to be allocated with larger memory space */
196 if (temp_string) pkg_free(temp_string);
197 temp_string = (char *)pkg_malloc(sizeof(char) * (((str *)val)->len + 1));
199 LOG(L_ERR, "ERROR: convert_val(): not enough memory\n");
202 memcpy(temp_string, ((str *)val)->s, ((str *)val)->len);
203 temp_string[((str *)val)->len] = '\0';
204 *new_val = (void *)temp_string;
211 if (val_type == CFG_VAR_INT) {
212 s.len = snprintf(buf, sizeof(buf)-1, "%ld", (long)val);
215 *new_val = (void *)&s;
218 } else if (val_type == CFG_VAR_STRING) {
220 s.len = (s.s) ? strlen(s.s) : 0;
221 *new_val = (void *)&s;
224 } else if (val_type == CFG_VAR_STR) {
234 LOG(L_ERR, "ERROR: convert_val(): got a value with type %u, but expected %u\n",
235 val_type, CFG_INPUT_MASK(var_type));
239 #define convert_val_cleanup() \
242 pkg_free(temp_string); \
243 temp_string = NULL; \
247 /* sets the value of a variable without the need of commit
252 * 1: variable has not been found
254 int cfg_set_now(cfg_ctx_t *ctx, str *group_name, str *var_name,
255 void *val, unsigned int val_type)
260 cfg_block_t *block = NULL;
262 char *old_string = NULL;
263 char **replaced = NULL;
264 cfg_child_cb_t *child_cb = NULL;
267 /* verify the context even if we do not need it now
268 to make sure that a cfg driver has called the function
269 (very very weak security) */
271 LOG(L_ERR, "ERROR: cfg_set_now(): context is undefined\n");
275 /* look-up the group and the variable */
276 if (cfg_lookup_var(group_name, var_name, &group, &var))
279 /* check whether we have to convert the type */
280 if (convert_val(val_type, val, CFG_INPUT_TYPE(var), &v))
283 if ((CFG_INPUT_TYPE(var) == CFG_INPUT_INT)
284 && (var->def->min || var->def->max)) {
285 /* perform a simple min-max check for integers */
286 if (((int)(long)v < var->def->min)
287 || ((int)(long)v > var->def->max)) {
288 LOG(L_ERR, "ERROR: cfg_set_now(): integer value is out of range\n");
293 if (var->def->on_change_cb) {
294 /* Call the fixup function.
295 There is no need to set a temporary cfg handle,
296 becaue a single variable is changed */
297 if (var->def->on_change_cb(*(group->handle),
300 LOG(L_ERR, "ERROR: cfg_set_now(): fixup failed\n");
306 if (var->def->on_set_child_cb) {
307 child_cb = cfg_child_cb_new(var_name,
308 var->def->on_set_child_cb);
310 LOG(L_ERR, "ERROR: cfg_set_now(): not enough shm memory\n");
316 /* make sure that nobody else replaces the global config
317 while the new one is prepared */
320 /* clone the memory block, and prepare the modification */
321 if (!(block = cfg_clone_global())) goto error;
323 p = block->vars+group->offset+var->offset;
325 /* we are allowed to rewrite the value on-the-fly
326 The handle either points to group->vars, or to the
327 shared memory block (dynamic group) */
328 p = *(group->handle) + var->offset;
331 /* set the new value */
332 switch (CFG_VAR_TYPE(var)) {
335 memcpy(p, &i, sizeof(int));
339 /* clone the string to shm mem */
341 s.len = (s.s) ? strlen(s.s) : 0;
342 if (cfg_clone_str(&s, &s)) goto error;
343 memcpy(&old_string, p, sizeof(char *));
344 memcpy(p, &s.s, sizeof(char *));
348 /* clone the string to shm mem */
350 if (cfg_clone_str(&s, &s)) goto error;
351 memcpy(&old_string, p, sizeof(char *));
352 memcpy(p, &s, sizeof(str));
355 case CFG_VAR_POINTER:
356 memcpy(p, &v, sizeof(void *));
363 /* prepare the array of the replaced strings,
364 they will be freed when the old block is freed */
365 replaced = (char **)shm_malloc(sizeof(char *)*2);
367 LOG(L_ERR, "ERROR: cfg_set_now(): not enough shm memory\n");
370 replaced[0] = old_string;
373 /* replace the global config with the new one */
374 cfg_install_global(block, replaced, child_cb, child_cb);
377 /* cfg_set() may be called more than once before forking */
378 if (old_string && (var->flag & cfg_var_shmized))
379 shm_free(old_string);
381 /* flag the variable because there is no need
382 to shmize it again */
383 var->flag |= cfg_var_shmized;
385 /* the global config does not have to be replaced,
386 but the child callback has to be installed, otherwise the
387 child processes will miss the change */
388 cfg_install_child_cb(child_cb, child_cb);
391 if (val_type == CFG_VAR_INT)
392 LOG(L_INFO, "INFO: cfg_set_now(): %.*s.%.*s "
393 "has been changed to %d\n",
394 group_name->len, group_name->s,
395 var_name->len, var_name->s,
398 else if (val_type == CFG_VAR_STRING)
399 LOG(L_INFO, "INFO: cfg_set_now(): %.*s.%.*s "
400 "has been changed to \"%s\"\n",
401 group_name->len, group_name->s,
402 var_name->len, var_name->s,
406 LOG(L_INFO, "INFO: cfg_set_now(): %.*s.%.*s "
407 "has been changed to \"%.*s\"\n",
408 group_name->len, group_name->s,
409 var_name->len, var_name->s,
410 ((str *)val)->len, ((str *)val)->s);
412 convert_val_cleanup();
416 if (cfg_shmized) CFG_WRITER_UNLOCK();
417 if (block) cfg_block_free(block);
418 if (child_cb) cfg_child_cb_free(child_cb);
421 LOG(L_ERR, "ERROR: cfg_set_now(): failed to set the variable: %.*s.%.*s\n",
422 group_name->len, group_name->s,
423 var_name->len, var_name->s);
426 convert_val_cleanup();
430 /* wrapper function for cfg_set_now */
431 int cfg_set_now_int(cfg_ctx_t *ctx, str *group_name, str *var_name, int val)
433 return cfg_set_now(ctx, group_name, var_name, (void *)(long)val, CFG_VAR_INT);
436 /* wrapper function for cfg_set_now */
437 int cfg_set_now_string(cfg_ctx_t *ctx, str *group_name, str *var_name, char *val)
439 return cfg_set_now(ctx, group_name, var_name, (void *)val, CFG_VAR_STRING);
442 /* wrapper function for cfg_set_now */
443 int cfg_set_now_str(cfg_ctx_t *ctx, str *group_name, str *var_name, str *val)
445 return cfg_set_now(ctx, group_name, var_name, (void *)val, CFG_VAR_STR);
448 /* returns the size of the variable */
449 static int cfg_var_size(cfg_mapping_t *var)
451 switch (CFG_VAR_TYPE(var)) {
457 return sizeof(char *);
462 case CFG_VAR_POINTER:
463 return sizeof(void *);
466 LOG(L_CRIT, "BUG: cfg_var_sizeK(): unknown type: %u\n",
472 /* sets the value of a variable but does not commit the change
477 * 1: variable has not been found
479 int cfg_set_delayed(cfg_ctx_t *ctx, str *group_name, str *var_name,
480 void *val, unsigned int val_type)
486 int temp_handle_created;
487 cfg_changed_var_t *changed = NULL;
492 /* the cfg has not been shmized yet, there is no
493 point in registering the change and committing it later */
494 return cfg_set_now(ctx, group_name, var_name,
498 LOG(L_ERR, "ERROR: cfg_set_delayed(): context is undefined\n");
502 /* look-up the group and the variable */
503 if (cfg_lookup_var(group_name, var_name, &group, &var))
506 /* check whether we have to convert the type */
507 if (convert_val(val_type, val, CFG_INPUT_TYPE(var), &v))
510 if ((CFG_INPUT_TYPE(var) == CFG_INPUT_INT)
511 && (var->def->min || var->def->max)) {
512 /* perform a simple min-max check for integers */
513 if (((int)(long)v < var->def->min)
514 || ((int)(long)v > var->def->max)) {
515 LOG(L_ERR, "ERROR: cfg_set_delayed(): integer value is out of range\n");
520 /* the ctx must be locked while reading and writing
521 the list of changed variables */
524 if (var->def->on_change_cb) {
525 /* The fixup function must see also the
526 not yet committed values, so a temporary handle
527 must be prepared that points to the new config.
528 Only the values within the group are applied,
529 other modifications are not visible to the callback.
530 The local config is the base. */
532 if (ctx->changed_first) {
533 temp_handle = (char *)pkg_malloc(group->size);
535 LOG(L_ERR, "ERROR: cfg_set_delayed(): "
536 "not enough memory\n");
539 temp_handle_created = 1;
540 memcpy(temp_handle, *(group->handle), group->size);
542 /* apply the changes */
543 for ( changed = ctx->changed_first;
545 changed = changed->next
547 if (changed->group != group) continue;
549 memcpy( temp_handle + changed->var->offset,
551 cfg_var_size(changed->var));
554 /* there is not any change */
555 temp_handle = *(group->handle);
556 temp_handle_created = 0;
559 if (var->def->on_change_cb(temp_handle,
562 LOG(L_ERR, "ERROR: cfg_set_delayed(): fixup failed\n");
563 if (temp_handle_created) pkg_free(temp_handle);
566 if (temp_handle_created) pkg_free(temp_handle);
570 /* everything went ok, we can add the new value to the list */
571 size = sizeof(cfg_changed_var_t) + cfg_var_size(var) - 1;
572 changed = (cfg_changed_var_t *)shm_malloc(size);
574 LOG(L_ERR, "ERROR: cfg_set_delayed(): not enough shm memory\n");
577 memset(changed, 0, size);
578 changed->group = group;
581 switch (CFG_VAR_TYPE(var)) {
585 memcpy(changed->new_val, &i, sizeof(int));
589 /* clone the string to shm mem */
591 s.len = (s.s) ? strlen(s.s) : 0;
592 if (cfg_clone_str(&s, &s)) goto error;
593 memcpy(changed->new_val, &s.s, sizeof(char *));
597 /* clone the string to shm mem */
599 if (cfg_clone_str(&s, &s)) goto error;
600 memcpy(changed->new_val, &s, sizeof(str));
603 case CFG_VAR_POINTER:
604 memcpy(changed->new_val, &v, sizeof(void *));
609 /* Add the new item to the end of the linked list,
610 The commit will go though the list from the first item,
611 so the list is kept in order */
612 if (ctx->changed_first)
613 ctx->changed_last->next = changed;
615 ctx->changed_first = changed;
617 ctx->changed_last = changed;
621 if (val_type == CFG_VAR_INT)
622 LOG(L_INFO, "INFO: cfg_set_delayed(): %.*s.%.*s "
623 "is going to be changed to %d "
625 group_name->len, group_name->s,
626 var_name->len, var_name->s,
630 else if (val_type == CFG_VAR_STRING)
631 LOG(L_INFO, "INFO: cfg_set_delayed(): %.*s.%.*s "
632 "is going to be changed to \"%s\" "
634 group_name->len, group_name->s,
635 var_name->len, var_name->s,
640 LOG(L_INFO, "INFO: cfg_set_delayed(): %.*s.%.*s "
641 "is going to be changed to \"%.*s\" "
643 group_name->len, group_name->s,
644 var_name->len, var_name->s,
645 ((str *)val)->len, ((str *)val)->s,
648 convert_val_cleanup();
653 if (changed) shm_free(changed);
655 LOG(L_ERR, "ERROR: cfg_set_delayed(): failed to set the variable: %.*s.%.*s\n",
656 group_name->len, group_name->s,
657 var_name->len, var_name->s);
659 convert_val_cleanup();
663 /* wrapper function for cfg_set_delayed */
664 int cfg_set_delayed_int(cfg_ctx_t *ctx, str *group_name, str *var_name, int val)
666 return cfg_set_delayed(ctx, group_name, var_name, (void *)(long)val, CFG_VAR_INT);
669 /* wrapper function for cfg_set_delayed */
670 int cfg_set_delayed_string(cfg_ctx_t *ctx, str *group_name, str *var_name, char *val)
672 return cfg_set_delayed(ctx, group_name, var_name, (void *)val, CFG_VAR_STRING);
675 /* wrapper function for cfg_set_delayed */
676 int cfg_set_delayed_str(cfg_ctx_t *ctx, str *group_name, str *var_name, str *val)
678 return cfg_set_delayed(ctx, group_name, var_name, (void *)val, CFG_VAR_STR);
681 /* commits the previously prepared changes within the context */
682 int cfg_commit(cfg_ctx_t *ctx)
684 int replaced_num = 0;
685 cfg_changed_var_t *changed, *changed2;
687 char **replaced = NULL;
688 cfg_child_cb_t *child_cb;
689 cfg_child_cb_t *child_cb_first = NULL;
690 cfg_child_cb_t *child_cb_last = NULL;
696 LOG(L_ERR, "ERROR: cfg_commit(): context is undefined\n");
700 if (!cfg_shmized) return 0; /* nothing to do */
702 /* the ctx must be locked while reading and writing
703 the list of changed variables */
706 /* is there any change? */
707 if (!ctx->changed_first) goto done;
709 /* count the number of replaced strings,
710 and prepare the linked list of per-child process
711 callbacks, that will be added to the global list */
712 for ( changed = ctx->changed_first;
714 changed = changed->next
716 if ((CFG_VAR_TYPE(changed->var) == CFG_VAR_STRING)
717 || (CFG_VAR_TYPE(changed->var) == CFG_VAR_STR))
721 if (changed->var->def->on_set_child_cb) {
722 s.s = changed->var->def->name;
723 s.len = changed->var->name_len;
724 child_cb = cfg_child_cb_new(&s,
725 changed->var->def->on_set_child_cb);
726 if (!child_cb) goto error0;
729 child_cb_last->next = child_cb;
731 child_cb_first = child_cb;
732 child_cb_last = child_cb;
737 /* allocate memory for the replaced string array */
738 size = sizeof(char *)*(replaced_num + 1);
739 replaced = (char **)shm_malloc(size);
741 LOG(L_ERR, "ERROR: cfg_commit(): not enough shm memory\n");
744 memset(replaced, 0 , size);
747 /* make sure that nobody else replaces the global config
748 while the new one is prepared */
751 /* clone the memory block, and prepare the modification */
752 if (!(block = cfg_clone_global())) {
757 /* apply the modifications to the buffer */
759 for ( changed = ctx->changed_first;
761 changed = changed->next
764 + changed->group->offset
765 + changed->var->offset;
767 if ((CFG_VAR_TYPE(changed->var) == CFG_VAR_STRING)
768 || (CFG_VAR_TYPE(changed->var) == CFG_VAR_STR)) {
769 memcpy(&(replaced[replaced_num]), p, sizeof(char *));
770 if (replaced[replaced_num])
772 /* else do not increase replaced_num, because
773 the cfg_block_free() will stop at the first
779 cfg_var_size(changed->var));
782 /* replace the global config with the new one */
783 cfg_install_global(block, replaced, child_cb_first, child_cb_last);
786 /* free the changed list */
787 for ( changed = ctx->changed_first;
791 changed2 = changed->next;
794 ctx->changed_first = NULL;
795 ctx->changed_last = NULL;
798 LOG(L_INFO, "INFO: cfg_commit(): config changes have been applied "
810 if (child_cb_first) cfg_child_cb_free(child_cb_first);
811 if (replaced) shm_free(replaced);
816 /* drops the not yet committed changes within the context */
817 int cfg_rollback(cfg_ctx_t *ctx)
819 cfg_changed_var_t *changed, *changed2;
823 LOG(L_ERR, "ERROR: cfg_rollback(): context is undefined\n");
827 if (!cfg_shmized) return 0; /* nothing to do */
829 LOG(L_INFO, "INFO: cfg_rollback(): deleting the config changes "
833 /* the ctx must be locked while reading and writing
834 the list of changed variables */
837 for ( changed = ctx->changed_first;
841 changed2 = changed->next;
843 if ((CFG_VAR_TYPE(changed->var) == CFG_VAR_STRING)
844 || (CFG_VAR_TYPE(changed->var) == CFG_VAR_STR)) {
845 memcpy(&new_string, changed->new_val, sizeof(char *));
846 if (new_string) shm_free(new_string);
850 ctx->changed_first = NULL;
851 ctx->changed_last = NULL;
858 /* retrieves the value of a variable
862 * 1 - variable exists, but it is not readable
864 int cfg_get_by_name(cfg_ctx_t *ctx, str *group_name, str *var_name,
865 void **val, unsigned int *val_type)
870 static str s; /* we need the value even
871 after the function returns */
875 /* verify the context even if we do not need it now
876 to make sure that a cfg driver has called the function
877 (very very weak security) */
879 LOG(L_ERR, "ERROR: cfg_get_by_name(): context is undefined\n");
883 /* look-up the group and the variable */
884 if (cfg_lookup_var(group_name, var_name, &group, &var))
887 if (var->def->on_change_cb) {
888 /* The variable cannot be retrieved, because the fixup
889 function may have changed it, and it is better to return
890 an error than an incorrect value */
894 /* use the module's handle to access the variable
895 It means that the variable is read from the local config
897 p = *(group->handle) + var->offset;
899 switch (CFG_VAR_TYPE(var)) {
901 memcpy(&i, p, sizeof(int));
902 *val = (void *)(long)i;
906 memcpy(&ch, p, sizeof(char *));
911 memcpy(&s, p, sizeof(str));
915 case CFG_VAR_POINTER:
916 memcpy(val, &p, sizeof(void *));
920 *val_type = CFG_VAR_TYPE(var);
925 /* returns the description of a variable */
926 int cfg_help(cfg_ctx_t *ctx, str *group_name, str *var_name,
927 char **ch, unsigned int *input_type)
931 /* verify the context even if we do not need it now
932 to make sure that a cfg driver has called the function
933 (very very weak security) */
935 LOG(L_ERR, "ERROR: cfg_help(): context is undefined\n");
939 /* look-up the group and the variable */
940 if (cfg_lookup_var(group_name, var_name, NULL, &var))
943 *ch = var->def->descr;
945 *input_type = CFG_INPUT_TYPE(var);
949 /* return the group name and the cfg structure definition,
950 * and moves the handle to the next group
955 int cfg_get_group_next(void **h,
956 str *gname, cfg_def_t **def)
960 group = (cfg_group_t *)(*h);
961 if (group == NULL) return 0;
963 gname->s = group->name;
964 gname->len = group->name_len;
965 (*def) = group->mapping->def;
967 (*h) = (void *)group->next;
971 /* Initialize the handle for cfg_diff_next() */
972 int cfg_diff_init(cfg_ctx_t *ctx,
976 LOG(L_ERR, "ERROR: cfg_diff_init(): context is undefined\n");
981 (*h) = (void *)ctx->changed_first;
986 /* return the pending changes that have not been
989 int cfg_diff_next(void **h,
990 str *gname, str *vname,
991 void **old_val, void **new_val,
992 unsigned int *val_type)
994 cfg_changed_var_t *changed;
996 static str old_s, new_s; /* we need the value even
997 after the function returns */
1001 changed = (cfg_changed_var_t *)(*h);
1002 if (changed == NULL) return 0;
1004 gname->s = changed->group->name;
1005 gname->len = changed->group->name_len;
1006 vname->s = changed->var->def->name;
1007 vname->len = changed->var->name_len;
1009 /* use the module's handle to access the variable
1010 It means that the variable is read from the local config
1012 p = *(changed->group->handle) + changed->var->offset;
1014 switch (CFG_VAR_TYPE(changed->var)) {
1016 memcpy(&i, p, sizeof(int));
1017 *old_val = (void *)(long)i;
1018 memcpy(&i, changed->new_val, sizeof(int));
1019 *new_val = (void *)(long)i;
1022 case CFG_VAR_STRING:
1023 memcpy(&ch, p, sizeof(char *));
1024 *old_val = (void *)ch;
1025 memcpy(&ch, changed->new_val, sizeof(char *));
1026 *new_val = (void *)ch;
1030 memcpy(&old_s, p, sizeof(str));
1031 *old_val = (void *)&old_s;
1032 memcpy(&new_s, changed->new_val, sizeof(str));
1033 *new_val = (void *)&new_s;
1036 case CFG_VAR_POINTER:
1037 memcpy(old_val, &p, sizeof(void *));
1038 memcpy(new_val, &changed->new_val, sizeof(void *));
1042 *val_type = CFG_VAR_TYPE(changed->var);
1044 (*h) = (void *)changed->next;
1048 /* release the handle of cfg_diff_next() */
1049 void cfg_diff_release(cfg_ctx_t *ctx)
1052 LOG(L_ERR, "ERROR: cfg_diff_release(): context is undefined\n");
1056 CFG_CTX_UNLOCK(ctx);