/* copy the variables to shm mem */ int cfg_shmize(void) { cfg_group_t *group; cfg_block_t *block = NULL; int size; if (!cfg_group) return 0; /* Let us allocate one memory block that will contain all the variables */ for ( size=0, group = cfg_group; group; group=group->next ) { size = ROUND_POINTER(size); group->offset = size; size += group->size; } block = (cfg_block_t*)shm_malloc(sizeof(cfg_block_t)+size-1); if (!block) { LOG(L_ERR, "ERROR: cfg_clone_str(): not enough shm memory\n"); goto error; } memset(block, 0, sizeof(cfg_block_t)+size-1); cfg_block_size = size; /* copy the memory fragments to the single block */ for ( group = cfg_group; group; group=group->next ) { if (group->dynamic == 0) { /* clone the strings to shm mem */ if (cfg_shmize_strings(group)) goto error; /* copy the values to the new block */ memcpy(block->vars+group->offset, group->vars, group->size); } else { /* The group was declared with NULL values, * we have to fix it up. * The fixup function takes care about the values, * it fills up the block */ if (cfg_script_fixup(group, block->vars+group->offset)) goto error; /* Notify the drivers about the new config definition. * Temporary set the group handle so that the drivers have a chance to * overwrite the default values. The handle must be reset after this * because the main process does not have a local configuration. */ *(group->handle) = block->vars+group->offset; cfg_notify_drivers(group->name, group->name_len, group->mapping->def); *(group->handle) = NULL; } } /* try to fixup the selects that failed to be fixed-up previously */ if (cfg_fixup_selects()) goto error; /* install the new config */ cfg_install_global(block, NULL, NULL, NULL); cfg_shmized = 1; return 0; error: if (block) shm_free(block); return -1; }
/* commits the previously prepared changes within the context */ int cfg_commit(cfg_ctx_t *ctx) { int replaced_num = 0; cfg_changed_var_t *changed, *changed2; cfg_block_t *block; char **replaced = NULL; cfg_child_cb_t *child_cb; cfg_child_cb_t *child_cb_first = NULL; cfg_child_cb_t *child_cb_last = NULL; int size; void *p; str s, s2; if (!ctx) { LOG(L_ERR, "ERROR: cfg_commit(): context is undefined\n"); return -1; } if (!cfg_shmized) return 0; /* nothing to do */ /* the ctx must be locked while reading and writing the list of changed variables */ CFG_CTX_LOCK(ctx); /* is there any change? */ if (!ctx->changed_first) goto done; /* count the number of replaced strings, and prepare the linked list of per-child process callbacks, that will be added to the global list */ for ( changed = ctx->changed_first; changed; changed = changed->next ) { if ((CFG_VAR_TYPE(changed->var) == CFG_VAR_STRING) || (CFG_VAR_TYPE(changed->var) == CFG_VAR_STR)) replaced_num++; if (changed->var->def->on_set_child_cb) { s.s = changed->group->name; s.len = changed->group->name_len; s2.s = changed->var->def->name; s2.len = changed->var->name_len; child_cb = cfg_child_cb_new(&s, &s2, changed->var->def->on_set_child_cb, changed->var->def->type); if (!child_cb) goto error0; if (child_cb_last) child_cb_last->next = child_cb; else child_cb_first = child_cb; child_cb_last = child_cb; } } if (replaced_num) { /* allocate memory for the replaced string array */ size = sizeof(char *)*(replaced_num + 1); replaced = (char **)shm_malloc(size); if (!replaced) { LOG(L_ERR, "ERROR: cfg_commit(): not enough shm memory\n"); goto error; } memset(replaced, 0 , size); } /* make sure that nobody else replaces the global config while the new one is prepared */ CFG_WRITER_LOCK(); /* clone the memory block, and prepare the modification */ if (!(block = cfg_clone_global())) { CFG_WRITER_UNLOCK(); goto error; } /* apply the modifications to the buffer */ replaced_num = 0; for ( changed = ctx->changed_first; changed; changed = changed->next ) { p = block->vars + changed->group->offset + changed->var->offset; if ((CFG_VAR_TYPE(changed->var) == CFG_VAR_STRING) || (CFG_VAR_TYPE(changed->var) == CFG_VAR_STR)) { replaced[replaced_num] = *(char **)p; if (replaced[replaced_num]) replaced_num++; /* else do not increase replaced_num, because the cfg_block_free() will stop at the first NULL value */ } memcpy( p, changed->new_val.vraw, cfg_var_size(changed->var)); } /* replace the global config with the new one */ cfg_install_global(block, replaced, child_cb_first, child_cb_last); CFG_WRITER_UNLOCK(); /* free the changed list */ for ( changed = ctx->changed_first; changed; changed = changed2 ) { changed2 = changed->next; shm_free(changed); } ctx->changed_first = NULL; ctx->changed_last = NULL; done: LOG(L_INFO, "INFO: cfg_commit(): config changes have been applied " "[context=%p]\n", ctx); CFG_CTX_UNLOCK(ctx); return 0; error: CFG_CTX_UNLOCK(ctx); error0: if (child_cb_first) cfg_child_cb_free(child_cb_first); if (replaced) shm_free(replaced); return -1; }
/* copy the variables to shm mem */ int cfg_shmize(void) { cfg_group_t *group; cfg_block_t *block = NULL; int size; if (!cfg_group) return 0; /* Let us allocate one memory block that * will contain all the variables + meta-data * in the following form: * |-----------| * | meta-data | <- group A: meta_offset * | variables | <- group A: var_offset * |-----------| * | meta-data | <- group B: meta_offset * | variables | <- group B: var_offset * |-----------| * | ... | * |-----------| * * The additional array for the multiple values * of the same variable is linked to the meta-data. */ for ( size=0, group = cfg_group; group; group=group->next ) { size = ROUND_POINTER(size); group->meta_offset = size; size += sizeof(cfg_group_meta_t); size = ROUND_POINTER(size); group->var_offset = size; size += group->size; } block = (cfg_block_t*)shm_malloc(sizeof(cfg_block_t)+size-1); if (!block) { LOG(L_ERR, "ERROR: cfg_shmize(): not enough shm memory\n"); goto error; } memset(block, 0, sizeof(cfg_block_t)+size-1); cfg_block_size = size; /* copy the memory fragments to the single block */ for ( group = cfg_group; group; group=group->next ) { if (group->dynamic == CFG_GROUP_STATIC) { /* clone the strings to shm mem */ if (cfg_shmize_strings(group)) goto error; /* copy the values to the new block */ memcpy(CFG_GROUP_DATA(block, group), group->vars, group->size); } else if (group->dynamic == CFG_GROUP_DYNAMIC) { /* The group was declared with NULL values, * we have to fix it up. * The fixup function takes care about the values, * it fills up the block */ if (cfg_script_fixup(group, CFG_GROUP_DATA(block, group))) goto error; /* Notify the drivers about the new config definition. * Temporary set the group handle so that the drivers have a chance to * overwrite the default values. The handle must be reset after this * because the main process does not have a local configuration. */ *(group->handle) = CFG_GROUP_DATA(block, group); cfg_notify_drivers(group->name, group->name_len, group->mapping->def); *(group->handle) = NULL; } else { LOG(L_ERR, "ERROR: cfg_shmize(): Configuration group is declared " "without any variable: %.*s\n", group->name_len, group->name); goto error; } /* Create the additional group instances with applying the temporary list. */ if (apply_add_var_list(block, group)) goto error; } /* try to fixup the selects that failed to be fixed-up previously */ if (cfg_fixup_selects()) goto error; /* install the new config */ cfg_install_global(block, NULL, NULL, NULL); cfg_shmized = 1; return 0; error: if (block) shm_free(block); return -1; }
/* sets the value of a variable without the need of commit * * return value: * 0: success * -1: error * 1: variable has not been found */ int cfg_set_now(cfg_ctx_t *ctx, str *group_name, str *var_name, void *val, unsigned int val_type) { cfg_group_t *group; cfg_mapping_t *var; void *p, *v; cfg_block_t *block = NULL; str s, s2; char *old_string = NULL; char **replaced = NULL; cfg_child_cb_t *child_cb = NULL; /* verify the context even if we do not need it now to make sure that a cfg driver has called the function (very very weak security) */ if (!ctx) { LOG(L_ERR, "ERROR: cfg_set_now(): context is undefined\n"); return -1; } /* look-up the group and the variable */ if (cfg_lookup_var(group_name, var_name, &group, &var)) return 1; /* check whether the variable is read-only */ if (var->def->type & CFG_READONLY) { LOG(L_ERR, "ERROR: cfg_set_now(): variable is read-only\n"); goto error0; } /* check whether we have to convert the type */ if (convert_val(val_type, val, CFG_INPUT_TYPE(var), &v)) goto error0; if ((CFG_INPUT_TYPE(var) == CFG_INPUT_INT) && (var->def->min || var->def->max)) { /* perform a simple min-max check for integers */ if (((int)(long)v < var->def->min) || ((int)(long)v > var->def->max)) { LOG(L_ERR, "ERROR: cfg_set_now(): integer value is out of range\n"); goto error0; } } if (var->def->on_change_cb) { /* Call the fixup function. There is no need to set a temporary cfg handle, becaue a single variable is changed */ if (var->def->on_change_cb(*(group->handle), group_name, var_name, &v) < 0) { LOG(L_ERR, "ERROR: cfg_set_now(): fixup failed\n"); goto error0; } } if (var->def->on_set_child_cb) { /* get the name of the variable from the internal struct, because var_name may be freed before the callback needs it */ s.s = group->name; s.len = group->name_len; s2.s = var->def->name; s2.len = var->name_len; child_cb = cfg_child_cb_new(&s, &s2, var->def->on_set_child_cb, var->def->type); if (!child_cb) { LOG(L_ERR, "ERROR: cfg_set_now(): not enough shm memory\n"); goto error0; } } if (cfg_shmized) { /* make sure that nobody else replaces the global config while the new one is prepared */ CFG_WRITER_LOCK(); if (var->def->type & CFG_ATOMIC) { /* atomic change is allowed, we can rewrite the value directly in the global config */ p = (*cfg_global)->vars+group->offset+var->offset; } else { /* clone the memory block, and prepare the modification */ if (!(block = cfg_clone_global())) goto error; p = block->vars+group->offset+var->offset; } } else { /* we are allowed to rewrite the value on-the-fly The handle either points to group->vars, or to the shared memory block (dynamic group) */ p = *(group->handle) + var->offset; } /* set the new value */ switch (CFG_VAR_TYPE(var)) { case CFG_VAR_INT: *(int *)p = (int)(long)v; break; case CFG_VAR_STRING: /* clone the string to shm mem */ s.s = v; s.len = (s.s) ? strlen(s.s) : 0; if (cfg_clone_str(&s, &s)) goto error; old_string = *(char **)p; *(char **)p = s.s; break; case CFG_VAR_STR: /* clone the string to shm mem */ s = *(str *)v; if (cfg_clone_str(&s, &s)) goto error; old_string = *(char **)p; memcpy(p, &s, sizeof(str)); break; case CFG_VAR_POINTER: *(void **)p = v; break; } if (cfg_shmized) { if (old_string) { /* prepare the array of the replaced strings, they will be freed when the old block is freed */ replaced = (char **)shm_malloc(sizeof(char *)*2); if (!replaced) { LOG(L_ERR, "ERROR: cfg_set_now(): not enough shm memory\n"); goto error; } replaced[0] = old_string; replaced[1] = NULL; } /* replace the global config with the new one */ if (block) cfg_install_global(block, replaced, child_cb, child_cb); CFG_WRITER_UNLOCK(); } else { /* cfg_set() may be called more than once before forking */ if (old_string && (var->flag & cfg_var_shmized)) shm_free(old_string); /* flag the variable because there is no need to shmize it again */ var->flag |= cfg_var_shmized; /* the global config does not have to be replaced, but the child callback has to be installed, otherwise the child processes will miss the change */ if (child_cb) cfg_install_child_cb(child_cb, child_cb); } if (val_type == CFG_VAR_INT) LOG(L_INFO, "INFO: cfg_set_now(): %.*s.%.*s " "has been changed to %d\n", group_name->len, group_name->s, var_name->len, var_name->s, (int)(long)val); else if (val_type == CFG_VAR_STRING) LOG(L_INFO, "INFO: cfg_set_now(): %.*s.%.*s " "has been changed to \"%s\"\n", group_name->len, group_name->s, var_name->len, var_name->s, (char *)val); else /* str type */ LOG(L_INFO, "INFO: cfg_set_now(): %.*s.%.*s " "has been changed to \"%.*s\"\n", group_name->len, group_name->s, var_name->len, var_name->s, ((str *)val)->len, ((str *)val)->s); convert_val_cleanup(); return 0; error: if (cfg_shmized) CFG_WRITER_UNLOCK(); if (block) cfg_block_free(block); if (child_cb) cfg_child_cb_free(child_cb); error0: LOG(L_ERR, "ERROR: cfg_set_now(): failed to set the variable: %.*s.%.*s\n", group_name->len, group_name->s, var_name->len, var_name->s); convert_val_cleanup(); return -1; }