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");
307 if (var->def->on_set_child_cb) {
308 child_cb = cfg_child_cb_new(var_name,
309 var->def->on_set_child_cb);
311 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;
386 if (val_type == CFG_VAR_INT)
387 LOG(L_INFO, "INFO: cfg_set_now(): %.*s.%.*s "
388 "has been changed to %d\n",
389 group_name->len, group_name->s,
390 var_name->len, var_name->s,
393 else if (val_type == CFG_VAR_STRING)
394 LOG(L_INFO, "INFO: cfg_set_now(): %.*s.%.*s "
395 "has been changed to \"%s\"\n",
396 group_name->len, group_name->s,
397 var_name->len, var_name->s,
401 LOG(L_INFO, "INFO: cfg_set_now(): %.*s.%.*s "
402 "has been changed to \"%.*s\"\n",
403 group_name->len, group_name->s,
404 var_name->len, var_name->s,
405 ((str *)val)->len, ((str *)val)->s);
407 convert_val_cleanup();
411 if (cfg_shmized) CFG_WRITER_UNLOCK();
412 if (block) cfg_block_free(block);
413 if (child_cb) cfg_child_cb_free(child_cb);
416 LOG(L_ERR, "ERROR: cfg_set_now(): failed to set the variable: %.*s.%.*s\n",
417 group_name->len, group_name->s,
418 var_name->len, var_name->s);
421 convert_val_cleanup();
425 /* wrapper function for cfg_set_now */
426 int cfg_set_now_int(cfg_ctx_t *ctx, str *group_name, str *var_name, int val)
428 return cfg_set_now(ctx, group_name, var_name, (void *)(long)val, CFG_VAR_INT);
431 /* wrapper function for cfg_set_now */
432 int cfg_set_now_string(cfg_ctx_t *ctx, str *group_name, str *var_name, char *val)
434 return cfg_set_now(ctx, group_name, var_name, (void *)val, CFG_VAR_STRING);
437 /* wrapper function for cfg_set_now */
438 int cfg_set_now_str(cfg_ctx_t *ctx, str *group_name, str *var_name, str *val)
440 return cfg_set_now(ctx, group_name, var_name, (void *)val, CFG_VAR_STR);
443 /* returns the size of the variable */
444 static int cfg_var_size(cfg_mapping_t *var)
446 switch (CFG_VAR_TYPE(var)) {
452 return sizeof(char *);
457 case CFG_VAR_POINTER:
458 return sizeof(void *);
461 LOG(L_CRIT, "BUG: cfg_var_sizeK(): unknown type: %u\n",
467 /* sets the value of a variable but does not commit the change
472 * 1: variable has not been found
474 int cfg_set_delayed(cfg_ctx_t *ctx, str *group_name, str *var_name,
475 void *val, unsigned int val_type)
481 int temp_handle_created;
482 cfg_changed_var_t *changed = NULL;
487 /* the cfg has not been shmized yet, there is no
488 point in registering the change and committing it later */
489 return cfg_set_now(ctx, group_name, var_name,
493 LOG(L_ERR, "ERROR: cfg_set_delayed(): context is undefined\n");
497 /* look-up the group and the variable */
498 if (cfg_lookup_var(group_name, var_name, &group, &var))
501 /* check whether we have to convert the type */
502 if (convert_val(val_type, val, CFG_INPUT_TYPE(var), &v))
505 if ((CFG_INPUT_TYPE(var) == CFG_INPUT_INT)
506 && (var->def->min || var->def->max)) {
507 /* perform a simple min-max check for integers */
508 if (((int)(long)v < var->def->min)
509 || ((int)(long)v > var->def->max)) {
510 LOG(L_ERR, "ERROR: cfg_set_delayed(): integer value is out of range\n");
515 /* the ctx must be locked while reading and writing
516 the list of changed variables */
519 if (var->def->on_change_cb) {
520 /* The fixup function must see also the
521 not yet committed values, so a temporary handle
522 must be prepared that points to the new config.
523 Only the values within the group are applied,
524 other modifications are not visible to the callback.
525 The local config is the base. */
527 if (ctx->changed_first) {
528 temp_handle = (char *)pkg_malloc(group->size);
530 LOG(L_ERR, "ERROR: cfg_set_delayed(): "
531 "not enough memory\n");
534 temp_handle_created = 1;
535 memcpy(temp_handle, *(group->handle), group->size);
537 /* apply the changes */
538 for ( changed = ctx->changed_first;
540 changed = changed->next
542 if (changed->group != group) continue;
544 memcpy( temp_handle + changed->var->offset,
546 cfg_var_size(changed->var));
549 /* there is not any change */
550 temp_handle = *(group->handle);
551 temp_handle_created = 0;
554 if (var->def->on_change_cb(temp_handle,
557 LOG(L_ERR, "ERROR: cfg_set_delayed(): fixup failed\n");
558 if (temp_handle_created) pkg_free(temp_handle);
561 if (temp_handle_created) pkg_free(temp_handle);
565 /* everything went ok, we can add the new value to the list */
566 size = sizeof(cfg_changed_var_t) + cfg_var_size(var) - 1;
567 changed = (cfg_changed_var_t *)shm_malloc(size);
569 LOG(L_ERR, "ERROR: cfg_set_delayed(): not enough shm memory\n");
572 memset(changed, 0, size);
573 changed->group = group;
576 switch (CFG_VAR_TYPE(var)) {
580 memcpy(changed->new_val, &i, sizeof(int));
584 /* clone the string to shm mem */
586 s.len = (s.s) ? strlen(s.s) : 0;
587 if (cfg_clone_str(&s, &s)) goto error;
588 memcpy(changed->new_val, &s.s, sizeof(char *));
592 /* clone the string to shm mem */
594 if (cfg_clone_str(&s, &s)) goto error;
595 memcpy(changed->new_val, &s, sizeof(str));
598 case CFG_VAR_POINTER:
599 memcpy(changed->new_val, &v, sizeof(void *));
604 /* Add the new item to the end of the linked list,
605 The commit will go though the list from the first item,
606 so the list is kept in order */
607 if (ctx->changed_first)
608 ctx->changed_last->next = changed;
610 ctx->changed_first = changed;
612 ctx->changed_last = changed;
616 if (val_type == CFG_VAR_INT)
617 LOG(L_INFO, "INFO: cfg_set_delayed(): %.*s.%.*s "
618 "is going to be changed to %d "
620 group_name->len, group_name->s,
621 var_name->len, var_name->s,
625 else if (val_type == CFG_VAR_STRING)
626 LOG(L_INFO, "INFO: cfg_set_delayed(): %.*s.%.*s "
627 "is going to be changed to \"%s\" "
629 group_name->len, group_name->s,
630 var_name->len, var_name->s,
635 LOG(L_INFO, "INFO: cfg_set_delayed(): %.*s.%.*s "
636 "is going to be changed to \"%.*s\" "
638 group_name->len, group_name->s,
639 var_name->len, var_name->s,
640 ((str *)val)->len, ((str *)val)->s,
643 convert_val_cleanup();
648 if (changed) shm_free(changed);
650 LOG(L_ERR, "ERROR: cfg_set_delayed(): failed to set the variable: %.*s.%.*s\n",
651 group_name->len, group_name->s,
652 var_name->len, var_name->s);
654 convert_val_cleanup();
658 /* wrapper function for cfg_set_delayed */
659 int cfg_set_delayed_int(cfg_ctx_t *ctx, str *group_name, str *var_name, int val)
661 return cfg_set_delayed(ctx, group_name, var_name, (void *)(long)val, CFG_VAR_INT);
664 /* wrapper function for cfg_set_delayed */
665 int cfg_set_delayed_string(cfg_ctx_t *ctx, str *group_name, str *var_name, char *val)
667 return cfg_set_delayed(ctx, group_name, var_name, (void *)val, CFG_VAR_STRING);
670 /* wrapper function for cfg_set_delayed */
671 int cfg_set_delayed_str(cfg_ctx_t *ctx, str *group_name, str *var_name, str *val)
673 return cfg_set_delayed(ctx, group_name, var_name, (void *)val, CFG_VAR_STR);
676 /* commits the previously prepared changes within the context */
677 int cfg_commit(cfg_ctx_t *ctx)
679 int replaced_num = 0;
680 cfg_changed_var_t *changed, *changed2;
682 char **replaced = NULL;
683 cfg_child_cb_t *child_cb;
684 cfg_child_cb_t *child_cb_first = NULL;
685 cfg_child_cb_t *child_cb_last = NULL;
691 LOG(L_ERR, "ERROR: cfg_commit(): context is undefined\n");
695 if (!cfg_shmized) return 0; /* nothing to do */
697 /* the ctx must be locked while reading and writing
698 the list of changed variables */
701 /* is there any change? */
702 if (!ctx->changed_first) goto done;
704 /* count the number of replaced strings,
705 and prepare the linked list of per-child process
706 callbacks, that will be added to the global list */
707 for ( changed = ctx->changed_first;
709 changed = changed->next
711 if ((CFG_VAR_TYPE(changed->var) == CFG_VAR_STRING)
712 || (CFG_VAR_TYPE(changed->var) == CFG_VAR_STR))
716 if (changed->var->def->on_set_child_cb) {
717 s.s = changed->var->def->name;
718 s.len = changed->var->name_len;
719 child_cb = cfg_child_cb_new(&s,
720 changed->var->def->on_set_child_cb);
721 if (!child_cb) goto error0;
724 child_cb_last->next = child_cb;
726 child_cb_first = child_cb;
727 child_cb_last = child_cb;
732 /* allocate memory for the replaced string array */
733 size = sizeof(char *)*(replaced_num + 1);
734 replaced = (char **)shm_malloc(size);
736 LOG(L_ERR, "ERROR: cfg_commit(): not enough shm memory\n");
739 memset(replaced, 0 , size);
742 /* make sure that nobody else replaces the global config
743 while the new one is prepared */
746 /* clone the memory block, and prepare the modification */
747 if (!(block = cfg_clone_global())) {
752 /* apply the modifications to the buffer */
754 for ( changed = ctx->changed_first;
756 changed = changed->next
759 + changed->group->offset
760 + changed->var->offset;
762 if ((CFG_VAR_TYPE(changed->var) == CFG_VAR_STRING)
763 || (CFG_VAR_TYPE(changed->var) == CFG_VAR_STR)) {
764 memcpy(&(replaced[replaced_num]), p, sizeof(char *));
765 if (replaced[replaced_num])
767 /* else do not increase replaced_num, because
768 the cfg_block_free() will stop at the first
774 cfg_var_size(changed->var));
777 /* replace the global config with the new one */
778 cfg_install_global(block, replaced, child_cb_first, child_cb_last);
781 /* free the changed list */
782 for ( changed = ctx->changed_first;
786 changed2 = changed->next;
789 ctx->changed_first = NULL;
790 ctx->changed_last = NULL;
793 LOG(L_INFO, "INFO: cfg_commit(): config changes have been applied "
805 if (child_cb_first) cfg_child_cb_free(child_cb_first);
806 if (replaced) shm_free(replaced);
811 /* drops the not yet committed changes within the context */
812 int cfg_rollback(cfg_ctx_t *ctx)
814 cfg_changed_var_t *changed, *changed2;
818 LOG(L_ERR, "ERROR: cfg_rollback(): context is undefined\n");
822 if (!cfg_shmized) return 0; /* nothing to do */
824 LOG(L_INFO, "INFO: cfg_rollback(): deleting the config changes "
828 /* the ctx must be locked while reading and writing
829 the list of changed variables */
832 for ( changed = ctx->changed_first;
836 changed2 = changed->next;
838 if ((CFG_VAR_TYPE(changed->var) == CFG_VAR_STRING)
839 || (CFG_VAR_TYPE(changed->var) == CFG_VAR_STR)) {
840 memcpy(&new_string, changed->new_val, sizeof(char *));
841 if (new_string) shm_free(new_string);
845 ctx->changed_first = NULL;
846 ctx->changed_last = NULL;
853 /* retrieves the value of a variable
857 * 1 - variable exists, but it is not readable
859 int cfg_get_by_name(cfg_ctx_t *ctx, str *group_name, str *var_name,
860 void **val, unsigned int *val_type)
865 static str s; /* we need the value even
866 after the function returns */
870 /* verify the context even if we do not need it now
871 to make sure that a cfg driver has called the function
872 (very very weak security) */
874 LOG(L_ERR, "ERROR: cfg_get_by_name(): context is undefined\n");
878 /* look-up the group and the variable */
879 if (cfg_lookup_var(group_name, var_name, &group, &var))
882 if (var->def->on_change_cb) {
883 /* The variable cannot be retrieved, because the fixup
884 function may have changed it, and it is better to return
885 an error than an incorrect value */
889 /* use the module's handle to access the variable
890 It means that the variable is read from the local config
892 p = *(group->handle) + var->offset;
894 switch (CFG_VAR_TYPE(var)) {
896 memcpy(&i, p, sizeof(int));
897 *val = (void *)(long)i;
901 memcpy(&ch, p, sizeof(char *));
906 memcpy(&s, p, sizeof(str));
910 case CFG_VAR_POINTER:
911 memcpy(val, &p, sizeof(void *));
915 *val_type = CFG_VAR_TYPE(var);
920 /* returns the description of a variable */
921 int cfg_help(cfg_ctx_t *ctx, str *group_name, str *var_name,
922 char **ch, unsigned int *input_type)
926 /* verify the context even if we do not need it now
927 to make sure that a cfg driver has called the function
928 (very very weak security) */
930 LOG(L_ERR, "ERROR: cfg_help(): context is undefined\n");
934 /* look-up the group and the variable */
935 if (cfg_lookup_var(group_name, var_name, NULL, &var))
938 *ch = var->def->descr;
940 *input_type = CFG_INPUT_TYPE(var);
944 /* return the group name and the cfg structure definition,
945 * and moves the handle to the next group
950 int cfg_get_group_next(void **h,
951 str *gname, cfg_def_t **def)
955 group = (cfg_group_t *)(*h);
956 if (group == NULL) return 0;
958 gname->s = group->name;
959 gname->len = group->name_len;
960 (*def) = group->mapping->def;
962 (*h) = (void *)group->next;
966 /* Initialize the handle for cfg_diff_next() */
967 int cfg_diff_init(cfg_ctx_t *ctx,
971 LOG(L_ERR, "ERROR: cfg_diff_init(): context is undefined\n");
976 (*h) = (void *)ctx->changed_first;
981 /* return the pending changes that have not been
984 int cfg_diff_next(void **h,
985 str *gname, str *vname,
986 void **old_val, void **new_val,
987 unsigned int *val_type)
989 cfg_changed_var_t *changed;
991 static str old_s, new_s; /* we need the value even
992 after the function returns */
996 changed = (cfg_changed_var_t *)(*h);
997 if (changed == NULL) return 0;
999 gname->s = changed->group->name;
1000 gname->len = changed->group->name_len;
1001 vname->s = changed->var->def->name;
1002 vname->len = changed->var->name_len;
1004 /* use the module's handle to access the variable
1005 It means that the variable is read from the local config
1007 p = *(changed->group->handle) + changed->var->offset;
1009 switch (CFG_VAR_TYPE(changed->var)) {
1011 memcpy(&i, p, sizeof(int));
1012 *old_val = (void *)(long)i;
1013 memcpy(&i, changed->new_val, sizeof(int));
1014 *new_val = (void *)(long)i;
1017 case CFG_VAR_STRING:
1018 memcpy(&ch, p, sizeof(char *));
1019 *old_val = (void *)ch;
1020 memcpy(&ch, changed->new_val, sizeof(char *));
1021 *new_val = (void *)ch;
1025 memcpy(&old_s, p, sizeof(str));
1026 *old_val = (void *)&old_s;
1027 memcpy(&new_s, changed->new_val, sizeof(str));
1028 *new_val = (void *)&new_s;
1031 case CFG_VAR_POINTER:
1032 memcpy(old_val, &p, sizeof(void *));
1033 memcpy(new_val, &changed->new_val, sizeof(void *));
1037 *val_type = CFG_VAR_TYPE(changed->var);
1039 (*h) = (void *)changed->next;
1043 /* release the handle of cfg_diff_next() */
1044 void cfg_diff_release(cfg_ctx_t *ctx)
1047 LOG(L_ERR, "ERROR: cfg_diff_release(): context is undefined\n");
1051 CFG_CTX_UNLOCK(ctx);