From 77d0ee4a0bad5093d163306d246d316f5ac636d5 Mon Sep 17 00:00:00 2001 From: Alexander Vdolainen Date: Wed, 13 Apr 2016 03:59:42 +0300 Subject: [PATCH] [core] Libydaemon added; --- Makefile.am | 2 +- configure.ac | 3 + include/Makefile.am | 3 +- include/ydaemon/ydaemon.h | 367 ++++++++++++++++++++++ ydaemon/Makefile.am | 33 ++ ydaemon/VERSION | 3 + ydaemon/context.c | 472 ++++++++++++++++++++++++++++ ydaemon/daemon.c | 135 ++++++++ ydaemon/functions.c | 184 +++++++++++ ydaemon/libydaemon.pc.in | 13 + ydaemon/log.c | 71 +++++ ydaemon/module.c | 241 +++++++++++++++ ydaemon/values.c | 636 ++++++++++++++++++++++++++++++++++++++ 13 files changed, 2161 insertions(+), 2 deletions(-) create mode 100644 include/ydaemon/ydaemon.h create mode 100644 ydaemon/Makefile.am create mode 100644 ydaemon/VERSION create mode 100644 ydaemon/context.c create mode 100644 ydaemon/daemon.c create mode 100644 ydaemon/functions.c create mode 100644 ydaemon/libydaemon.pc.in create mode 100644 ydaemon/log.c create mode 100644 ydaemon/module.c create mode 100644 ydaemon/values.c diff --git a/Makefile.am b/Makefile.am index f2ea9f4..3840e9c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -6,7 +6,7 @@ else EXAMPLES = endif -SUBDIRS = include tdata sexpr sxmp man $(EXAMPLES) +SUBDIRS = include tdata sexpr sxmp ydaemon man $(EXAMPLES) libsxmpdocdir = ${prefix}/doc/libsxmp libsxmpdoc_DATA = \ diff --git a/configure.ac b/configure.ac index e263827..7a164e0 100644 --- a/configure.ac +++ b/configure.ac @@ -3,6 +3,7 @@ dnl Process this file with autoconf to produce a configure script. AC_INIT(libsxmp, m4_esyscmd([tr -d '\n' < VERSION.sxmp])) AC_SUBST([LIBTDATA_VERSION], m4_esyscmd([tr -d '\n' < tdata/VERSION])) AC_SUBST([LIBSEXPR_VERSION], m4_esyscmd([tr -d '\n' < sexpr/VERSION])) +AC_SUBST([LIBYDAEMON_VERSION], m4_esyscmd([tr -d '\n' < ydaemon/VERSION])) AC_CONFIG_HEADERS([config.h]) @@ -107,6 +108,8 @@ sexpr/Makefile sexpr/libsexpr.pc tdata/Makefile tdata/libtdata.pc +ydaemon/Makefile +ydaemon/libydaemon.pc include/Makefile man/Makefile examples/Makefile]) diff --git a/include/Makefile.am b/include/Makefile.am index 2f2d75c..022cfd1 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -3,4 +3,5 @@ nobase_include_HEADERS = sxmp/sxmp.h sxmp/errno.h sxmp/limits.h sxmp/version.h \ sexpr/cstring.h sexpr/faststack.h sexpr/sexp_errors.h sexpr/sexp.h \ sexpr/sexp_memory.h sexpr/sexp_ops.h sexpr/sexp_vis.h \ tdata/bitwise.h tdata/idx_allocator.h tdata/macro.h tdata/tree.h \ - tdata/usrtc.h tdata/list.h tdata/ctrie.h + tdata/usrtc.h tdata/list.h tdata/ctrie.h \ + ydaemon/ydaemon.h diff --git a/include/ydaemon/ydaemon.h b/include/ydaemon/ydaemon.h new file mode 100644 index 0000000..eae6da1 --- /dev/null +++ b/include/ydaemon/ydaemon.h @@ -0,0 +1,367 @@ +/* + * Yet another daemon library especially designed to be used + * with libsxmp based daemons. + * + * (c) Alexander Vdolainen 2016 + * + * 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 ."; + * + */ + +#ifndef __YDAEMON_YDAEMON_H__ +#define __YDAEMON_YDAEMON_H__ + +#include +#include +#include +#include + +#include +#include +#include + +/* some syntax */ + +/** + * --values (variables) + */ +#define VAR_CREATE_GROUP "create-var-group" +#define VAR_SET "var-set" +/** + * --modules + * (%MODULE_ADD_SX ' ) -- add a module to the list of available modules + * (%MODULE_SET_SX ' (:%MODULE_PREFIX|%MODULE_PATHNAME|%MODULE_CNFPATH *)) + * *value might be the following: + * a. in case of double or single quote - will be taken as is + * b. without quotes will be interpreted as variable (must be defined before this expression) + */ +#define MODULE_ADD_SX "module-add" +#define MODULE_SET_SX "module-set" /** { */ +/* module config values for _SET_SX */ +#define MODULE_PREFIX "prefix" +#define MODULE_PATHNAME "pathname" +#define MODULE_CNFPATH "config-path" +/** } */ +#define MODULE_LOAD_SX "module-load" +#define MODULE_RUN_SX "module-run" +/* internal functions */ +#define SCMOBJECT "object" +/* functions for the logs */ +#define YDOPENLOGS "yd-open-logfile" + +/* limits */ +#define MAX_SCM_FUNCTIONS 16384 + +#define MAX_SERVICES 8192 +#define MAX_MODULES 4096 + +enum { + SCINT = 0, + SCCSTR, + SCOBJECTPTR, + SCUNDEFINED, +}; + +typedef struct __scm_ret { + uint8_t serr; + uint8_t type; + uint32_t len; + union { + void *ret; + struct { + int32_t ec; + uint32_t ru32; + }; + }; +} scret_t; + +#define RETURN_SRET_SERR(r, e) (r).serr = e; return r +#define RETURN_SRET_IRES(r, e) (r).serr = 0; (r).type = SCINT; (r).ec = e; \ + return r +#define RETURN_SRET_ORES(r, e) (r).serr = 0; (r).type = SCOBJECTPTR; (r).ret = (e); \ + return r + +struct __ydaemon_context_type; + +/** + * @brief Type used to describe scm function + * + * It contains name i.e. keyword, pointer to the + * function itself and optional pointer to the + * implementation specific data. It packs to usrtc + * search structure. + */ +typedef struct __scm_func { + char *name; + scret_t (*call)(struct __ydaemon_context_type *, sexp_t *, void *); + void *priv; + usrtc_node_t node; +} scm_function_t; + +/** + * @brief Type used to describe a tree of scm functions + * + * It contains a search structure with packed + * scm_function_t set and also contain special + * private pointer that might be optionally used + * by implementation. + */ +typedef struct __scm_func_tree { + usrtc_t functions; + void *priv; +} scm_function_tree_t; + +/** + * @brief Values types + * + * Describes the type of value + * @see zdc_conf_val_t + */ +typedef enum { + STRING = 0, + INT, + UINT, + LONG, + ULONG, + BLOB, + SEXPR, + CUSTOM, +} ydc_val_type_t; + +/** + * @brief configuration event + * + * Type used to describe events occured with values + */ +typedef enum { + YC_EVENT_ADD, + YC_EVENT_MODIFY, + YC_EVENT_DESTROY, + YC_EVENT_ATTACH, +} ydc_event_t; + +/** + * @brief Type used to fit all required parameters + * + * It contain mode, system settings and a lot of other + * config defined parameters. + */ +typedef struct __ym_conf { + /* parameters groups */ + usrtc_t p_groups; /**< Parameters groups */ + /* functions tree */ + void *init_funcs; + /* locking */ + pthread_rwlock_t grp_lock; /**< Locking for groups */ + pthread_mutex_t conf_lock; /**< Locking for state change, function ops, system io */ +} ydc_conf_t; + +/** + * @brief Type of the parameters group + * + * Store values. + */ +typedef struct __ym_pgroup { + usrtc_t values; + usrtc_node_t unode; + char *name; + pthread_rwlock_t val_lock; +} ydc_pgroup_t; + +/** + * @brief Type used to describe value + * + * Used to store value. + */ +typedef struct __ym_conf_value { + usrtc_node_t unode; + ydc_val_type_t type; + int len; + char *name; + void *value; + void (*on_eventv)(struct __ym_conf_value *, + struct __ym_conf_value *, + ydc_conf_t *, ydc_event_t); + void *priv; +} ydc_conf_val_t; + +typedef struct __ydaemon_log_type { + FILE *logstream; + int verbose_level; + int se; +} yd_log_t; + +typedef struct __ydaemon_context_type { + usrtc_t *services; + usrtc_t *modules; + scm_function_tree_t *func; /** < functions for configuration and processing */ + ydc_conf_t *values; /** < values stored by the groups */ + yd_log_t *logcontext; + int flags; + pthread_rwlock_t modlock; + pthread_mutex_t looplock; + int daemon; +} yd_context_t; + +#define yd_mod_ctx_wrlock(n) pthread_rwlock_wrlock(&(n)->modlock) +#define yd_mod_ctx_rdlock(n) pthread_rwlock_rdlock(&(n)->modlock) +#define yd_mod_ctx_unlock(n) pthread_rwlock_unlock(&(n)->modlock) + +typedef enum { + YD_SERVICE_MUX = 0, + YD_SXMP_MOD, + YD_DATASTORE_MOD, + YD_OTHER, +} yd_mod_type_t; + +typedef struct __ydaemon_module { + char *name; + char *pathname; + char *cnfname; + char *prefix; + void *dlhandle; + void *(*getobject)(void *, const char *); + int (*shutdown)(yd_context_t *); + int (*preinit)(yd_context_t *); + int (*init)(yd_context_t *); + int (*run)(yd_context_t *); + yd_mod_type_t type; + int flags; + usrtc_node_t node; +} yd_mod_t; + +/* functions defines for modules */ +#define def_shutdown(PREFIX) int PREFIX ## _shutdown(yd_context_t *ctx) +#define def_preinit(PREFIX) int PREFIX ## _preinit(yd_context_t *ctx) +#define def_init(PREFIX) int PREFIX ## _init(yd_context_t *ctx) +#define def_run(PREFIX) int PREFIX ## _run(yd_context_t *ctx) +#define def_getobject(PREFIX) void* PREFIX ## _getobject(void *priv, const char *oname) +/* function arguments macros */ +#define def_ctx ctx +#define defpriv priv +#define defoname oname + +#define yd_mod_set_prefix(n, p) (n)->prefix = (p) +#define yd_mod_set_pathname(n, p) (n)->pathname = (p) +#define yd_mod_set_cfgpath(n, p) (n)->cnfname = (p) + +#define yd_mod_preinit(n) \ + { memset((n), 0, sizeof(yd_mod_t)); usrtc_node_init((n)->node, (n)); } + +int yd_mod_init(yd_context_t *, yd_mod_t *); + +int yd_mod_get(yd_context_t *, const char *, yd_mod_t **); + +int yd_mod_load(yd_context_t *, yd_mod_t *); + +int yd_mod_run(yd_context_t *, yd_mod_t *); + +int yd_mod_shutdown(yd_context_t *, yd_mod_t *); + +void *yd_mod_getobject(yd_context_t *, yd_mod_t *, void *, const char *); + +void *yd_mod_getsym(yd_context_t *, yd_mod_t *, const char *); + +typedef struct __ydaemon_service { + char *name; /* name of the service */ + void *ops; /* the subject to think about ... */ + void *connection; /* as backends private data */ + void *conf; /* special configuration */ + usrtc_node_t node; +} yd_service_t; + +#define yd_ctx_values(c) (c)->values + +int yd_init_ctx(yd_context_t *); + +int yd_eval_ctx(yd_context_t *, const char *); + +scret_t yd_eval_sexp(yd_context_t *, sexp_t *); + +int yd_mainloop(yd_context_t *); + +void yd_mainloop_exit(yd_context_t *); + +int yd_set_daemonize(yd_context_t *, int); + +/* modules related functions */ +int yd_mod_add(yd_context_t *, const char *, yd_mod_type_t); + +/* internal functions API */ +void scm_func_tree_free(scm_function_tree_t *pt); + +int scm_func_tree_init(scm_function_tree_t **pt, void *priv); + +int scm_func_tree_insert(yd_context_t *zdx, const char *name, + scret_t (*call)(yd_context_t *, sexp_t *, void *), + void *priv); + +int scm_func_tree_insert_t(yd_context_t *zdx, scm_function_t *f); + +scret_t scm_func_tree_call(yd_context_t *zdx, void *cnf, sexp_t *sx, + char *name); + +int scm_func_tree_delete(yd_context_t *zdx, char *name); + +/* variables related functions */ +int ydc_conf_init(ydc_conf_t *); + +int ydc_conf_create_pgroup(ydc_conf_t *, char *); + +int ydc_conf_get_val(ydc_conf_t *, const char *, ydc_conf_val_t **); + +int ydc_conf_add_val_p(ydc_conf_t *, const char *, int); + +/* some internal stuff */ +#define UNIT_TESTS 1 +#ifdef UNIT_TESTS +#define blub printf("%d at %s\n", __LINE__, __FUNCTION__) +#endif + +/* logging */ +enum { + YL_DEBUG = 0, + YL_INFO, + YL_WARN, + YL_ERROR, +}; + +void ydlog(yd_context_t *ctx, int olvl, const char *fmt, ...); + +void yddaemon(yd_context_t *ctx); + +/* object storage */ + +#define MAX_OBJECTS 65535 + +typedef struct __ostore_type { + usrtc_t objects; + pthread_rwlock_t rwlock; +} obj_store_t; + +typedef struct __ostore_node_type { + char *name; + void *objdata; + usrtc_node_t node; +} obj_store_node_t; + +int obj_store_init(obj_store_t *); + +int obj_store_set(obj_store_t *, const char *, void *); + +int obj_store_get(obj_store_t *, const char *, void **); + +int obj_store_remove(obj_store_t *, const char *); + +#endif /* __YDAEMON_YDAEMON_H__ */ diff --git a/ydaemon/Makefile.am b/ydaemon/Makefile.am new file mode 100644 index 0000000..f0d38a9 --- /dev/null +++ b/ydaemon/Makefile.am @@ -0,0 +1,33 @@ +## Process this file with automake to produce Makefile.in + +AM_CPPFLAGS = \ + -DPACKAGE_LOCALE_DIR=\""$(localedir)"\" \ + -DPACKAGE_SRC_DIR=\""$(srcdir)"\" \ + -DPACKAGE_DATA_DIR=\""$(pkgdatadir)"\" \ + $(LIBYDAEMON_CFLAGS) -I../include + +AM_CFLAGS =\ + -Wall\ + -g + +lib_LTLIBRARIES = libydaemon.la + + +libydaemon_la_SOURCES = \ + values.c functions.c log.c module.c \ + daemon.c context.c + +libydaemon_la_LDFLAGS = -Wl,--export-dynamic + +# where to find other libs ... +LIBTDATA_LIBSLA = ../tdata/.libs/libtdata.la +LIBSEXPR_LIBSLA = ../sexpr/.libs/libsexpr.la + +libydaemon_la_LIBADD = -lpthread -lcrypto -ldl $(LIBTDATA_LIBSLA) $(LIBSEXPR_LIBSLA) + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = libydaemon.pc + +EXTRA_DIST = \ + libydaemon.pc.in + diff --git a/ydaemon/VERSION b/ydaemon/VERSION new file mode 100644 index 0000000..64b3f4a --- /dev/null +++ b/ydaemon/VERSION @@ -0,0 +1,3 @@ +0.0.1 + + diff --git a/ydaemon/context.c b/ydaemon/context.c new file mode 100644 index 0000000..836e119 --- /dev/null +++ b/ydaemon/context.c @@ -0,0 +1,472 @@ +/* + * Yet another daemon library especially designed to be used + * with libsxmp based daemons. + * + * (c) Alexander Vdolainen 2016 + * + * 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 ."; + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +static long __cmp_cstr(const void *a, const void *b) +{ + return strcmp((const char *)a, (const char *)b); +} + +/* config entries */ +static scret_t __ymconf_create_pgroup(yd_context_t *cnf, sexp_t *sx, void *priv) +{ + char *grp_name; + scret_t rets; + int r = EINVAL; + + /* get the group name */ + sx->list = sx->list->next; + if(!(sx->list)) { RETURN_SRET_IRES(rets, r); } + else grp_name = sx->list->val; + + /* now we will add this */ + r = ydc_conf_create_pgroup(cnf->values, (char *)grp_name); + RETURN_SRET_IRES(rets, r); +} + +static scret_t __module_add(yd_context_t *ctx, sexp_t *sx, void *priv) +{ + char *mod_name, *mod_type; + yd_mod_type_t type; + scret_t rets; + int r = 0; + + /* get the name */ + sx->list = sx->list->next; + if(!(sx->list)) { r = EINVAL; goto __fail; } + else mod_name = sx->list->val; + + sx->list = sx->list->next; + if(!(sx->list)) { r = EINVAL; goto __fail; } + else mod_type = sx->list->val; + + if(!strcmp(mod_type, "service")) type = YD_SERVICE_MUX; + else if(!strcmp(mod_type, "sxmp")) type = YD_SXMP_MOD; + else if(!strcmp(mod_type, "datastore")) type = YD_DATASTORE_MOD; + else if(!strcmp(mod_type, "other")) type = YD_OTHER; + else r = EINVAL; + + if(!r) r = yd_mod_add(ctx, mod_name, type); + + __fail: + RETURN_SRET_IRES(rets, r); +} + +static scret_t __object(yd_context_t *ctx, sexp_t *sx, void *priv) +{ + char *mod_name, *mod_obj; + yd_mod_t *lmod; + scret_t rets; + int r = 0; + void *fdata; + + /* get the name */ + sx->list = sx->list->next; + if(!(sx->list)) { r = EINVAL; goto __fail; } + else mod_name = sx->list->val; + + sx->list = sx->list->next; + if(!(sx->list)) { r = EINVAL; goto __fail; } + else mod_obj = sx->list->val; + + r = yd_mod_get(ctx, mod_name, &lmod); + if(r) { + RETURN_SRET_SERR(rets, r); + } else fdata = yd_mod_getobject(ctx, lmod, NULL, mod_obj); + + if(!fdata) { + r = ENOENT; + goto __fail; + } + + RETURN_SRET_ORES(rets, fdata); + + __fail: + RETURN_SRET_IRES(rets, r); +} + +static scret_t __module_load(yd_context_t *ctx, sexp_t *sx, void *priv) +{ + register int state = 0; + register int idx; + yd_mod_t *lmod; + sexp_t *isx; + int r = EINVAL; + scret_t rets; + + SEXP_ITERATE_LIST(sx, isx, idx) { + if(isx->ty == SEXP_LIST) goto __fail; + else { + if(!state) { + if(strcmp(isx->val, MODULE_LOAD_SX)) goto __fail; + else state++; + } else { /* find module */ + if(isx->aty != SEXP_SQUOTE) goto __fail; + + r = yd_mod_get(ctx, (const char *)isx->val, &lmod); + if(r) goto __fail; + + /* load it */ + r = yd_mod_load(ctx, lmod); + } + } + } + + __fail: + RETURN_SRET_IRES(rets, r); +} + +static scret_t __module_run(yd_context_t *ctx, sexp_t *sx, void *priv) +{ + register int state = 0; + register int idx; + yd_mod_t *lmod; + sexp_t *isx; + int r = EINVAL; + scret_t rets; + + SEXP_ITERATE_LIST(sx, isx, idx) { + if(isx->ty == SEXP_LIST) goto __fail; + else { + if(!state) { + if(strcmp(isx->val, MODULE_RUN_SX)) goto __fail; + else state++; + } else { /* find module */ + if(isx->aty != SEXP_SQUOTE) goto __fail; + + r = yd_mod_get(ctx, (const char *)isx->val, &lmod); + if(r) goto __fail; + + /* load it */ + r = yd_mod_run(ctx, lmod); + } + } + } + + __fail: + RETURN_SRET_IRES(rets, r); +} + +static scret_t __module_set(yd_context_t *ctx, sexp_t *sx, void *priv) +{ + register int state = 0; /* states - 0 begin, 1 - rpc function checked, 2 - module found */ + register int pst = 0; /* 0 - nothing, 1 - name set */ + char *pname, *pval, *pss; + yd_mod_t *lmod; + int r = 0, idx, pidx; + sexp_t *isx, *pisx; + ydc_conf_val_t *rval; + scret_t rets; + + r = EINVAL; /* invalid syntax */ + SEXP_ITERATE_LIST(sx, isx, idx) { + if(isx->ty == SEXP_LIST) { + r = EINVAL; + if(state < 2) goto __err_fail; + pst = 0; + SEXP_ITERATE_LIST(isx, pisx, pidx) { + if(pisx->ty != SEXP_VALUE) { /* values are not acceptable */ + goto __err_fail; + } + + if(!pst && *(pisx->val) != ':') goto __err_fail; /* syntax error */ + + if(!pst) { + pname = pisx->val + sizeof(char); pst++; + } else { + if(pisx->aty == SEXP_DQUOTE || pisx->aty == SEXP_SQUOTE) pval = pisx->val; + else if((pisx->aty == SEXP_BASIC) && (pss = strchr((const char *)pisx->val, '/'))) { + /* it's a variable */ + r = ydc_conf_get_val(ctx->values, (const char *)pisx->val, &rval); + if(r) goto __err_fail; + + if(rval->type != STRING) goto __err_fail; + else pval = (char *)rval->value; + } else goto __err_fail; + + /* set the right part of the module */ + if(!strcmp(pname, MODULE_PREFIX)) yd_mod_set_prefix(lmod, pval); + else if(!strcmp(pname, MODULE_PATHNAME)) yd_mod_set_pathname(lmod, pval); + else if(!strcmp(pname, MODULE_CNFPATH)) yd_mod_set_cfgpath(lmod, pval); + else goto __err_fail; + } + } + } else if(isx->ty == SEXP_VALUE) { /* got the value */ + switch(state) { + case 0: + if(strcmp((const char *)isx->val, MODULE_SET_SX)) goto __err_fail; + else state++; + break; + case 1: + if(isx->aty != SEXP_SQUOTE) goto __err_fail; + else { + r = yd_mod_get(ctx, (const char *)isx->val, &lmod); + if(r) goto __err_fail; + else state++; + } + break; + default: + goto __err_fail; + } + } + } + + r = 0; + + __err_fail: + + RETURN_SRET_IRES(rets, r); +} + +static scret_t __ymconf_set_cv(yd_context_t *cnf, sexp_t *sx, void *priv) +{ + char *value_expression, *value; + char *buf = priv; + scret_t rets; + int r = EINVAL; + int cstr = 0; + + /* skip keyword */ + sx->list = sx->list->next; + /* check out this */ + if(sx->ty != SEXP_LIST) { RETURN_SRET_IRES(rets, r); } + else sx = sx->list; + value_expression = sx->list->val; + sx->list = sx->list->next; + //if(sx->ty == SEXP_LIST) return -EINVAL; + value = sx->list->val; + /* check for the forcing of cstring */ + if(sx->list->aty == SEXP_DQUOTE) cstr = 1; + + snprintf(buf, sizeof(char)*4096, "%s:%s", value_expression, value); + + r = ydc_conf_add_val_p(cnf->values, (const char *)buf, cstr); + + RETURN_SRET_IRES(rets, r); +} + +static scret_t __ymconf_uselogfile(yd_context_t *cnf, sexp_t *sx, void *priv) +{ + char *logfile; + FILE *ls; + ydc_conf_val_t *rval; + scret_t rets; + int r = EINVAL; + + r = ydc_conf_get_val(cnf->values, "daemon/logfile", &rval); + if(r) goto __fini; + if(rval->type != STRING) goto __fini; + else logfile = (char *)rval->value; + + ls = fopen(logfile, "a+"); + if(!ls) r = errno; + else { + cnf->logcontext->logstream = ls; + setbuf(ls, NULL); + r = 0; + } + + if(cnf->daemon && !r) { + fclose(stdout); + fclose(stderr); + stdout = fopen(logfile, "a+"); + stderr = fopen(logfile, "a+"); + setbuf(stdout, NULL); + setbuf(stderr, NULL); + } + + __fini: + RETURN_SRET_IRES(rets, r); +} + +int yd_init_ctx(yd_context_t *ctx) +{ + int r = 0; + usrtc_t *services = malloc(sizeof(usrtc_t)); + usrtc_t *modules = malloc(sizeof(usrtc_t)); + scm_function_tree_t *ft = NULL; + ydc_conf_t *c = malloc(sizeof(ydc_conf_t)); + yd_log_t *zdl = malloc(sizeof(yd_log_t)); + char *pbuf = malloc(sizeof(char)*4096); + + if(!ctx) return EINVAL; + else memset(ctx, 0, sizeof(yd_context_t)); + + if((r = pthread_rwlock_init(&ctx->modlock, NULL))) goto __fini; + if((r = pthread_mutex_init(&ctx->looplock, NULL))) { + pthread_rwlock_destroy(&ctx->modlock); + goto __fini; + } + + if(!zdl) goto __fail; + /* init trees */ + if(!services || !modules) goto __fail; + else { + usrtc_init(services, USRTC_SPLAY, MAX_SERVICES, __cmp_cstr); + usrtc_init(modules, USRTC_SPLAY, MAX_MODULES, __cmp_cstr); + ctx->services = services; + ctx->modules = modules; + ctx->daemon = 0; + ctx->logcontext = zdl; + zdl->logstream = stdout; + zdl->verbose_level = YL_INFO; + zdl->se = 1; + } + + r = scm_func_tree_init(&ft, ctx); + if(r) goto __fail; + else { /* fill up with default functions */ + if(!pbuf) goto __fail; + if(!c) goto __fail; /* let's init config */ + r = ydc_conf_init(c); + if(r) goto __fail; + else { + ctx->values = c; + ctx->func = ft; + } + + /* defaults ones */ + scm_func_tree_insert(ctx, VAR_CREATE_GROUP, + __ymconf_create_pgroup, NULL); + scm_func_tree_insert(ctx, VAR_SET, + __ymconf_set_cv, pbuf); + scm_func_tree_insert(ctx, MODULE_ADD_SX, + __module_add, NULL); + scm_func_tree_insert(ctx, MODULE_SET_SX, + __module_set, NULL); + scm_func_tree_insert(ctx, MODULE_LOAD_SX, + __module_load, NULL); + scm_func_tree_insert(ctx, MODULE_RUN_SX, + __module_run, NULL); + scm_func_tree_insert(ctx, SCMOBJECT, + __object, NULL); + scm_func_tree_insert(ctx, YDOPENLOGS, + __ymconf_uselogfile, NULL); + + } + + if(!r) pthread_mutex_lock(&ctx->looplock); + + __fini: + return r; + + __fail: + pthread_rwlock_destroy(&ctx->modlock); + pthread_mutex_destroy(&ctx->looplock); + if(c) free(c); + if(zdl) free(zdl); + if(pbuf) free(pbuf); + if(services) free(services); + if(modules) free(modules); + if(ft) scm_func_tree_free(ft); + + return r; +} + +scret_t yd_eval_sexp(yd_context_t *ctx, sexp_t *sx) +{ + scret_t rets; + int r; + char *keyw; + char ibuf[64]; + + if(sx->ty == SEXP_LIST) keyw = sx->list->val; + else { + r = EINVAL; + RETURN_SRET_SERR(rets, r); + } + + rets = scm_func_tree_call(ctx, ctx, sx, keyw); + + if(rets.type == SCINT && rets.ec != 0) { + r = rets.ec; + print_sexp(ibuf, 63, sx); + if(strlen(ibuf) >= 63) ydlog(ctx, YL_WARN, "%s... return %d (%s)\n", ibuf, r, strerror(r)); + else ydlog(ctx, YL_WARN, "%s return %d (%s)\n", ibuf, r, strerror(r)); + } + + return rets; +} + +int yd_eval_ctx(yd_context_t *ctx, const char *confpath) +{ + sexp_t *sx; + sexp_iowrap_t *iow; + int fd, r = 0; + char *keyw; + scret_t ret; + char ibuf[64]; + + /* try to open it */ + if((fd = open(confpath, O_RDONLY)) < 0) { + r = errno; + return r; + } + + if(!(iow = init_iowrap(fd))) { + r = errno; + close(fd); + return r; + } + + /* now load the configuration scenario */ + while((sx = read_one_sexp(iow)) != NULL) { + if(sx->ty == SEXP_LIST) keyw = sx->list->val; + else keyw = sx->val; + + ret = scm_func_tree_call(ctx, ctx, sx, keyw); + /* not found */ + if(ret.serr) { + close(fd); + ydlog(ctx, YL_ERROR, "%s: unknown keyword ``%s''\n", __FUNCTION__, keyw); + return r; + } + /* more information */ + if(ret.type == SCINT && ret.ec != 0) { + r = ret.ec; + print_sexp(ibuf, 63, sx); + if(strlen(ibuf) >= 63) ydlog(ctx, YL_WARN, "%s... return %d (%s)\n", ibuf, r, strerror(r)); + else ydlog(ctx, YL_WARN, "%s return %d (%s)\n", ibuf, r, strerror(r)); + } + } + + r = 0; + + close(fd); + + return r; +} + diff --git a/ydaemon/daemon.c b/ydaemon/daemon.c new file mode 100644 index 0000000..a1c38a7 --- /dev/null +++ b/ydaemon/daemon.c @@ -0,0 +1,135 @@ +/* + * Yet another daemon library especially designed to be used + * with libsxmp based daemons. + * + * (c) Alexander Vdolainen 2016 + * + * 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 ."; + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +int yd_mainloop(yd_context_t *ctx) +{ + usrtc_node_t *node = NULL; + yd_mod_t *mod; + + /* we will locked here */ + pthread_mutex_lock(&ctx->looplock); + + /* if we're here ... somebody decide to end the loop */ + yd_mod_ctx_wrlock(ctx); + for(node = usrtc_last(ctx->modules); ;node = usrtc_prev(ctx->modules, node)) { + mod = (yd_mod_t *)usrtc_node_getdata(node); + mod->shutdown(ctx); + + /* for() */ + if(node == usrtc_first(ctx->modules)) break; + } + yd_mod_ctx_unlock(ctx); + + return 0; +} + +void yd_mainloop_exit(yd_context_t *ctx) +{ + pthread_mutex_unlock(&ctx->looplock); + + return; +} + +/* + * daemon workout + */ + +void yddaemon(yd_context_t *ctx) +{ + pid_t pid; + ydc_conf_val_t *rval; + + /* fork the first time to detach the controlling terminal + * and avoid to be a group leader. + */ + pid = fork(); + if(pid < 0) exit(EXIT_FAILURE); /* something goes wrong ... */ + if(pid > 0) exit(EXIT_SUCCESS); /* parent can take a rest */ + + /* try to become a session leader */ + if(setsid() < 0) exit(EXIT_FAILURE); /* something goes wrong ... */ + + /* ignore fucking signals */ + signal(SIGCHLD, SIG_IGN); + signal(SIGHUP, SIG_IGN); + signal(SIGPIPE, SIG_IGN); + + /* fork the second time, to avoid be a session leader - the goal is + * regain a terminal control never. + */ + pid = fork(); + if(pid < 0) exit(EXIT_FAILURE); /* something goes wrong ... */ + if(pid > 0) exit(EXIT_SUCCESS); /* parent can take a rest */ + + /* reset mask */ + umask(0); + + /* change working directory to root */ + // chdir("/"); +#if 0 + /* test and change if set */ + if(!ydc_conf_get_val(ctx->zvalues, "daemon/workingdir", &rval)) { + if(rval->type == STRING) chdir((const char *)rval->value); + } +#endif + + /* let's a deal */ + fclose(stdin); + fclose(stderr); + fclose(stdout); + + /* if oe - mean we allowing to write a lof file thru standard prints ... */ + stdin = fopen("/dev/null", "r"); +#if 0 + if(ctx->logcontext->se && !ydc_conf_get_val(ctx->zvalues, "daemon/logfile", &rval)) { + if(rval->type == STRING) { + stdout = fopen((char *)rval->value, "a"); + stderr = fopen((char *)rval->value, "a"); + } + } else { +#endif + stdout = fopen("/dev/null", "a"); + stderr = fopen("/dev/null", "a"); +#if 0 + } +#endif + ctx->daemon = 1; + + return; +} diff --git a/ydaemon/functions.c b/ydaemon/functions.c new file mode 100644 index 0000000..e7d5487 --- /dev/null +++ b/ydaemon/functions.c @@ -0,0 +1,184 @@ +/* + * Yet another daemon library especially designed to be used + * with libsxmp based daemons. + * + * (c) Alexander Vdolainen 2016 + * + * 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 ."; + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +static long __cmp_cstrs(const void *a, const void *b) +{ + return (long)(strcmp((const char *)a, (const char *)b)); +} + +static void __scm_func_tree_init(scm_function_tree_t *pt, void *priv) +{ + usrtc_t *foost = (usrtc_t *)&(pt->functions); + + usrtc_init(foost, USRTC_SPLAY, MAX_SCM_FUNCTIONS, __cmp_cstrs); + return; +} + +static scm_function_tree_t *__scm_func_tree_alloc(void *priv) +{ + scm_function_tree_t *t = malloc(sizeof(scm_function_tree_t)); + + if(!t) goto __fail; + else __scm_func_tree_init(t, priv); + + __fail: + return t; +} + +void scm_func_tree_free(scm_function_tree_t *pt) +{ + usrtc_t *ftree = &(pt->functions); + usrtc_node_t *node = NULL; + scm_function_t *sf; + + if(usrtc_count(ftree)) { + for(node = usrtc_first(ftree); node != NULL; node = usrtc_first(ftree)) { + sf = (scm_function_t *)usrtc_node_getdata(node); + /* remove from the tree first */ + usrtc_delete(ftree, node); + /* free all */ + free(sf->name); + free(sf); + } + } + + free(pt); + + return; +} + +/* external API */ +int scm_func_tree_init(scm_function_tree_t **pt, void *priv) +{ + scm_function_tree_t *nt = __scm_func_tree_alloc(priv); + + if(!nt) return ENOENT; + else *pt = nt; + + return 0; +} + +int scm_func_tree_insert(yd_context_t *zdx, const char *name, + scret_t (*call)(yd_context_t *, sexp_t *, void *), + void *priv) +{ + scm_function_tree_t *pt = zdx->func; + usrtc_t *ftree = &(pt->functions); + scm_function_t *n_func = malloc(sizeof(scm_function_t)); + usrtc_node_t *node = NULL; + int e = 0; + + /* just check existence */ + if(!n_func) return ENOMEM; + if(usrtc_count(ftree) >= MAX_SCM_FUNCTIONS) { + e = ENOMEM; + goto __end; + } + if((node = usrtc_lookup(ftree, (const void *)name))) { + e = EEXIST; + goto __end; + } + + /* init structure */ + if(!(n_func->name = strdup(name))) { + free(n_func); + return ENOMEM; + } + n_func->call = call; + n_func->priv = priv; + node = &(n_func->node); + usrtc_node_init(node, n_func); + + /* insert to the tree */ + usrtc_insert(ftree, node, (const void *)name); + + __end: + if(e) free(n_func); + + return e; +} + +int scm_func_tree_insert_t(yd_context_t *zdx, scm_function_t *f) +{ + scm_function_tree_t *pt = zdx->func; + usrtc_t *ftree = &(pt->functions); + usrtc_node_t *node = usrtc_lookup(ftree, (const void *)f->name); + + if(node) return EEXIST; + else node = &(f->node); + if(usrtc_count(ftree) >= MAX_SCM_FUNCTIONS) return ENOMEM; + + usrtc_node_init(node, f); + usrtc_insert(ftree, node, (const void *)f->name); + + return 0; +} + +scret_t scm_func_tree_call(yd_context_t *zdx, void *cnf, sexp_t *sx, + char *name) +{ + scm_function_tree_t *pt = zdx->func; + usrtc_t *ftree = &(pt->functions); + usrtc_node_t *node = usrtc_lookup(ftree, (const void *)name); + scm_function_t *fn; + + if(!node) { + scret_t rets; + RETURN_SRET_SERR(rets, ENOENT); + } else fn = (scm_function_t *)usrtc_node_getdata(node); + + cnf = zdx; + + return fn->call(cnf, sx, fn->priv); +} + +int scm_func_tree_delete(yd_context_t *zdx, char *name) +{ + scm_function_tree_t *pt = zdx->func; + usrtc_t *ftree = &(pt->functions); + usrtc_node_t *node = usrtc_lookup(ftree, (const void *)name); + scm_function_t *fn; + + if(!node) return ENOENT; + else fn = (scm_function_t *)usrtc_node_getdata(node); + + /* remove from tree first */ + usrtc_delete(ftree, node); + + /* free */ + free(fn->name); + free(fn); + + return 0; +} diff --git a/ydaemon/libydaemon.pc.in b/ydaemon/libydaemon.pc.in new file mode 100644 index 0000000..3f544d1 --- /dev/null +++ b/ydaemon/libydaemon.pc.in @@ -0,0 +1,13 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +datarootdir=@datarootdir@ +datadir=@datadir@ +includedir=@includedir@ + +Name: libydaemon +Description: yet another library for daemons +Version: @LIBYDAEMON_VERSION@ +Requires: +Libs: -L${libdir} -lydaemon +Cflags: -I${includedir} diff --git a/ydaemon/log.c b/ydaemon/log.c new file mode 100644 index 0000000..8f8ee59 --- /dev/null +++ b/ydaemon/log.c @@ -0,0 +1,71 @@ +/* + * Yet another daemon library especially designed to be used + * with libsxmp based daemons. + * + * (c) Alexander Vdolainen 2016 + * + * 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 ."; + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +void ydlog(yd_context_t *ctx, int olvl, const char *fmt, ...) +{ + yd_log_t *log = ctx->logcontext; + FILE *logstr = NULL; + char tbuf[32]; + struct tm tmb; + time_t ctime; + va_list args; + + if(!log) return; + if(!(logstr = log->logstream)) return; + + if(olvl >= log->verbose_level) { /* we can output this */ + /* take a time and output this */ + time(&ctime); + localtime_r(&ctime, &tmb); + asctime_r(&tmb, tbuf); + /* remove trailing \n */ + tbuf[strlen(tbuf) - 1] = '\0'; + fprintf(logstr, "[%s] ", tbuf); + switch(olvl) { /* out prefix */ + case YL_DEBUG: fprintf(logstr, "[DEBUG] "); break; + case YL_INFO: fprintf(logstr, "[INFO] "); break; + case YL_WARN: fprintf(logstr, "[WARNING] "); break; + case YL_ERROR: fprintf(logstr, "[ERROR] "); break; + default: return; + } + + va_start(args, fmt); + vfprintf(logstr, fmt, args); + va_end(args); + } + + return; +} diff --git a/ydaemon/module.c b/ydaemon/module.c new file mode 100644 index 0000000..ab1bba2 --- /dev/null +++ b/ydaemon/module.c @@ -0,0 +1,241 @@ +/* + * Yet another daemon library especially designed to be used + * with libsxmp based daemons. + * + * (c) Alexander Vdolainen 2016 + * + * 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 ."; + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +int yd_mod_add(yd_context_t *ctx, const char *name, yd_mod_type_t type) +{ + yd_mod_t *mod = malloc(sizeof(yd_mod_t)); + usrtc_node_t *node = NULL; + int r = 0; + + if(!mod) return ENOMEM; + else if(!(mod->name = strdup(name))) { + free(mod); + return ENOMEM; + } + mod->type = type; + usrtc_node_init(&mod->node, mod); + + yd_mod_ctx_wrlock(ctx); + node = usrtc_lookup(ctx->modules, mod->name); + if(node) r = EEXIST; + else usrtc_insert(ctx->modules, &mod->node, mod->name); + yd_mod_ctx_unlock(ctx); + + return r; +} + +int yd_mod_load(yd_context_t *ctx, yd_mod_t *mod) +{ + char buf[256]; + int r = 0; + + /* let's try to find it and load */ + if(!mod->pathname || !mod->name) return EINVAL; + + if(!(mod->dlhandle = dlopen(mod->pathname, RTLD_LAZY|RTLD_GLOBAL))) + return ENOENT; + + /* ok now we can resolve all symbols */ + snprintf(buf, 256, "%s_preinit", mod->prefix); + mod->preinit = dlsym(mod->dlhandle, buf); + if(!mod->preinit) { + __einval: + dlclose(mod->dlhandle); + return EINVAL; + } + + snprintf(buf, 256, "%s_init", mod->prefix); + if(!(mod->init = dlsym(mod->dlhandle, buf))) goto __einval; + snprintf(buf, 256, "%s_run", mod->prefix); + if(!(mod->run = dlsym(mod->dlhandle, buf))) goto __einval; + snprintf(buf, 256, "%s_shutdown", mod->prefix); + if(!(mod->shutdown = dlsym(mod->dlhandle, buf))) goto __einval; + snprintf(buf, 256, "%s_getobject", mod->prefix); + if(!(mod->getobject = dlsym(mod->dlhandle, buf))) goto __einval; + + if((r = mod->preinit(ctx))) { + blub; + __fail: + dlclose(mod->dlhandle); + return r; + } + + if(mod->cnfname) { + blub; + r = yd_eval_ctx(ctx, (const char *)mod->cnfname); + if(r) goto __fail; + } + + if((r = mod->init(ctx))) goto __fail; + + if(mod->type == YD_SERVICE_MUX) { + if((r = mod->run(ctx))) goto __fail; + } + + return 0; +} + +int yd_mod_get(yd_context_t *ctx, const char *name, yd_mod_t **rmod) +{ + int r = 0; + usrtc_node_t *node; + + yd_mod_ctx_rdlock(ctx); + node = usrtc_lookup(ctx->modules, name); + if(!node) r = ENOENT; + else *rmod = (yd_mod_t *)usrtc_node_getdata(node); + yd_mod_ctx_unlock(ctx); + + return r; +} + +int yd_mod_run(yd_context_t *ctx, yd_mod_t *mod) +{ + return mod->run(ctx); +} + +int yd_mod_shutdown(yd_context_t *ctx, yd_mod_t *mod) +{ + return mod->shutdown(ctx); +} + +void *yd_mod_getobject(yd_context_t *ctx, yd_mod_t *mod, + void *priv, const char *name) +{ + return mod->getobject(priv, name); +} + +void *yd_mod_getsym(yd_context_t *ctx, yd_mod_t *mod, const char *name) +{ + return dlsym(mod->dlhandle, name); +} + +/* object store */ + +static long __cmp_cstr(const void *a, const void *b) +{ + return strcmp((const char *)a, (const char *)b); +} + +#define __obs_wrlock(s) pthread_rwlock_wrlock(&((s)->rwlock)) +#define __obs_rdlock(s) pthread_rwlock_rdlock(&((s)->rwlock)) +#define __obs_unlock(s) pthread_rwlock_unlock(&((s)->rwlock)) +#define __obs_tree(s) &((s)->objects) + +int obj_store_init(obj_store_t *st) +{ + usrtc_t *tree = &st->objects; + + if(pthread_rwlock_init(&(st->rwlock), NULL)) return ENOMEM; + else usrtc_init(tree, USRTC_SPLAY, MAX_OBJECTS, __cmp_cstr); + + return 0; +} + +int obj_store_set(obj_store_t *st, const char *nm, void *od) +{ + usrtc_node_t *node = NULL; + obj_store_node_t *onode = NULL; + + __obs_rdlock(st); + node = usrtc_lookup(__obs_tree(st), nm); + __obs_unlock(st); + + if(!node) { + if(!(onode = malloc(sizeof(obj_store_node_t)))) return ENOMEM; + else memset(onode, 0, sizeof(obj_store_node_t)); + + if(!(onode->name = strdup(nm))) { + free(onode); + return ENOMEM; + } + + onode->objdata = od; + node = &(onode->node); + usrtc_node_init(node, onode); + + __obs_wrlock(st); + usrtc_insert(__obs_tree(st), node, (const void *)onode->name); + __obs_unlock(st); + } else { + onode = (obj_store_node_t *)usrtc_node_getdata(node); + + __obs_wrlock(st); + onode->objdata = od; + __obs_unlock(st); + } + + return 0; +} + +int obj_store_get(obj_store_t *st, const char *nm, void **rod) +{ + usrtc_node_t *node = NULL; + obj_store_node_t *onode = NULL; + + __obs_rdlock(st); + node = usrtc_lookup(__obs_tree(st), nm); + __obs_unlock(st); + + if(!node) return ENOENT; + else onode = (obj_store_node_t *)usrtc_node_getdata(node); + + *rod = onode->objdata; + + return 0; +} + +int obj_store_remove(obj_store_t *st, const char *nm) +{ + usrtc_node_t *node = NULL; + obj_store_node_t *onode = NULL; + + __obs_rdlock(st); + node = usrtc_lookup(__obs_tree(st), nm); + __obs_unlock(st); + + if(!node) return ENOENT; + else { + __obs_wrlock(st); + usrtc_delete(__obs_tree(st), node); + __obs_unlock(st); + + onode = (obj_store_node_t *)usrtc_node_getdata(node); + if(onode->name) free(onode->name); + free(onode); + } + + return 0; +} + diff --git a/ydaemon/values.c b/ydaemon/values.c new file mode 100644 index 0000000..58f6f7b --- /dev/null +++ b/ydaemon/values.c @@ -0,0 +1,636 @@ +/* + * Yet another daemon library especially designed to be used + * with libsxmp based daemons. + * + * (c) Alexander Vdolainen 2016 + * + * 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 ."; + * + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#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; +}