aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2010-06-21 10:43:50 +0300
committerSergey Poznyakoff <gray@gnu.org.ua>2010-06-21 10:43:50 +0300
commit7d63e9e21b74f3b0ca3a87714e19ef1bbfeb7fbf (patch)
tree15b0b4a18fb91cc4b5146b9fe96070886facdcfb
parentb21233c538ebb235a780e068641263801730339d (diff)
downloadsmap-7d63e9e21b74f3b0ca3a87714e19ef1bbfeb7fbf.tar.gz
smap-7d63e9e21b74f3b0ca3a87714e19ef1bbfeb7fbf.tar.bz2
Implement argument transformations.
Argument transformations allow to change map and/or key in the input query before actually processing it. They are implemented at the module level, via additional method in smap_module structure. * include/smap/module.h (SMAP_MODULE_VERSION): Define to 2. (SMAP_CAPA_DEFAULT): Change constant. (SMAP_CAPA_XFORM): New define. (smap_module)<smap_xform>: New member. * src/module.c (_load_module): Fix consistency checking. * src/query.c: New condition "key [op] value". New target "transform dbname". (query_cond_type): New constant query_cond_key. (map_comparison): Rename to comparison. All uses changed. (map_cond): Rename to comp_cond. All uses changed. (dispatch_rule)<xform>: New member. (query_tok)<T_TRANSFORM, T_KEY>: New tokens. (query_kwtab): New keywords: key, transform. (parse_dispatch_map): Rename to parse_dispatch_comp; change signature. All uses changed. (parse_subcond): Handle T_KEY token. (parse_complex_dispatch): Handle T_KEY and T_TRANSFORM. (rule_fixup): New function. (link_dispatch_rules): Use rule_fixup. (find_dispatch_rule): Second argument defines where to start searching. (dispatch_query): Handle transformations. Add checking of return values. * modules/guile/guile.c (guile_proc_ind)<xform_proc>: New index. (guile_proc_name): New proc "xform". (guile_init_db): Bail out if neither query nor xform is defined. (guile_xform): New function. (module): Add guile_xform. * modules/guile/getpw.scm (smap-xform): Implement an example transformation. * modules/echo/echo.c (module): Update. * modules/mailutils/mailutils.c: Likewise.
-rw-r--r--include/smap/module.h15
-rw-r--r--modules/echo/echo.c5
-rw-r--r--modules/guile/getpw.scm7
-rw-r--r--modules/guile/guile.c55
-rw-r--r--modules/mailutils/mailutils.c5
-rw-r--r--src/module.c24
-rw-r--r--src/query.c232
7 files changed, 251 insertions, 92 deletions
diff --git a/include/smap/module.h b/include/smap/module.h
index 120bf6e..27befad 100644
--- a/include/smap/module.h
+++ b/include/smap/module.h
@@ -20,9 +20,11 @@
#define __smap_s_cat3__(a,b,c) a ## b ## c
#define SMAP_EXPORT(module,name) __smap_s_cat3__(module,_LTX_,name)
-#define SMAP_MODULE_VERSION 1
+#define SMAP_MODULE_VERSION 2
#define SMAP_CAPA_NONE 0
-#define SMAP_CAPA_DEFAULT SMAP_CAPA_NONE
+#define SMAP_CAPA_QUERY 0x0001
+#define SMAP_CAPA_XFORM 0x0002
+#define SMAP_CAPA_DEFAULT SMAP_CAPA_QUERY
typedef struct smap_database *smap_database_t;
@@ -37,9 +39,10 @@ struct smap_conninfo {
struct smap_module {
unsigned smap_version;
- unsigned smap_capabilities; /* Unused so far */
+ unsigned smap_capabilities;
int (*smap_init)(int argc, char **argv);
- smap_database_t (*smap_init_db)(const char *dbid, int argc, char **argv);
+ smap_database_t (*smap_init_db)(const char *dbid,
+ int argc, char **argv);
int (*smap_free_db)(smap_database_t dbp);
int (*smap_open) (smap_database_t hp);
int (*smap_close) (smap_database_t hp);
@@ -47,6 +50,10 @@ struct smap_module {
smap_stream_t ostr,
const char *map, const char *key,
struct smap_conninfo const *conninfo);
+ int (*smap_xform)(smap_database_t dbp,
+ const char *map, const char *key,
+ struct smap_conninfo const *conninfo,
+ char **newmap, char **newkey);
};
#endif
diff --git a/modules/echo/echo.c b/modules/echo/echo.c
index ad1d770..57dbbce 100644
--- a/modules/echo/echo.c
+++ b/modules/echo/echo.c
@@ -69,11 +69,12 @@ echo_query(smap_database_t dbp,
struct smap_module SMAP_EXPORT(echo, module) = {
SMAP_MODULE_VERSION,
- SMAP_CAPA_NONE,
+ SMAP_CAPA_DEFAULT,
NULL, /* smap_init */
echo_init_db,
echo_free_db,
NULL, /* smap_open */
NULL, /* smap_close */
- echo_query
+ echo_query,
+ NULL /* smap_xform */
};
diff --git a/modules/guile/getpw.scm b/modules/guile/getpw.scm
index 2fd4a44..88bd724 100644
--- a/modules/guile/getpw.scm
+++ b/modules/guile/getpw.scm
@@ -107,12 +107,19 @@
"NOTFOUND")))
(newline)))
+(define-public (smap-xform handle map arg . rest)
+ (let ((arg-parts (string-split arg #\@)))
+ (if (null? (cdr arg-parts))
+ #f
+ (cons #f (car arg-parts)))))
+
;;; Module initialization function returns an associative list
;;; of methods implemented by the module. Each method is represented
;;; by a cons.
(define (init dbname)
(list
(cons "query" smap-query)
+ (cons "xform" smap-xform)
(cons "open" smap-open)
(cons "close" smap-close)))
diff --git a/modules/guile/guile.c b/modules/guile/guile.c
index 4853ee5..510cd7d 100644
--- a/modules/guile/guile.c
+++ b/modules/guile/guile.c
@@ -328,16 +328,18 @@ enum guile_proc_ind {
done_proc, /* FIXME: this one too */
open_proc,
close_proc,
- query_proc
+ query_proc,
+ xform_proc
};
-#define MAX_PROC (query_proc+1)
+#define MAX_PROC (xform_proc+1)
static char *guile_proc_name[] = {
"init",
"done",
"open",
"close",
- "query"
+ "query",
+ "xform"
};
typedef SCM guile_vtab[MAX_PROC];
@@ -528,9 +530,10 @@ guile_init_db(const char *dbname, int argc, char **argv)
return NULL;
}
- if (!db->vtab[query_proc]) {
- smap_error("guile: %s: %s: void virtual function",
- argv[0], guile_proc_name[query_proc]);
+ if (!db->vtab[query_proc] && !db->vtab[xform_proc]) {
+ smap_error("guile: %s: neither %s nor %s are defined",
+ dbname, guile_proc_name[query_proc],
+ guile_proc_name[xform_proc]);
free(db);
return NULL;
}
@@ -615,13 +618,49 @@ guile_query(smap_database_t dbp,
return res == SCM_BOOL_F;
}
+int
+guile_xform(smap_database_t dbp,
+ const char *map, const char *key,
+ struct smap_conninfo const *conninfo,
+ char **newmap, char **newkey)
+{
+ struct _guile_database *db = (struct _guile_database *)dbp;
+ SCM res, arg;
+
+ if (!db->vtab[xform_proc])
+ return 1;
+
+ arg = scm_append(scm_list_2(scm_list_3(db->handle,
+ scm_from_locale_string(map),
+ scm_from_locale_string(key)),
+ scm_from_smap_conninfo(conninfo)));
+ if (guile_call_proc(&res, db->vtab[xform_proc], arg))
+ return 1;
+ if (!scm_is_pair(res))
+ return 1;
+
+ arg = scm_car(res);
+ if (!scm_is_string(arg))
+ *newmap = NULL;
+ else
+ *newmap = scm_to_locale_string(arg);
+ arg = scm_cdr(res);
+ if (!scm_is_string(arg))
+ *newkey = NULL;
+ else
+ *newkey = scm_to_locale_string(arg);
+
+ return 0;
+}
+
struct smap_module SMAP_EXPORT(guile, module) = {
SMAP_MODULE_VERSION,
- SMAP_CAPA_NONE,
+ SMAP_CAPA_QUERY|SMAP_CAPA_XFORM,
guile_init,
guile_init_db,
guile_free_db,
guile_open,
guile_close,
- guile_query
+ guile_query,
+ guile_xform,
};
diff --git a/modules/mailutils/mailutils.c b/modules/mailutils/mailutils.c
index 1423227..4566b2b 100644
--- a/modules/mailutils/mailutils.c
+++ b/modules/mailutils/mailutils.c
@@ -576,11 +576,12 @@ mod_mailutils_open(smap_database_t dbp)
struct smap_module SMAP_EXPORT(mailutils, module) = {
SMAP_MODULE_VERSION,
- SMAP_CAPA_NONE,
+ SMAP_CAPA_DEFAULT,
mod_mailutils_init,
mod_mailutils_init_db,
mod_mailutils_free_db,
mod_mailutils_open, /* smap_open */
NULL, /* smap_close */
- mod_mailutils_query
+ mod_mailutils_query,
+ NULL, /* smap_xform */
};
diff --git a/src/module.c b/src/module.c
index 6160893..fa79425 100644
--- a/src/module.c
+++ b/src/module.c
@@ -111,13 +111,15 @@ module_declare(const char *file, unsigned line,
}
#define MODULE_ASSERT(cond) \
- if (!(cond)) { \
- lt_dlclose(handle); \
- smap_error("%s: faulty module: (%s) failed", \
- inst->id, \
- #cond); \
- return 1; \
- }
+ do { \
+ if (!(cond)) { \
+ lt_dlclose(handle); \
+ smap_error("%s: faulty module: (%s) failed", \
+ inst->id, \
+ #cond); \
+ return 1; \
+ } \
+ } while (0)
static int
_load_module(struct smap_module_instance *inst)
@@ -149,7 +151,13 @@ _load_module(struct smap_module_instance *inst)
MODULE_ASSERT(pmod->smap_version <= SMAP_MODULE_VERSION);
MODULE_ASSERT(pmod->smap_init_db);
MODULE_ASSERT(pmod->smap_free_db);
- MODULE_ASSERT(pmod->smap_query);
+ if (pmod->smap_version == 1)
+ MODULE_ASSERT(pmod->smap_query);
+ else if (pmod->smap_capabilities & SMAP_CAPA_QUERY)
+ MODULE_ASSERT(pmod->smap_query);
+ else if (pmod->smap_capabilities & SMAP_CAPA_XFORM)
+ MODULE_ASSERT(pmod->smap_xform);
+
if (pmod->smap_init && pmod->smap_init(inst->argc, inst->argv)) {
lt_dlclose(handle);
smap_error("%s: initialization failed", inst->argv[0]);
diff --git a/src/query.c b/src/query.c
index 15dc467..a2ee1f5 100644
--- a/src/query.c
+++ b/src/query.c
@@ -28,17 +28,18 @@ enum query_cond_type {
query_cond_not,
query_cond_source,
query_cond_server,
- query_cond_map
+ query_cond_map,
+ query_cond_key
};
-enum map_comparison {
- map_eq,
- map_like,
- map_re
+enum comparison {
+ comp_eq,
+ comp_like,
+ comp_re
};
-struct map_cond {
- enum map_comparison op;
+struct comp_cond {
+ enum comparison op;
char *str;
regex_t re;
};
@@ -50,7 +51,8 @@ struct query_cond {
struct query_cond *subcond; /* query_cond_not */
struct smap_sockaddr *addr; /* query_cond_source */
char *id; /* query_cond_server */
- struct map_cond mapcond; /* query_cond_map */
+ struct comp_cond comp; /* query_cond_map/
+ query_cond_key */
} v;
};
@@ -61,6 +63,7 @@ struct dispatch_rule {
struct query_cond *cond;
char *dbname;
struct smap_database_instance *dbi;
+ int xform;
};
@@ -128,7 +131,9 @@ enum query_tok {
T_EQ,
T_REGEXP,
T_DB,
- T_NOT
+ T_NOT,
+ T_TRANSFORM,
+ T_KEY
};
static struct smap_kwtab query_kwtab[] = {
@@ -142,6 +147,8 @@ static struct smap_kwtab query_kwtab[] = {
{ "is", T_EQ },
{ "database", T_DB },
{ "not", T_NOT },
+ { "transform", T_TRANSFORM },
+ { "key", T_KEY },
{ NULL }
};
@@ -173,7 +180,8 @@ parse_dispatch_server(struct query_cond **pcond)
}
static int parse_dispatch_not(struct query_cond **pcond);
-static int parse_dispatch_map(struct query_cond **pcond);
+static int parse_dispatch_comp(struct query_cond **pcond,
+ enum query_cond_type t);
static int parse_dispatch_from(struct query_cond **pcond);
static int
@@ -201,7 +209,10 @@ parse_subcond(struct query_cond **pcond)
return parse_dispatch_server(pcond);
case T_MAP:
- return parse_dispatch_map(pcond);
+ return parse_dispatch_comp(pcond, query_cond_map);
+
+ case T_KEY:
+ return parse_dispatch_comp(pcond, query_cond_key);
}
smap_error("%s:%lu: unexpected keyword: %s",
cfg_file_name, cfg_line, s);
@@ -223,7 +234,7 @@ parse_dispatch_not(struct query_cond **pcond)
}
static int
-parse_regexp(const char *s, struct map_cond *mapcond)
+parse_regexp(const char *s, struct comp_cond *comp)
{
int rc;
char *buf;
@@ -270,24 +281,24 @@ parse_regexp(const char *s, struct map_cond *mapcond)
buf = emalloc(len + 1);
memcpy(buf, s + 1, len);
buf[len] = 0;
- rc = regcomp(&mapcond->re, buf, flags);
+ rc = regcomp(&comp->re, buf, flags);
free(buf);
if (rc) {
char errbuf[512];
- regerror(rc, &mapcond->re, errbuf, sizeof(errbuf));
+ regerror(rc, &comp->re, errbuf, sizeof(errbuf));
smap_error("%s:%lu: regexp error: %s",
cfg_file_name, cfg_line, errbuf);
return 1;
}
- mapcond->op = map_re;
+ comp->op = comp_re;
return 0;
}
static int
-parse_dispatch_map(struct query_cond **pcond)
+parse_dispatch_comp(struct query_cond **pcond, enum query_cond_type t)
{
struct query_cond *cond;
- struct map_cond mapcond;
+ struct comp_cond comp;
char *s = nextarg();
int tok;
@@ -300,32 +311,32 @@ parse_dispatch_map(struct query_cond **pcond)
s = nextarg();
if (!s)
return 1;
- mapcond.op = map_like;
+ comp.op = comp_like;
break;
case T_EQ:
s = nextarg();
if (!s)
return 1;
- mapcond.op = map_eq;
+ comp.op = comp_eq;
break;
case T_REGEXP:
s = nextarg();
if (!s)
return 1;
- if (parse_regexp(s, &mapcond))
+ if (parse_regexp(s, &comp))
return 1;
break;
default:
- mapcond.op = map_eq;
+ comp.op = comp_eq;
}
} else
- mapcond.op = map_eq;
- mapcond.str = estrdup(s);
- cond = query_cond_new(query_cond_map);
- cond->v.mapcond = mapcond;
+ comp.op = comp_eq;
+ comp.str = estrdup(s);
+ cond = query_cond_new(t);
+ cond->v.comp = comp;
*pcond = cond;
return 0;
}
@@ -423,7 +434,8 @@ parse_complex_dispatch()
struct query_cond *head = NULL, *tail = NULL;
char *dbname = NULL;
struct dispatch_rule *rp;
-
+ int xform;
+
while (*input && rc == 0) {
char *s = *input++;
int tok;
@@ -450,10 +462,15 @@ parse_complex_dispatch()
break;
case T_MAP:
- rc = parse_dispatch_map(&cond);
+ rc = parse_dispatch_comp(&cond, query_cond_map);
+ break;
+
+ case T_KEY:
+ rc = parse_dispatch_comp(&cond, query_cond_key);
break;
case T_DB:
+ case T_TRANSFORM:
s = nextarg();
if (!s)
rc = 1;
@@ -463,6 +480,7 @@ parse_complex_dispatch()
rc = 1;
} else
dbname = estrdup(s);
+ xform = tok == T_TRANSFORM;
}
if (rc == 1)
break;
@@ -486,6 +504,7 @@ parse_complex_dispatch()
rp->line = cfg_line;
rp->cond = head;
rp->dbname = dbname;
+ rp->xform = xform;
dispatch_attach(rp);
return 0;
}
@@ -532,6 +551,49 @@ parse_dispatch(char **wordv)
return parse_complex_dispatch();
}
+int
+rule_fixup(struct dispatch_rule *p)
+{
+ struct smap_database_instance *dbi;
+ struct smap_module *mod;
+
+ dbi = database_locate(p->dbname);
+ if (!dbi) {
+ smap_error("%s:%u: no such database: %s",
+ p->file, p->line, p->dbname);
+ return 1;
+ }
+
+ mod = dbi->inst->module;
+ if (mod->smap_version == 1) {
+ if (p->xform) {
+ smap_error("%s:%u: database %s does not "
+ "handle transformations",
+ p->file, p->line, p->dbname);
+ return 1;
+ }
+ } else if (mod->smap_version > 1) {
+ if (p->xform) {
+ if (!(mod->smap_capabilities & SMAP_CAPA_XFORM)) {
+ smap_error("%s:%u: database %s does not "
+ "handle transformations",
+ p->file, p->line, p->dbname);
+ return 1;
+ }
+ } else {
+ if (!(mod->smap_capabilities & SMAP_CAPA_QUERY)) {
+ smap_error("%s:%u: database %s does not "
+ "handle queries: %x",
+ p->file, p->line, p->dbname,
+ mod->smap_capabilities);
+ return 1;
+ }
+ }
+ }
+ p->dbi = dbi;
+ return 0;
+}
+
void
link_dispatch_rules()
{
@@ -539,18 +601,12 @@ link_dispatch_rules()
for (p = dispatch_head; p; ) {
struct dispatch_rule *next = p->next;
- struct smap_database_instance *dbi;
-
- dbi = database_locate(p->dbname);
- if (!dbi) {
- smap_error("%s:%u: no such database: %s",
- p->file, p->line, p->dbname);
+ if (rule_fixup(p)) {
debug(DBG_MODULE, 1, ("removing query %s:%u",
p->file, p->line));
dispatch_detach(p);
/* FIXME: free memory */
}
- p->dbi = dbi;
p = next;
}
}
@@ -563,16 +619,16 @@ struct query_pack {
};
static int
-match_map(struct map_cond *cond, const char *map)
+compare(struct comp_cond *cond, const char *map)
{
switch (cond->op) {
- case map_eq:
+ case comp_eq:
return strcmp(cond->str, map) == 0;
- case map_like:
+ case comp_like:
return fnmatch(cond->str, map, 0) == 0;
- case map_re:
+ case comp_re:
return regexec(&cond->re, map, 0, NULL, 0) == 0;
}
return 0;
@@ -594,7 +650,10 @@ match_cond(struct query_cond *cond, struct query_pack *qp)
return strcmp(cond->v.id, qp->server_id) == 0;
case query_cond_map:
- return match_map(&cond->v.mapcond, qp->map);
+ return compare(&cond->v.comp, qp->map);
+
+ case query_cond_key:
+ return compare(&cond->v.comp, qp->key);
}
return 0;
}
@@ -609,11 +668,13 @@ match_cond_list(struct query_cond *cond, struct query_pack *qp)
}
static struct dispatch_rule *
-find_dispatch_rule(struct query_pack *qp)
+find_dispatch_rule(struct query_pack *qp, struct dispatch_rule *start)
{
struct dispatch_rule *p;
- for (p = dispatch_head; p; p = p->next) {
+ if (!start)
+ start = dispatch_head;
+ for (p = start; p; p = p->next) {
debug(DBG_QUERY, 2, ("trying %s:%u", p->file, p->line));
if (match_cond_list(p->cond, qp))
break;
@@ -628,38 +689,73 @@ dispatch_query(const char *id, struct smap_conninfo const *conninfo,
struct smap_database_instance *dbi;
struct smap_module *mod;
struct query_pack query;
- struct dispatch_rule *qr;
-
- debug(DBG_QUERY, 1, ("routing query %s %s", map, key));
+ struct dispatch_rule *next = NULL;
+ char *alloc_map = NULL, *alloc_key = NULL;
+
+ debug(DBG_QUERY, 1, ("dispatching query %s %s", map, key));
query.server_id = id;
query.conninfo = conninfo;
query.map = map;
query.key = key;
- qr = find_dispatch_rule(&query);
- if (qr)
- debug(DBG_QUERY, 1, ("route at %s:%u, database %s",
- qr->file, qr->line, qr->dbname));
- else {
- smap_error("no match for %s %s", map, key);
- smap_stream_printf(ostr, "NOTFOUND\n");
- return;
- }
- dbi = qr->dbi;
- mod = dbi->inst->module;
- if (!dbi->opened) {
- int rc = 0;
-
- debug(DBG_DATABASE, 2, ("opening database %s", dbi->id));
- if (mod->smap_open)
- rc = mod->smap_open(dbi->dbh);
- if (rc) {
- smap_error("cannot open database %s", dbi->id);
- /* FIXME: mark database unusable */
- smap_stream_printf(ostr, "NOTFOUND\n");
+ do {
+ struct dispatch_rule *qr;
+
+ qr = find_dispatch_rule(&query, next);
+ if (qr)
+ debug(DBG_QUERY, 1, ("rule at %s:%u, database %s",
+ qr->file, qr->line, qr->dbname));
+ else
+ break;
+
+ dbi = qr->dbi;
+ mod = dbi->inst->module;
+ if (!dbi->opened) {
+ int rc = 0;
+
+ debug(DBG_DATABASE, 2,
+ ("opening database %s", dbi->id));
+ if (mod->smap_open)
+ rc = mod->smap_open(dbi->dbh);
+ if (rc) {
+ smap_error("cannot open database %s", dbi->id);
+ /* FIXME: mark database unusable */
+ smap_stream_printf(ostr, "NOTFOUND\n");
+ return;
+ }
+ dbi->opened = 1;
+ }
+
+ if (qr->xform) {
+ char *nmap = NULL;
+ char *nkey = NULL;
+ if (mod->smap_xform(dbi->dbh, map, key, conninfo,
+ &nmap, &nkey) == 0) {
+ if (nmap) {
+ if (alloc_map)
+ free(alloc_map);
+ alloc_map = nmap;
+ query.map = nmap;
+ }
+ if (nkey) {
+ if (alloc_key)
+ free(alloc_key);
+ alloc_key = nkey;
+ query.key = nkey;
+ }
+ debug(DBG_QUERY, 1,
+ ("rule at %s:%u, transformed query: %s %s",
+ qr->file, qr->line, query.map, query.key));
+ }
+ next = qr->next;
+ } else {
+ if (mod->smap_query(dbi->dbh, ostr,
+ query.map, query.key,
+ conninfo))
+ break;
return;
}
- dbi->opened = 1;
- }
- mod->smap_query(dbi->dbh, ostr, map, key, conninfo);
+ } while (next);
+ smap_error("no database matches %s %s", map, key);
+ smap_stream_printf(ostr, "NOTFOUND\n");
}

Return to:

Send suggestions and report system problems to the System administrator.