/* installs a new global config * * replaced is an array of strings that must be freed together * with the previous global config. * cb_first and cb_last define a linked list of per-child process * callbacks. This list is added to the global linked list. */ void cfg_install_global(cfg_block_t *block, void **replaced, cfg_child_cb_t *cb_first, cfg_child_cb_t *cb_last) { cfg_block_t* old_cfg; CFG_REF(block); if (replaced) { /* The replaced array is specified, it has to be linked to the child cb structure. * The last child process processing this structure will free the old strings and the array. */ if (cb_first) { cb_first->replaced = replaced; } else { /* At least one child cb structure is needed. */ cb_first = cfg_child_cb_new(NULL, NULL, NULL, 0 /* gname, name, cb, type */); if (cb_first) { cb_last = cb_first; cb_first->replaced = replaced; } else { LOG(L_ERR, "ERROR: cfg_install_global(): not enough shm memory\n"); /* Nothing more can be done here, the replaced strings are still needed, * they cannot be freed at this moment. */ } } } CFG_LOCK(); old_cfg = *cfg_global; *cfg_global = block; if (cb_first) cfg_install_child_cb(cb_first, cb_last); CFG_UNLOCK(); if (old_cfg) CFG_UNREF(old_cfg); }
/* installs a new global config * * replaced is an array of strings that must be freed together * with the previous global config. * cb_first and cb_last define a linked list of per-child process * callbacks. This list is added to the global linked list. */ void cfg_install_global(cfg_block_t *block, char **replaced, cfg_child_cb_t *cb_first, cfg_child_cb_t *cb_last) { cfg_block_t* old_cfg; CFG_REF(block); CFG_LOCK(); old_cfg = *cfg_global; *cfg_global = block; if (cb_first) cfg_install_child_cb(cb_first, cb_last); CFG_UNLOCK(); if (old_cfg) { if (replaced) (old_cfg)->replaced = replaced; CFG_UNREF(old_cfg); } }
/* 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; }