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 cfg_ctx_t *cfg_register_ctx(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 */
74 ctx->on_declare_cb = on_declare_cb;
76 for ( group = cfg_group;
80 /* dynamic groups are not ready, the callback
81 will be called later when the group is fixed-up */
82 if (group->dynamic) continue;
84 gname.s = group->name;
85 gname.len = group->name_len;
86 on_declare_cb(&gname, group->mapping->def);
93 /* free the memory allocated for the contexts */
94 void cfg_ctx_destroy(void)
96 cfg_ctx_t *ctx, *ctx2;
98 for ( ctx = cfg_ctx_list;
108 /* notify the drivers about the new config definition */
109 void cfg_notify_drivers(char *group_name, int group_name_len, cfg_def_t *def)
114 gname.s = group_name;
115 gname.len = group_name_len;
117 for ( ctx = cfg_ctx_list;
121 if (ctx->on_declare_cb)
122 ctx->on_declare_cb(&gname, def);
126 * Convert an str into signed integer
127 * this function should be moved to ../ut.h
129 static int str2sint(str* _s, int* _r)
134 if (_s->len == 0) return -1;
139 if (_s->s[0] == '+') {
141 } else if (_s->s[0] == '-') {
145 for(; i < _s->len; i++) {
146 if ((_s->s[i] >= '0') && (_s->s[i] <= '9')) {
148 *_r += _s->s[i] - '0';
159 /* placeholder for a temporary string */
160 static char *temp_string = NULL;
162 /* convert the value to the requested type */
163 static int convert_val(unsigned int val_type, void *val,
164 unsigned int var_type, void **new_val)
169 static char buf[INT2STR_MAX_LEN];
171 /* we have to convert from val_type to var_type */
172 switch (CFG_INPUT_MASK(var_type)) {
174 if (val_type == CFG_VAR_INT) {
178 } else if (val_type == CFG_VAR_STRING) {
179 *new_val = (void *)(long)strtol((char *)val, &end, 10);
181 LOG(L_ERR, "ERROR: convert_val(): "
182 "cannot convert string to integer '%s'\n",
188 } else if (val_type == CFG_VAR_STR) {
189 if (str2sint((str *)val, &i)) {
190 LOG(L_ERR, "ERROR: convert_val(): "
191 "cannot convert string to integer '%.*s'\n",
192 ((str *)val)->len, ((str *)val)->s);
195 *new_val = (void *)(long)i;
200 case CFG_INPUT_STRING:
201 if (val_type == CFG_VAR_INT) {
202 buf[snprintf(buf, sizeof(buf)-1, "%ld", (long)val)] = '\0';
206 } else if (val_type == CFG_VAR_STRING) {
210 } else if (val_type == CFG_VAR_STR) {
211 /* the value may not be zero-terminated, thus,
212 a new variable has to be allocated with larger memory space */
213 if (temp_string) pkg_free(temp_string);
214 temp_string = (char *)pkg_malloc(sizeof(char) * (((str *)val)->len + 1));
216 LOG(L_ERR, "ERROR: convert_val(): not enough memory\n");
219 memcpy(temp_string, ((str *)val)->s, ((str *)val)->len);
220 temp_string[((str *)val)->len] = '\0';
221 *new_val = (void *)temp_string;
228 if (val_type == CFG_VAR_INT) {
229 s.len = snprintf(buf, sizeof(buf)-1, "%ld", (long)val);
232 *new_val = (void *)&s;
235 } else if (val_type == CFG_VAR_STRING) {
238 *new_val = (void *)&s;
241 } else if (val_type == CFG_VAR_STR) {
251 LOG(L_ERR, "ERROR: convert_val(): got a value with type %u, but expected %u\n",
252 val_type, CFG_INPUT_MASK(var_type));
256 #define convert_val_cleanup() \
259 pkg_free(temp_string); \
260 temp_string = NULL; \
264 /* sets the value of a variable without the need of commit
269 * 1: variable has not been found
271 int cfg_set_now(cfg_ctx_t *ctx, str *group_name, str *var_name,
272 void *val, unsigned int val_type)
277 cfg_block_t *block = NULL;
279 char *old_string = NULL;
280 char **replaced = NULL;
281 cfg_child_cb_t *child_cb = NULL;
284 /* verify the context even if we do not need it now
285 to make sure that a cfg driver has called the function
286 (very very weak security) */
288 LOG(L_ERR, "ERROR: cfg_set_now(): context is undefined\n");
292 /* look-up the group and the variable */
293 if (cfg_lookup_var(group_name, var_name, &group, &var))
296 /* check whether we have to convert the type */
297 if (convert_val(val_type, val, CFG_INPUT_TYPE(var), &v))
300 if (var->def->on_change_cb) {
301 /* Call the fixup function.
302 There is no need to set a temporary cfg handle,
303 becaue a single variable is changed */
304 if (var->def->on_change_cb(*(group->handle),
307 LOG(L_ERR, "ERROR: cfg_set_now(): fixup failed\n");
311 } else if ((CFG_VAR_TYPE(var) == CFG_VAR_INT)
312 && (var->def->min != var->def->max)) {
313 /* perform a simple min-max check for integers */
314 if (((int)(long)v < var->def->min)
315 || ((int)(long)v > var->def->max)) {
316 LOG(L_ERR, "ERROR: cfg_set_now(): integer value is out of range\n");
322 if (var->def->on_set_child_cb) {
323 child_cb = cfg_child_cb_new(var_name,
324 var->def->on_set_child_cb);
326 LOG(L_ERR, "ERROR: cfg_set_now(): not enough shm memory\n");
331 /* make sure that nobody else replaces the global config
332 while the new one is prepared */
335 /* clone the memory block, and prepare the modification */
336 if (!(block = cfg_clone_global())) goto error;
338 p = block->vars+group->offset+var->offset;
340 /* we are allowed to rewrite the value on-the-fly
341 The handle either points to group->vars, or to the
342 shared memory block (dynamic group) */
343 p = *(group->handle) + var->offset;
346 /* set the new value */
347 switch (CFG_VAR_TYPE(var)) {
350 memcpy(p, &i, sizeof(int));
354 /* clone the string to shm mem */
357 if (!(s.s = cfg_clone_str(s))) goto error;
358 memcpy(&old_string, p, sizeof(char *));
359 memcpy(p, &s.s, sizeof(char *));
363 /* clone the string to shm mem */
365 if (!(s.s = cfg_clone_str(s))) goto error;
366 memcpy(&old_string, p, sizeof(char *));
367 memcpy(p, &s, sizeof(str));
370 case CFG_VAR_POINTER:
371 memcpy(p, &v, sizeof(void *));
378 /* prepare the array of the replaced strings,
379 they will be freed when the old block is freed */
380 replaced = (char **)shm_malloc(sizeof(char *)*2);
382 LOG(L_ERR, "ERROR: cfg_set_now(): not enough shm memory\n");
385 replaced[0] = old_string;
388 /* replace the global config with the new one */
389 cfg_install_global(block, replaced, child_cb, child_cb);
392 /* cfg_set() may be called more than once before forking */
393 if (old_string && (var->flag & cfg_var_shmized))
394 shm_free(old_string);
396 /* flag the variable because there is no need
397 to shmize it again */
398 var->flag |= cfg_var_shmized;
401 if (val_type == CFG_VAR_INT)
402 LOG(L_INFO, "INFO: cfg_set_now(): %.*s.%.*s "
403 "has been changed to %d\n",
404 group_name->len, group_name->s,
405 var_name->len, var_name->s,
408 else if (val_type == CFG_VAR_STRING)
409 LOG(L_INFO, "INFO: cfg_set_now(): %.*s.%.*s "
410 "has been changed to \"%s\"\n",
411 group_name->len, group_name->s,
412 var_name->len, var_name->s,
416 LOG(L_INFO, "INFO: cfg_set_now(): %.*s.%.*s "
417 "has been changed to \"%.*s\"\n",
418 group_name->len, group_name->s,
419 var_name->len, var_name->s,
420 ((str *)val)->len, ((str *)val)->s);
422 convert_val_cleanup();
426 if (cfg_shmized) CFG_WRITER_UNLOCK();
427 if (block) cfg_block_free(block);
428 if (child_cb) cfg_child_cb_free(child_cb);
431 LOG(L_ERR, "ERROR: cfg_set_now(): failed to set the variable: %.*s.%.*s\n",
432 group_name->len, group_name->s,
433 var_name->len, var_name->s);
436 convert_val_cleanup();
440 /* wrapper function for cfg_set_now */
441 int cfg_set_now_int(cfg_ctx_t *ctx, str *group_name, str *var_name, int val)
443 return cfg_set_now(ctx, group_name, var_name, (void *)(long)val, CFG_VAR_INT);
446 /* wrapper function for cfg_set_now */
447 int cfg_set_now_string(cfg_ctx_t *ctx, str *group_name, str *var_name, char *val)
449 return cfg_set_now(ctx, group_name, var_name, (void *)val, CFG_VAR_STRING);
452 /* wrapper function for cfg_set_now */
453 int cfg_set_now_str(cfg_ctx_t *ctx, str *group_name, str *var_name, str *val)
455 return cfg_set_now(ctx, group_name, var_name, (void *)val, CFG_VAR_STR);
458 /* returns the size of the variable */
459 static int cfg_var_size(cfg_mapping_t *var)
461 switch (CFG_VAR_TYPE(var)) {
467 return sizeof(char *);
472 case CFG_VAR_POINTER:
473 return sizeof(void *);
476 LOG(L_CRIT, "BUG: cfg_var_sizeK(): unknown type: %u\n",
482 /* sets the value of a variable but does not commit the change
487 * 1: variable has not been found
489 int cfg_set_delayed(cfg_ctx_t *ctx, str *group_name, str *var_name,
490 void *val, unsigned int val_type)
496 int temp_handle_created;
497 cfg_changed_var_t *changed = NULL;
502 /* the cfg has not been shmized yet, there is no
503 point in registering the change and committing it later */
504 return cfg_set_now(ctx, group_name, var_name,
508 LOG(L_ERR, "ERROR: cfg_set_delayed(): context is undefined\n");
512 /* look-up the group and the variable */
513 if (cfg_lookup_var(group_name, var_name, &group, &var))
516 /* check whether we have to convert the type */
517 if (convert_val(val_type, val, CFG_INPUT_TYPE(var), &v))
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);
568 } else if ((CFG_VAR_TYPE(var) == CFG_VAR_INT)
569 && (var->def->min != var->def->max)) {
570 /* perform a simple min-max check for integers */
571 if (((int)(long)v < var->def->min)
572 || ((int)(long)v > var->def->max)) {
573 LOG(L_ERR, "ERROR: cfg_set_delayed(): integer value is out of range\n");
578 /* everything went ok, we can add the new value to the list */
579 size = sizeof(cfg_changed_var_t) + cfg_var_size(var) - 1;
580 changed = (cfg_changed_var_t *)shm_malloc(size);
582 LOG(L_ERR, "ERROR: cfg_set_delayed(): not enough shm memory\n");
585 memset(changed, 0, size);
586 changed->group = group;
589 switch (CFG_VAR_TYPE(var)) {
593 memcpy(changed->new_val, &i, sizeof(int));
597 /* clone the string to shm mem */
600 if (!(s.s = cfg_clone_str(s))) goto error;
601 memcpy(changed->new_val, &s.s, sizeof(char *));
605 /* clone the string to shm mem */
607 if (!(s.s = cfg_clone_str(s))) goto error;
608 memcpy(changed->new_val, &s, sizeof(str));
611 case CFG_VAR_POINTER:
612 memcpy(changed->new_val, &v, sizeof(void *));
617 /* Add the new item to the end of the linked list,
618 The commit will go though the list from the first item,
619 so the list is kept in order */
620 if (ctx->changed_first)
621 ctx->changed_last->next = changed;
623 ctx->changed_first = changed;
625 ctx->changed_last = changed;
629 if (val_type == CFG_VAR_INT)
630 LOG(L_INFO, "INFO: cfg_set_delayed(): %.*s.%.*s "
631 "is going to be changed to %d "
633 group_name->len, group_name->s,
634 var_name->len, var_name->s,
638 else if (val_type == CFG_VAR_STRING)
639 LOG(L_INFO, "INFO: cfg_set_delayed(): %.*s.%.*s "
640 "is going to be changed to \"%s\" "
642 group_name->len, group_name->s,
643 var_name->len, var_name->s,
648 LOG(L_INFO, "INFO: cfg_set_delayed(): %.*s.%.*s "
649 "is going to be changed to \"%.*s\" "
651 group_name->len, group_name->s,
652 var_name->len, var_name->s,
653 ((str *)val)->len, ((str *)val)->s,
656 convert_val_cleanup();
661 if (changed) shm_free(changed);
663 LOG(L_ERR, "ERROR: cfg_set_delayed(): failed to set the variable: %.*s.%.*s\n",
664 group_name->len, group_name->s,
665 var_name->len, var_name->s);
667 convert_val_cleanup();
671 /* wrapper function for cfg_set_delayed */
672 int cfg_set_delayed_int(cfg_ctx_t *ctx, str *group_name, str *var_name, int val)
674 return cfg_set_delayed(ctx, group_name, var_name, (void *)(long)val, CFG_VAR_INT);
677 /* wrapper function for cfg_set_delayed */
678 int cfg_set_delayed_string(cfg_ctx_t *ctx, str *group_name, str *var_name, char *val)
680 return cfg_set_delayed(ctx, group_name, var_name, (void *)val, CFG_VAR_STRING);
683 /* wrapper function for cfg_set_delayed */
684 int cfg_set_delayed_str(cfg_ctx_t *ctx, str *group_name, str *var_name, str *val)
686 return cfg_set_delayed(ctx, group_name, var_name, (void *)val, CFG_VAR_STR);
689 /* commits the previously prepared changes within the context */
690 int cfg_commit(cfg_ctx_t *ctx)
692 int replaced_num = 0;
693 cfg_changed_var_t *changed, *changed2;
695 char **replaced = NULL;
696 cfg_child_cb_t *child_cb;
697 cfg_child_cb_t *child_cb_first = NULL;
698 cfg_child_cb_t *child_cb_last = NULL;
704 LOG(L_ERR, "ERROR: cfg_commit(): context is undefined\n");
708 if (!cfg_shmized) return 0; /* nothing to do */
710 /* the ctx must be locked while reading and writing
711 the list of changed variables */
714 /* is there any change? */
715 if (!ctx->changed_first) goto done;
717 /* count the number of replaced strings,
718 and prepare the linked list of per-child process
719 callbacks, that will be added to the global list */
720 for ( changed = ctx->changed_first;
722 changed = changed->next
724 if ((CFG_VAR_TYPE(changed->var) == CFG_VAR_STRING)
725 || (CFG_VAR_TYPE(changed->var) == CFG_VAR_STR))
729 if (changed->var->def->on_set_child_cb) {
730 s.s = changed->var->def->name;
731 s.len = changed->var->name_len;
732 child_cb = cfg_child_cb_new(&s,
733 changed->var->def->on_set_child_cb);
734 if (!child_cb) goto error0;
737 child_cb_last->next = child_cb;
739 child_cb_first = child_cb;
740 child_cb_last = child_cb;
744 /* allocate memory for the replaced string array */
745 size = sizeof(char *)*(replaced_num + 1);
746 replaced = (char **)shm_malloc(size);
748 LOG(L_ERR, "ERROR: cfg_commit(): not enough shm memory\n");
751 memset(replaced, 0 , size);
753 /* make sure that nobody else replaces the global config
754 while the new one is prepared */
757 /* clone the memory block, and prepare the modification */
758 if (!(block = cfg_clone_global())) {
763 /* apply the modifications to the buffer */
765 for ( changed = ctx->changed_first;
767 changed = changed->next
770 + changed->group->offset
771 + changed->var->offset;
773 if ((CFG_VAR_TYPE(changed->var) == CFG_VAR_STRING)
774 || (CFG_VAR_TYPE(changed->var) == CFG_VAR_STR)) {
775 memcpy(&(replaced[replaced_num]), p, sizeof(char *));
781 cfg_var_size(changed->var));
784 /* replace the global config with the new one */
785 cfg_install_global(block, replaced, child_cb_first, child_cb_last);
788 /* free the changed list */
789 for ( changed = ctx->changed_first;
793 changed2 = changed->next;
796 ctx->changed_first = NULL;
797 ctx->changed_last = NULL;
800 LOG(L_INFO, "INFO: cfg_commit(): config changes have been applied "
812 if (child_cb_first) cfg_child_cb_free(child_cb_first);
813 if (replaced) shm_free(replaced);
818 /* drops the not yet committed changes within the context */
819 int cfg_rollback(cfg_ctx_t *ctx)
821 cfg_changed_var_t *changed, *changed2;
825 LOG(L_ERR, "ERROR: cfg_rollback(): context is undefined\n");
829 if (!cfg_shmized) return 0; /* nothing to do */
831 LOG(L_INFO, "INFO: cfg_rollback(): deleting the config changes "
835 /* the ctx must be locked while reading and writing
836 the list of changed variables */
839 for ( changed = ctx->changed_first;
843 changed2 = changed->next;
845 if ((CFG_VAR_TYPE(changed->var) == CFG_VAR_STRING)
846 || (CFG_VAR_TYPE(changed->var) == CFG_VAR_STR)) {
847 memcpy(&new_string, changed->new_val, sizeof(char *));
848 shm_free(new_string);
852 ctx->changed_first = NULL;
853 ctx->changed_last = NULL;
860 /* returns the value of a variable */
861 int cfg_get_by_name(cfg_ctx_t *ctx, str *group_name, str *var_name,
862 void **val, unsigned int *val_type)
867 static str s; /* we need the value even
868 after the function returns */
872 /* verify the context even if we do not need it now
873 to make sure that a cfg driver has called the function
874 (very very weak security) */
876 LOG(L_ERR, "ERROR: cfg_get_by_name(): context is undefined\n");
880 /* look-up the group and the variable */
881 if (cfg_lookup_var(group_name, var_name, &group, &var))
884 if (var->def->on_change_cb) {
885 /* The variable cannot be retrieved, because the fixup
886 function may have changed it, and it is better to return
887 an error than an incorrect value */
891 /* use the module's handle to access the variable
892 It means that the variable is read from the local config
894 p = *(group->handle) + var->offset;
896 switch (CFG_VAR_TYPE(var)) {
898 memcpy(&i, p, sizeof(int));
899 *val = (void *)(long)i;
903 memcpy(&ch, p, sizeof(char *));
908 memcpy(&s, p, sizeof(str));
912 case CFG_VAR_POINTER:
913 memcpy(val, &p, sizeof(void *));
917 *val_type = CFG_VAR_TYPE(var);
922 /* returns the description of a variable */
923 int cfg_help(cfg_ctx_t *ctx, str *group_name, str *var_name,
924 char **ch, unsigned int *input_type)
928 /* verify the context even if we do not need it now
929 to make sure that a cfg driver has called the function
930 (very very weak security) */
932 LOG(L_ERR, "ERROR: cfg_help(): context is undefined\n");
936 /* look-up the group and the variable */
937 if (cfg_lookup_var(group_name, var_name, NULL, &var))
940 *ch = var->def->descr;
942 *input_type = CFG_INPUT_TYPE(var);
946 /* return the group name and the cfg structure definition,
947 * and moves the handle to the next group
952 int cfg_get_group_next(void **h,
953 str *gname, cfg_def_t **def)
957 group = (cfg_group_t *)(*h);
958 if (group == NULL) return 0;
960 gname->s = group->name;
961 gname->len = group->name_len;
962 (*def) = group->mapping->def;
964 (*h) = (void *)group->next;
968 /* Initialize the handle for cfg_diff_next() */
969 int cfg_diff_init(cfg_ctx_t *ctx,
973 LOG(L_ERR, "ERROR: cfg_diff_init(): context is undefined\n");
978 (*h) = (void *)ctx->changed_first;
983 /* return the pending changes that have not been
986 int cfg_diff_next(void **h,
987 str *gname, str *vname,
988 void **old_val, void **new_val,
989 unsigned int *val_type)
991 cfg_changed_var_t *changed;
993 static str old_s, new_s; /* we need the value even
994 after the function returns */
998 changed = (cfg_changed_var_t *)(*h);
999 if (changed == NULL) return 0;
1001 gname->s = changed->group->name;
1002 gname->len = changed->group->name_len;
1003 vname->s = changed->var->def->name;
1004 vname->len = changed->var->name_len;
1006 /* use the module's handle to access the variable
1007 It means that the variable is read from the local config
1009 p = *(changed->group->handle) + changed->var->offset;
1011 switch (CFG_VAR_TYPE(changed->var)) {
1013 memcpy(&i, p, sizeof(int));
1014 *old_val = (void *)(long)i;
1015 memcpy(&i, changed->new_val, sizeof(int));
1016 *new_val = (void *)(long)i;
1019 case CFG_VAR_STRING:
1020 memcpy(&ch, p, sizeof(char *));
1021 *old_val = (void *)ch;
1022 memcpy(&ch, changed->new_val, sizeof(char *));
1023 *new_val = (void *)ch;
1027 memcpy(&old_s, p, sizeof(str));
1028 *old_val = (void *)&old_s;
1029 memcpy(&new_s, changed->new_val, sizeof(str));
1030 *new_val = (void *)&new_s;
1033 case CFG_VAR_POINTER:
1034 memcpy(old_val, &p, sizeof(void *));
1035 memcpy(new_val, &changed->new_val, sizeof(void *));
1039 *val_type = CFG_VAR_TYPE(changed->var);
1041 (*h) = (void *)changed->next;
1045 /* release the handle of cfg_diff_next() */
1046 void cfg_diff_release(cfg_ctx_t *ctx)
1049 LOG(L_ERR, "ERROR: cfg_diff_release(): context is undefined\n");
1053 CFG_CTX_UNLOCK(ctx);