/* wrapper function for read_cfg_var() -- convert the value to integer * returns -1 on error, 0 on success */ int read_cfg_var_int(struct cfg_read_handle *read_handle, int *val) { unsigned int type; void *v1=NULL, *v2=NULL; if ((type = read_cfg_var(read_handle, &v1)) == 0) return -1; if (convert_val(type, v1, CFG_INPUT_INT, &v2)) return -1; *val = (int)(long)(v2); return 0; }
/* wrapper function for read_cfg_var() -- convert the value to str * returns -1 on error, 0 on success */ int read_cfg_var_str(struct cfg_read_handle *read_handle, str *val) { unsigned int type; void *v1=NULL, *v2=NULL; if ((type = read_cfg_var(read_handle, &v1)) == 0) return -1; if (convert_val(type, v1, CFG_INPUT_STR, &v2)) return -1; *val = *(str *)(v2); return 0; }
/* * Top level: setup the jmp buffer then call the recursive function */ term_t convert_value(val_converter_t *convert, value_t v) { term_t t; t = setjmp(convert->env); if (t == 0) { t = convert_val(convert, v); } else { // exception assert(t < 0); reset_istack(&convert->stack); } return t; }
/* * Convert a tuple */ static term_t convert_tuple(val_converter_t *convert, value_tuple_t *tup) { uint32_t i, n; term_t *a; term_t t; n = tup->nelems; a = alloc_istack_array(&convert->stack, n); for (i=0; i<n; i++) { a[i] = convert_val(convert, tup->elem[i]); } t = tuple_term(convert->terms, n, a); free_istack_array(&convert->stack, a); return t; }
/* Rewrite the value of an already declared script variable before forking. * Return value: * 0: success * -1: error * 1: variable not found */ int cfg_set_script_var(cfg_group_t *group, str *var_name, void *val, unsigned int val_type) { cfg_script_var_t *var; void *v; str s; if (cfg_shmized || (group->dynamic != CFG_GROUP_DYNAMIC)) { LOG(L_ERR, "BUG: cfg_set_script_var(): Not a dynamic group before forking\n"); return -1; } for ( var = (cfg_script_var_t *)(void *)group->vars; var; var = var->next ) { if ((var->name_len == var_name->len) && (memcmp(var->name, var_name->s, var_name->len) == 0) ) { switch (var->type) { case CFG_VAR_INT: if (convert_val(val_type, val, CFG_INPUT_INT, &v)) goto error; if ((var->min || var->max) && ((var->min > (int)(long)v) || (var->max < (int)(long)v)) ) { LOG(L_ERR, "ERROR: cfg_set_script_var(): integer value is out of range\n"); goto error; } var->val.i = (int)(long)v; break; case CFG_VAR_STR: if (convert_val(val_type, val, CFG_INPUT_STR, &v)) goto error; if (((str *)v)->s) { s.len = ((str *)v)->len; s.s = pkg_malloc(sizeof(char) * (s.len + 1)); if (!s.s) { LOG(L_ERR, "ERROR: cfg_set_script_var(): not enough memory\n"); goto error; } memcpy(s.s, ((str *)v)->s, s.len); s.s[s.len] = '\0'; } else { s.s = NULL; s.len = 0; } if (var->val.s.s) pkg_free(var->val.s.s); var->val.s = s; break; default: LOG(L_ERR, "ERROR: cfg_set_script_var(): unsupported variable type\n"); goto error; } convert_val_cleanup(); return 0; } } return 1; error: LOG(L_ERR, "ERROR: cfg_set_script_var(): failed to set the script variable: %.*s.%.*s\n", group->name_len, group->name, var_name->len, var_name->s); return -1; }
/* sets the value of a variable but does not commit the change * * return value: * 0: success * -1: error * 1: variable has not been found */ int cfg_set_delayed(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 *v; char *temp_handle; int temp_handle_created; cfg_changed_var_t *changed = NULL; int size; str s; if (!cfg_shmized) /* the cfg has not been shmized yet, there is no point in registering the change and committing it later */ return cfg_set_now(ctx, group_name, var_name, val, val_type); if (!ctx) { LOG(L_ERR, "ERROR: cfg_set_delayed(): 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_delayed(): 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_delayed(): integer value is out of range\n"); goto error0; } } /* the ctx must be locked while reading and writing the list of changed variables */ CFG_CTX_LOCK(ctx); if (var->def->on_change_cb) { /* The fixup function must see also the not yet committed values, so a temporary handle must be prepared that points to the new config. Only the values within the group are applied, other modifications are not visible to the callback. The local config is the base. */ if (ctx->changed_first) { temp_handle = (char *)pkg_malloc(group->size); if (!temp_handle) { LOG(L_ERR, "ERROR: cfg_set_delayed(): " "not enough memory\n"); goto error; } temp_handle_created = 1; memcpy(temp_handle, *(group->handle), group->size); /* apply the changes */ for ( changed = ctx->changed_first; changed; changed = changed->next ) { if (changed->group != group) continue; memcpy( temp_handle + changed->var->offset, changed->new_val.vraw, cfg_var_size(changed->var)); } } else { /* there is not any change */ temp_handle = *(group->handle); temp_handle_created = 0; } if (var->def->on_change_cb(temp_handle, group_name, var_name, &v) < 0) { LOG(L_ERR, "ERROR: cfg_set_delayed(): fixup failed\n"); if (temp_handle_created) pkg_free(temp_handle); goto error; } if (temp_handle_created) pkg_free(temp_handle); } /* everything went ok, we can add the new value to the list */ size = sizeof(cfg_changed_var_t) - sizeof(((cfg_changed_var_t*)0)->new_val) + cfg_var_size(var); changed = (cfg_changed_var_t *)shm_malloc(size); if (!changed) { LOG(L_ERR, "ERROR: cfg_set_delayed(): not enough shm memory\n"); goto error; } memset(changed, 0, size); changed->group = group; changed->var = var; switch (CFG_VAR_TYPE(var)) { case CFG_VAR_INT: changed->new_val.vint = (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; changed->new_val.vp = s.s; break; case CFG_VAR_STR: /* clone the string to shm mem */ s = *(str *)v; if (cfg_clone_str(&s, &s)) goto error; changed->new_val.vstr=s; break; case CFG_VAR_POINTER: changed->new_val.vp=v; break; } /* Add the new item to the end of the linked list, The commit will go though the list from the first item, so the list is kept in order */ if (ctx->changed_first) ctx->changed_last->next = changed; else ctx->changed_first = changed; ctx->changed_last = changed; CFG_CTX_UNLOCK(ctx); if (val_type == CFG_VAR_INT) LOG(L_INFO, "INFO: cfg_set_delayed(): %.*s.%.*s " "is going to be changed to %d " "[context=%p]\n", group_name->len, group_name->s, var_name->len, var_name->s, (int)(long)val, ctx); else if (val_type == CFG_VAR_STRING) LOG(L_INFO, "INFO: cfg_set_delayed(): %.*s.%.*s " "is going to be changed to \"%s\" " "[context=%p]\n", group_name->len, group_name->s, var_name->len, var_name->s, (char *)val, ctx); else /* str type */ LOG(L_INFO, "INFO: cfg_set_delayed(): %.*s.%.*s " "is going to be changed to \"%.*s\" " "[context=%p]\n", group_name->len, group_name->s, var_name->len, var_name->s, ((str *)val)->len, ((str *)val)->s, ctx); convert_val_cleanup(); return 0; error: CFG_CTX_UNLOCK(ctx); if (changed) shm_free(changed); error0: LOG(L_ERR, "ERROR: cfg_set_delayed(): 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; }
/* 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; }