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; | ||
|  | } |