You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
637 lines
17 KiB
C
637 lines
17 KiB
C
/*
|
|
* Yet another daemon library especially designed to be used
|
|
* with libsxmp based daemons.
|
|
*
|
|
* (c) Alexander Vdolainen 2016 <avdolainen@zoho.com>
|
|
*
|
|
* libsxmp is free software: you can redistribute it and/or modify it
|
|
* under the terms of the GNU Lesser General Public License as published
|
|
* by the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* libsxmp is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
* See the GNU Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.";
|
|
*
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <pthread.h>
|
|
#include <ctype.h>
|
|
|
|
#include <tdata/usrtc.h>
|
|
#include <sexpr/sexp.h>
|
|
|
|
#include <ydaemon/ydaemon.h>
|
|
|
|
#define _MAX_GROUPS 256
|
|
#define _MAX_VALUES 4096
|
|
|
|
/*
|
|
* TODO:
|
|
* - Optimize where it possible
|
|
*/
|
|
|
|
static long __cmp_strs(const void *a, const void *b)
|
|
{
|
|
return strcmp((const char *)a, (const char *)b);
|
|
}
|
|
|
|
static void __destroy_val(ydc_conf_val_t *val)
|
|
{
|
|
if(val->on_eventv != NULL)
|
|
val->on_eventv(val, NULL, NULL, YC_EVENT_DESTROY);
|
|
|
|
/* ok, let's begin to free all memory */
|
|
if(val->type == SEXPR) sexp_t_deallocate((sexp_t *)val->priv);
|
|
|
|
free(val->value);
|
|
free(val->name);
|
|
|
|
return;
|
|
}
|
|
|
|
static int __ydc_conf_get_val_f(ydc_conf_t *c, const char *phvar, ydc_conf_val_t **rval,
|
|
ydc_pgroup_t **rgrp)
|
|
{
|
|
int err = 0;
|
|
int len = 0;
|
|
usrtc_t *ptree = &(c->p_groups), *tree = NULL;
|
|
usrtc_node_t *node;
|
|
char *group_name = NULL, *var_name = NULL;
|
|
ydc_conf_val_t *val = NULL;
|
|
ydc_pgroup_t *pgroup = NULL;
|
|
|
|
/* ok, first try to understand what to do */
|
|
if(!(var_name = strchr(phvar, '/'))) {
|
|
err = EINVAL;
|
|
goto __failure;
|
|
} else {
|
|
len = strlen(phvar) - strlen(var_name);
|
|
if(!(group_name = strndup(phvar, len))) {
|
|
err = ENOMEM;
|
|
goto __failure;
|
|
}
|
|
if(!(node = usrtc_lookup(ptree, (const void *)group_name))) {
|
|
err = ENOENT;
|
|
goto __failure;
|
|
} else {
|
|
pgroup = (ydc_pgroup_t *)usrtc_node_getdata(node);
|
|
pthread_rwlock_rdlock(&(pgroup->val_lock));
|
|
}
|
|
}
|
|
free(group_name);
|
|
/* we should know the value name now */
|
|
var_name += sizeof(char);
|
|
|
|
tree = &(pgroup->values);
|
|
if(!(node = usrtc_lookup(tree, (const void *)var_name))) {
|
|
err = ENOENT;
|
|
goto __failure_1;
|
|
}
|
|
val = (ydc_conf_val_t *)usrtc_node_getdata(node);
|
|
|
|
/* fill this */
|
|
if(rval) *rval = val;
|
|
if(rgrp) *rgrp = pgroup;
|
|
|
|
__failure_1:
|
|
pthread_rwlock_unlock(&(pgroup->val_lock));
|
|
__failure:
|
|
return err;
|
|
}
|
|
|
|
int ydc_conf_init(ydc_conf_t *c)
|
|
{
|
|
int err = ENOMEM;
|
|
usrtc_t *tree = &(c->p_groups);
|
|
|
|
/* zero all to keep it simple as possible */
|
|
memset(c, 0, sizeof(ydc_conf_t));
|
|
/* init locking */
|
|
if(pthread_rwlock_init(&(c->grp_lock), NULL)) goto __fail_0;
|
|
if(pthread_mutex_init(&(c->conf_lock), NULL)) goto __fail_0_1;
|
|
|
|
/* initialize tree structure */
|
|
usrtc_init(tree, USRTC_SPLAY, _MAX_GROUPS, __cmp_strs);
|
|
|
|
return 0;
|
|
|
|
__fail_0_1:
|
|
pthread_rwlock_destroy(&(c->grp_lock));
|
|
__fail_0:
|
|
return err;
|
|
}
|
|
|
|
int ydc_conf_create_pgroup(ydc_conf_t *c, char *nm)
|
|
{
|
|
int err = ENOMEM;
|
|
ydc_pgroup_t *pg = malloc(sizeof(ydc_pgroup_t));
|
|
usrtc_t *tree = NULL, *ptree = &(c->p_groups);
|
|
usrtc_node_t *node = NULL;
|
|
|
|
/* initialize */
|
|
if(!pg) {
|
|
err = ENOMEM;
|
|
goto __failure;
|
|
} else if(!(pg->name = strdup(nm))){
|
|
err = ENOMEM;
|
|
goto __failure_1;
|
|
}
|
|
/* check availability */
|
|
/* from here we will lock conf */
|
|
pthread_rwlock_wrlock(&(c->grp_lock));
|
|
if((node = usrtc_lookup(ptree, (const void *)nm))) {
|
|
err = EEXIST;
|
|
goto __failure_2;
|
|
}
|
|
if(usrtc_count(ptree) >= _MAX_GROUPS) {
|
|
err = ENOMEM;
|
|
goto __failure_2;
|
|
}
|
|
/* init rwlock */
|
|
if(pthread_rwlock_init(&(pg->val_lock), NULL)) {
|
|
err = ENOMEM;
|
|
goto __failure_2;
|
|
}
|
|
|
|
/* init tree structures */
|
|
tree = &(pg->values);
|
|
node = &(pg->unode);
|
|
usrtc_init(tree, USRTC_SPLAY, _MAX_VALUES, __cmp_strs);
|
|
usrtc_node_init(node, pg);
|
|
/* insert */
|
|
usrtc_insert(ptree, node, pg->name);
|
|
|
|
pthread_rwlock_unlock(&(c->grp_lock));
|
|
|
|
return 0;
|
|
|
|
__failure_2:
|
|
pthread_rwlock_unlock(&(c->grp_lock));
|
|
__failure_1:
|
|
free(pg);
|
|
__failure:
|
|
return err;
|
|
}
|
|
|
|
int ydc_conf_destroy_pgroup(ydc_conf_t *c, char *nm, int force)
|
|
{
|
|
int err = 0;
|
|
usrtc_t *ptree = &(c->p_groups), *tree;
|
|
usrtc_node_t *node = NULL, *nn = NULL;
|
|
ydc_pgroup_t *pg = NULL;
|
|
ydc_conf_val_t *val = NULL;
|
|
|
|
/* take an rwlock for write due to the our sirection to write somethings */
|
|
pthread_rwlock_wrlock(&(c->grp_lock));
|
|
node = usrtc_lookup(ptree, (const void *)nm);
|
|
if(!node) {
|
|
pthread_rwlock_unlock(&(c->grp_lock));
|
|
return ENOENT;
|
|
} else {
|
|
pg = (ydc_pgroup_t *)usrtc_node_getdata(node);
|
|
tree = &(pg->values);
|
|
pthread_rwlock_rdlock(&(pg->val_lock));
|
|
}
|
|
|
|
if(!force && usrtc_count(tree)) {
|
|
pthread_rwlock_unlock(&(c->grp_lock));
|
|
pthread_rwlock_unlock(&(pg->val_lock));
|
|
return ENOTEMPTY; /* auuhh ... */
|
|
}
|
|
/* let's retake lock */
|
|
pthread_rwlock_unlock(&(pg->val_lock));
|
|
pthread_rwlock_wrlock(&(pg->val_lock));
|
|
|
|
/* remove from the tree */
|
|
usrtc_delete(ptree, node);
|
|
pthread_rwlock_unlock(&(c->grp_lock));
|
|
|
|
for(nn = usrtc_first(tree); nn != NULL; nn = usrtc_first(tree)) {
|
|
val = usrtc_node_getdata(nn);
|
|
__destroy_val(val);
|
|
usrtc_delete(tree, nn);
|
|
}
|
|
|
|
/* free all */
|
|
pthread_rwlock_unlock(&(pg->val_lock));
|
|
pthread_rwlock_destroy(&(pg->val_lock));
|
|
free(pg->name);
|
|
free(pg);
|
|
|
|
return err;
|
|
}
|
|
|
|
int ydc_conf_add_val_p(ydc_conf_t *c, const char *phvar, int cstr)
|
|
{
|
|
int err = 0;
|
|
int len = 0, p1 = 0, p2 = 0;
|
|
unsigned long siv = 0; unsigned long uiv = 0;
|
|
usrtc_t *ptree = &(c->p_groups), *tree = NULL;
|
|
usrtc_node_t *node;
|
|
char *grp_name, *val_name, *value;
|
|
ydc_pgroup_t *pg = NULL;
|
|
ydc_conf_val_t *val = malloc(sizeof(ydc_conf_val_t)), *oldval = NULL;
|
|
|
|
if(!val) return ENOMEM;
|
|
else memset(val, 0, sizeof(ydc_conf_val_t));
|
|
|
|
/* take the path, group name first */
|
|
if(!(val_name = strchr(phvar, '/'))) {
|
|
err = EINVAL;
|
|
goto __failure;
|
|
} else {
|
|
grp_name = val_name;
|
|
len = strlen(phvar) - strlen(grp_name);
|
|
if(!(grp_name = strndup(phvar, len))) { /* we got it */
|
|
err = ENOMEM;
|
|
goto __failure;
|
|
} else {
|
|
/* looking for existence of the group */
|
|
pthread_rwlock_rdlock(&(c->grp_lock));
|
|
node = usrtc_lookup(ptree, (const void *)grp_name); free(grp_name);
|
|
pthread_rwlock_unlock(&(c->grp_lock));
|
|
if(!node) {
|
|
err = ENOENT; goto __failure;
|
|
} else {
|
|
pg = (ydc_pgroup_t *)usrtc_node_getdata(node);
|
|
pthread_rwlock_wrlock(&(pg->val_lock)); /* we apologize to change something */
|
|
}
|
|
tree = &(pg->values);
|
|
|
|
/* get the value name */
|
|
if(!(value = strchr(val_name, ':'))) {
|
|
err = EINVAL; goto __failure_0;
|
|
} else {
|
|
len = (strlen(val_name) - strlen(value)) - sizeof(char);
|
|
if(!(val_name = strndup(val_name + sizeof(char), len))) { /* gotcha ! */
|
|
err = ENOMEM; goto __failure_0;
|
|
} else if(!(value = strdup(value + sizeof(char)))) { /* take the value itself */
|
|
free(val_name); err = ENOMEM; goto __failure_0;
|
|
}
|
|
|
|
/* check out if we're have one */
|
|
if((node = usrtc_lookup(tree, (const void *)val_name))) { /* oops, then remove */
|
|
oldval = (ydc_conf_val_t *)usrtc_node_getdata(node);
|
|
usrtc_delete(tree, node);
|
|
}
|
|
if(usrtc_count(tree) >= _MAX_VALUES) { /* know your limits (c) British ale and beer stickers */
|
|
err = ENOMEM;
|
|
goto __failure_1;
|
|
}
|
|
/* ok, here we can create a new value */
|
|
/* detect the type */
|
|
len = strlen(value);
|
|
/* first for long values */
|
|
if(isdigit(value[0]) || (isdigit(value[1]) && (value[0] == '-'))) { /* looks like a digit */
|
|
if(value[0] == '-') { /* signed should be used */
|
|
if(len > 9) val->type = LONG;
|
|
else val->type = INT;
|
|
} else {
|
|
if(len > 9) val->type = ULONG;
|
|
else val->type = UINT;
|
|
}
|
|
} else if(value[0] == '(' && value[len - 1] == ')') { /* might be a sexp */
|
|
/* first, we must count parentes */
|
|
for(grp_name = strchr(value, '('); grp_name != NULL;
|
|
grp_name = strchr(grp_name + sizeof(char), '('))
|
|
p1++;
|
|
for(grp_name = strchr(value, ')'); grp_name != NULL;
|
|
grp_name = strchr(grp_name + sizeof(char), ')'))
|
|
p2++;
|
|
|
|
if(p1 != p2) val->type = STRING; /* looks like string */
|
|
else if((grp_name = strchr(value, ' '))) val->type = SEXPR; /* should be at least one space */
|
|
else val->type = STRING;
|
|
} else
|
|
val->type = STRING;
|
|
|
|
/* if forced cstring */
|
|
if(cstr) val->type = STRING;
|
|
|
|
/* let's decide what to do */
|
|
switch(val->type) {
|
|
case STRING:
|
|
val->len = len + sizeof(char);
|
|
val->value = (void *)value;
|
|
break;
|
|
case INT:
|
|
case LONG:
|
|
val->len = sizeof(unsigned long);
|
|
if(!(val->value = malloc(sizeof(unsigned long)))) {
|
|
err = ENOMEM;
|
|
goto __failure_2;
|
|
}
|
|
siv = strtoll(value, NULL, 0);
|
|
*(long *)val->value = siv;
|
|
#if 0 /* FIXME: */
|
|
if((siv != MAXINT64) || (siv != G_MININT64)) {
|
|
*(gint64 *)val->value = siv;
|
|
} else {
|
|
g_free(val->value);
|
|
err = EINVAL;
|
|
goto __failure_2;
|
|
}
|
|
#endif
|
|
free(value);
|
|
break;
|
|
case UINT:
|
|
case ULONG:
|
|
val->len = sizeof(unsigned long);
|
|
if(!(val->value = malloc(sizeof(unsigned long)))) {
|
|
err = ENOMEM;
|
|
goto __failure_2;
|
|
}
|
|
uiv = strtoull(value, NULL, 0);
|
|
*(unsigned long *)val->value = uiv;
|
|
#if 0
|
|
if(uiv != G_MAXUINT64) {
|
|
*(guint64 *)val->value = uiv;
|
|
} else {
|
|
g_free(val->value);
|
|
err = EINVAL;
|
|
goto __failure_2;
|
|
}
|
|
#endif
|
|
free(value);
|
|
break;
|
|
case SEXPR:
|
|
val->len = len;
|
|
val->value = value;
|
|
if(!(val->priv = (void *)parse_sexp(value, len))) {
|
|
errno = EINVAL;
|
|
goto __failure_2;
|
|
}
|
|
break;
|
|
default: /* just to keep gcc happy */
|
|
break;
|
|
}
|
|
|
|
val->name = val_name;
|
|
/* well, init tree and other stuff */
|
|
node = &(val->unode);
|
|
usrtc_node_init(node, val);
|
|
usrtc_insert(tree, node, (const void *)val->name);
|
|
if(oldval) {
|
|
if(oldval->on_eventv) {
|
|
oldval->on_eventv(val, oldval, c, YC_EVENT_MODIFY);
|
|
val->on_eventv = oldval->on_eventv;
|
|
}
|
|
__destroy_val(oldval);
|
|
free(oldval);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
pthread_rwlock_unlock(&(pg->val_lock));
|
|
|
|
return 0;
|
|
|
|
__failure_2:
|
|
if(oldval) {
|
|
node = &(oldval->unode);
|
|
usrtc_insert(tree, node, (const void *)oldval->name);
|
|
}
|
|
__failure_1:
|
|
free(val_name);
|
|
free(value);
|
|
__failure_0:
|
|
pthread_rwlock_unlock(&(pg->val_lock));
|
|
__failure:
|
|
free(val);
|
|
return err;
|
|
}
|
|
|
|
int ydc_conf_rm_val(ydc_conf_t *c, const char *phvar)
|
|
{
|
|
int err = 0;
|
|
usrtc_node_t *node;
|
|
usrtc_t *tree;
|
|
ydc_conf_val_t *val = NULL;
|
|
ydc_pgroup_t *pgroup = NULL;
|
|
|
|
pthread_rwlock_rdlock(&(c->grp_lock));
|
|
if((err = __ydc_conf_get_val_f(c, phvar, &val, &pgroup))) goto __failure;
|
|
|
|
/* delete it */
|
|
pthread_rwlock_wrlock(&(pgroup->val_lock));
|
|
tree = &(pgroup->values);
|
|
node = &(val->unode);
|
|
usrtc_delete(tree, node);
|
|
pthread_rwlock_unlock(&(pgroup->val_lock));
|
|
__destroy_val(val);
|
|
free(val);
|
|
|
|
__failure:
|
|
pthread_rwlock_unlock(&(c->grp_lock));
|
|
return err;
|
|
}
|
|
|
|
int ydc_conf_get_val(ydc_conf_t *c, const char *phvar, ydc_conf_val_t **rval)
|
|
{
|
|
int err = 0;
|
|
ydc_conf_val_t *val = NULL;
|
|
|
|
pthread_rwlock_rdlock(&(c->grp_lock));
|
|
if((err = __ydc_conf_get_val_f(c, phvar, &val, NULL))) goto __failure;
|
|
|
|
/* fill this */
|
|
*rval = val;
|
|
|
|
__failure:
|
|
pthread_rwlock_unlock(&(c->grp_lock));
|
|
return err;
|
|
}
|
|
|
|
/* more code, but faster than double search */
|
|
void ydc_conf_destroy(ydc_conf_t *c)
|
|
{
|
|
usrtc_t *ptree = &(c->p_groups), *tree;
|
|
usrtc_node_t *node, *nnode;
|
|
ydc_pgroup_t *grp;
|
|
ydc_conf_val_t *val;
|
|
|
|
/* lock for all operations */
|
|
pthread_mutex_lock(&(c->conf_lock));
|
|
pthread_rwlock_wrlock(&(c->grp_lock));
|
|
|
|
if(!usrtc_count(ptree)) goto __e_out; /* all is clean*/
|
|
/* well let's clean all groups */
|
|
for(node = usrtc_first(ptree); node != NULL; node = usrtc_first(ptree)) {
|
|
grp = (ydc_pgroup_t *)usrtc_node_getdata(node);
|
|
pthread_rwlock_wrlock(&(grp->val_lock));
|
|
|
|
tree = &(grp->values);
|
|
if(usrtc_count(tree)) {
|
|
for(nnode = usrtc_first(tree); nnode != NULL; nnode = usrtc_first(tree)) {
|
|
val = (ydc_conf_val_t *)usrtc_node_getdata(nnode);
|
|
__destroy_val(val);
|
|
usrtc_delete(tree, nnode);
|
|
free(val);
|
|
}
|
|
}
|
|
usrtc_delete(ptree, node);
|
|
pthread_rwlock_destroy(&(grp->val_lock));
|
|
free(grp->name);
|
|
free(grp);
|
|
}
|
|
|
|
__e_out:
|
|
pthread_mutex_destroy(&(c->conf_lock));
|
|
pthread_rwlock_destroy(&(c->grp_lock));
|
|
|
|
/* secure */
|
|
memset(c, 0, sizeof(ydc_conf_t));
|
|
|
|
free(c);
|
|
return;
|
|
}
|
|
|
|
int ydc_conf_val_attach_event(ydc_conf_t *c, const char *phvar,
|
|
void (*on_eventv)(struct __ym_conf_value *,
|
|
struct __ym_conf_value *,
|
|
ydc_conf_t *, ydc_event_t))
|
|
{
|
|
int err = 0;
|
|
ydc_conf_val_t *val = NULL;
|
|
ydc_pgroup_t *grp;
|
|
|
|
pthread_rwlock_rdlock(&(c->grp_lock));
|
|
if((err = __ydc_conf_get_val_f(c, phvar, &val, &grp))) goto __failure;
|
|
|
|
pthread_rwlock_wrlock(&(grp->val_lock));
|
|
/* attach callback */
|
|
val->on_eventv = on_eventv;
|
|
val->on_eventv(val, NULL, c, YC_EVENT_ATTACH); /* yep, it's an event */
|
|
|
|
pthread_rwlock_unlock(&(grp->val_lock));
|
|
|
|
__failure:
|
|
pthread_rwlock_unlock(&(c->grp_lock));
|
|
return err;
|
|
}
|
|
|
|
int ydc_conf_modify_val(ydc_conf_t *c, const char *phvar, void *data, int len)
|
|
{
|
|
ydc_conf_val_t *val;
|
|
ydc_pgroup_t *grp;
|
|
char *t = NULL;
|
|
void *tb = NULL;
|
|
sexp_t *nsexp = NULL;
|
|
int err = 0;
|
|
|
|
pthread_rwlock_rdlock(&(c->grp_lock));
|
|
if((err = __ydc_conf_get_val_f(c, phvar, &val, &grp))) goto __failure;
|
|
pthread_rwlock_unlock(&(c->grp_lock));
|
|
|
|
/* take group lock */
|
|
pthread_rwlock_wrlock(&(grp->val_lock));
|
|
|
|
switch(val->type) {
|
|
case STRING:
|
|
t = val->value;
|
|
if(!(val->value = strndup((const char *)data, len))) {
|
|
err = ENOMEM;
|
|
val->value = t;
|
|
goto __failure_1;
|
|
} else free(t);
|
|
break;
|
|
case INT:
|
|
case LONG:
|
|
*(long *)val->value = *(long *)data;
|
|
break;
|
|
case ULONG:
|
|
case UINT:
|
|
*(unsigned long *)val->value = *(unsigned long *)data;
|
|
break;
|
|
case CUSTOM: /* that means we must call event callback before data change also */
|
|
if(val->on_eventv)
|
|
val->on_eventv(val, val, c, YC_EVENT_MODIFY); /* oldval is val, since it pre call */
|
|
case BLOB:
|
|
tb = val->value;
|
|
if(!(val->value = malloc(len))) {
|
|
err = ENOMEM;
|
|
val->value = tb;
|
|
goto __failure_1;
|
|
} else memcpy(data, val->value, len);
|
|
free(tb);
|
|
break;
|
|
case SEXPR:
|
|
t = val->value;
|
|
if(!(val->value = strndup((const char *)data, len))) {
|
|
err = ENOMEM;
|
|
val->value = t;
|
|
goto __failure_1;
|
|
}
|
|
if(!(nsexp = parse_sexp(val->value, len))) {
|
|
err = EINVAL;
|
|
val->value = t;
|
|
goto __failure_1;
|
|
} else { /* change it */
|
|
free(t);
|
|
sexp_t_deallocate((sexp_t *)val->priv);
|
|
val->priv = (void *)nsexp;
|
|
}
|
|
break;
|
|
}
|
|
|
|
/* well, run callback if exists */
|
|
if(val->on_eventv)
|
|
val->on_eventv(val, NULL, c, YC_EVENT_MODIFY); /* oldval is nil, incdicate
|
|
* post event for CUSTOM type
|
|
*/
|
|
__failure_1:
|
|
pthread_rwlock_unlock(&(grp->val_lock));
|
|
return err;
|
|
|
|
__failure:
|
|
pthread_rwlock_unlock(&(c->grp_lock));
|
|
return err;
|
|
}
|
|
|
|
int ydc_conf_clone_val(ydc_conf_val_t *s, ydc_conf_val_t *c)
|
|
{
|
|
int e = 0;
|
|
|
|
/* zeroing for our clone */
|
|
memset(c, 0, sizeof(ydc_conf_val_t));
|
|
|
|
/* clone name and data */
|
|
if((c->name = strdup(s->name))) {
|
|
e = ENOMEM; goto __fail;
|
|
}
|
|
if(!(c->value = malloc(s->len))) {
|
|
free(c->name); e = ENOMEM; goto __fail;
|
|
} else
|
|
memcpy(c->value, s->value, s->len);
|
|
/* clone others */
|
|
c->type = s->type;
|
|
c->priv = s->priv;
|
|
|
|
__fail:
|
|
return e;
|
|
}
|
|
|
|
void ydc_conf_val_destroy_clone(ydc_conf_val_t *c)
|
|
{
|
|
free(c->name);
|
|
free(c->value);
|
|
|
|
/* this function will not free structure itself, instead we will
|
|
* zero all stuff within structure, since we don't know how it was allocated,
|
|
* it's better to be sure about cloned data */
|
|
memset(c, 0, sizeof(ydc_conf_val_t));
|
|
|
|
return;
|
|
}
|