From: Wouter Wijngaards Date: Mon, 31 Aug 2009 15:58:38 +0000 (+0000) Subject: variable processing. X-Git-Tag: release-1.4.0rc1~108 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=a24f9ff9eab856149cec887465ccbea51c70a60a;p=thirdparty%2Funbound.git variable processing. git-svn-id: file:///svn/unbound/trunk@1795 be551aaa-1e26-0410-a405-d3ace91eadb9 --- diff --git a/daemon/unbound.c b/daemon/unbound.c index d516bef4b..424766d90 100644 --- a/daemon/unbound.c +++ b/daemon/unbound.c @@ -120,6 +120,14 @@ static void usage() printf("Report bugs to %s\n", PACKAGE_BUGREPORT); } +#ifndef unbound_testbound +int replay_var_compare(const void* ATTR_UNUSED(a), const void* ATTR_UNUSED(b)) +{ + log_assert(0); + return 0; +} +#endif + /** check file descriptor count */ static void checkrlimits(struct config_file* cfg) diff --git a/daemon/worker.c b/daemon/worker.c index 99ec82233..761e9af2b 100644 --- a/daemon/worker.c +++ b/daemon/worker.c @@ -1305,3 +1305,4 @@ int codeline_cmp(const void* ATTR_UNUSED(a), const void* ATTR_UNUSED(b)) log_assert(0); return 0; } + diff --git a/doc/Changelog b/doc/Changelog index b03f08a6e..7c0aeea8e 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,6 @@ +31 August 2009: Wouter + - testbound variable processing. + 28 August 2009: Wouter - fixup unbound-control lookup to print forward and stub servers. diff --git a/libunbound/libworker.c b/libunbound/libworker.c index 44d21419d..be0c5b47e 100644 --- a/libunbound/libworker.c +++ b/libunbound/libworker.c @@ -851,6 +851,12 @@ codeline_cmp(const void* ATTR_UNUSED(a), const void* ATTR_UNUSED(b)) return 0; } +int replay_var_compare(const void* ATTR_UNUSED(a), const void* ATTR_UNUSED(b)) +{ + log_assert(0); + return 0; +} + #ifdef UB_ON_WINDOWS void worker_win_stop_cb(int ATTR_UNUSED(fd), short ATTR_UNUSED(ev), void* diff --git a/smallapp/worker_cb.c b/smallapp/worker_cb.c index 8e7dc5b57..f5f6f5335 100644 --- a/smallapp/worker_cb.c +++ b/smallapp/worker_cb.c @@ -233,3 +233,9 @@ codeline_cmp(const void* a, const void* b) { return strcmp((const char*)a, (const char*)b); } + +int replay_var_compare(const void* ATTR_UNUSED(a), const void* ATTR_UNUSED(b)) +{ + log_assert(0); + return 0; +} diff --git a/testcode/fake_event.c b/testcode/fake_event.c index c3407bfde..28fe2fad2 100644 --- a/testcode/fake_event.c +++ b/testcode/fake_event.c @@ -708,6 +708,8 @@ comm_base_create(int ATTR_UNUSED(sigs)) struct replay_runtime* runtime = (struct replay_runtime*) calloc(1, sizeof(struct replay_runtime)); runtime->scenario = saved_scenario; + runtime->vars = macro_store_create(); + if(!runtime->vars) fatal_exit("out of memory"); return (struct comm_base*)runtime; } @@ -739,6 +741,7 @@ comm_base_delete(struct comm_base* b) free(t); t = nt; } + macro_store_delete(runtime->vars); free(runtime); } diff --git a/testcode/replay.c b/testcode/replay.c index e97017b50..1e1ce208b 100644 --- a/testcode/replay.c +++ b/testcode/replay.c @@ -49,6 +49,17 @@ /** max length of lines in file */ #define MAX_LINE_LEN 10240 +/** + * Expand a macro + * @param store: value storage + * @param runtime: replay runtime for other stuff. + * @param text: the macro text, after the ${, Updated to after the } when + * done (successfully). + * @return expanded text, malloced. NULL on failure. + */ +static char* macro_expand(rbtree_t* store, + struct replay_runtime* runtime, char** text); + /** parse keyword in string. * @param line: if found, the line is advanced to after the keyword. * @param keyword: string. @@ -420,3 +431,328 @@ replay_scenario_delete(struct replay_scenario* scen) } free(scen); } + +int +replay_var_compare(const void* a, const void* b) +{ + struct replay_var* x = (struct replay_var*)a; + struct replay_var* y = (struct replay_var*)b; + return strcmp(x->name, y->name); +} + +rbtree_t* +macro_store_create(void) +{ + return rbtree_create(&replay_var_compare); +} + +/** helper function to delete macro values */ +static void +del_macro(rbnode_t* x, void* ATTR_UNUSED(arg)) +{ + struct replay_var* v = (struct replay_var*)x; + free(v->name); + free(v->value); + free(v); +} + +void +macro_store_delete(rbtree_t* store) +{ + if(!store) + return; + traverse_postorder(store, del_macro, NULL); + free(store); +} + +/** return length of macro */ +static size_t +macro_length(char* text) +{ + /* we are after ${, looking for } */ + int depth = 0; + size_t len = 0; + while(*text) { + len++; + if(*text == '}') { + if(depth == 0) + break; + depth--; + } else if(text[0] == '$' && text[1] == '{') { + depth++; + } + text++; + } + return len; +} + +/** insert new stuff at start of buffer */ +static int +do_buf_insert(char* buf, size_t remain, char* after, char* inserted) +{ + char* save = strdup(after); + if(!save) return 0; + if(strlen(inserted) > remain) { + free(save); + return 0; + } + strlcpy(buf, inserted, remain); + buf += strlen(inserted); + remain -= strlen(inserted); + strlcpy(buf, save, remain); + free(save); + return 1; +} + +/** do macro recursion */ +static char* +do_macro_recursion(rbtree_t* store, struct replay_runtime* runtime, + char* at, size_t remain) +{ + char* after = at+2; + char* expand = macro_expand(store, runtime, &after); + if(!expand) + return NULL; /* expansion failed */ + if(!do_buf_insert(at, remain, after, expand)) { + free(expand); + return NULL; + } + free(expand); + return at; /* and parse over the expanded text to see if again */ +} + +/** get var from store */ +struct replay_var* +macro_getvar(rbtree_t* store, char* name) +{ + struct replay_var k; + k.node.key = &k; + k.name = name; + return (struct replay_var*)rbtree_search(store, &k); +} + +/** do macro variable */ +static char* +do_macro_variable(rbtree_t* store, char* buf, size_t remain) +{ + struct replay_var* v; + char* at = buf+1; + char* name = at; + char sv; + if(at[0]==0) + return NULL; /* no variable name after $ */ + while(*at && (isalnum((int)*at) || *at=='_')) { + at++; + } + /* terminator, we are working in macro_expand() buffer */ + sv = *at; + *at = 0; + v = macro_getvar(store, name); + *at = sv; + + if(!v) { + log_err("variable is not defined: $%s", name); + return NULL; /* variable undefined is error for now */ + } + + /* insert the variable contents */ + if(!do_buf_insert(buf, remain, at, v->value)) + return NULL; + return buf; /* and expand the variable contents */ +} + +/** do ctime macro on argument */ +static char* +do_macro_ctime(char* arg) +{ + char buf[32]; + time_t tt = atoi(arg); + if(tt == 0 && strcmp(arg, "0") != 0) { + log_err("macro ctime: expected number, not: %s", arg); + return NULL; + } + ctime_r(&tt, buf); + return strdup(buf); +} + +static char* +macro_expand(rbtree_t* store, struct replay_runtime* runtime, char** text) +{ + char buf[10240]; + char* at = *text; + size_t len = macro_length(at); + int dofunc = 0; + /*printf("macro: '%s', len=%d\n", at, len);*/ + if(len >= sizeof(buf)) + return NULL; /* too long */ + buf[0] = 0; + strlcpy(buf, at, len+1-1); /* do not copy last '}' character */ + at = buf; + + /* check for functions */ + if(strcmp(buf, "time") == 0) { + snprintf(buf, sizeof(buf), "%u", (unsigned)runtime->now_secs); + return strdup(buf); + } else if(strcmp(buf, "timeout") == 0) { + /* TODO: find timeout value */ + snprintf(buf, sizeof(buf), "%u", (unsigned)runtime->now_secs); + return strdup(buf); + } else if(strncmp(buf, "ctime ", 6) == 0 || + strncmp(buf, "ctime\t", 6) == 0) { + at += 6; + dofunc = 1; + } + + /* actual macro text expansion */ + while(*at) { + size_t remain = sizeof(buf)-strlen(buf); + if(strncmp(at, "${", 2) == 0) { + at = do_macro_recursion(store, runtime, at, remain); + } else if(*at == '$') { + at = do_macro_variable(store, at, remain); + } else if(isdigit((int)*at)) { + /* TODO replace arithmatic with result */ + } else { + /* copy until whitespace */ + at++; + while(*at && !isblank((int)*at) && *at != '$') + at++; + } + if(!at) return NULL; /* failure */ + } + *text += len; + if(dofunc) { + /* post process functions, buf has the argument(s) */ + if(strncmp(*text, "ctime", 5) == 0) { + return do_macro_ctime(buf); + } + } + /*printf("macro return '%s'\n", buf);*/ + return strdup(buf); +} + +char* +macro_process(rbtree_t* store, struct replay_runtime* runtime, char* text) +{ + char buf[10240]; + char* next, *expand; + char* at = text; + if(!strstr(text, "${")) + return strdup(text); /* no macros */ + buf[0] = 0; + buf[sizeof(buf)-1]=0; + while( (next=strstr(at, "${")) ) { + /* copy text before next macro */ + if((size_t)(next-at) >= sizeof(buf)-strlen(buf)) + return NULL; /* string too long */ + strlcpy(buf+strlen(buf), at, next-at+1); + /* process the macro itself */ + next += 2; + expand = macro_expand(store, runtime, &next); + if(!expand) return NULL; /* expansion failed */ + strlcpy(buf+strlen(buf), expand, sizeof(buf)-strlen(buf)); + free(expand); + at = next; + } + /* copy remainder fixed text */ + strlcpy(buf+strlen(buf), at, sizeof(buf)-strlen(buf)); + return strdup(buf); +} + +char* +macro_lookup(rbtree_t* store, char* name) +{ + struct replay_var* x = macro_getvar(store, name); + if(!x) return strdup(""); + return strdup(x->value); +} + +int +macro_assign(rbtree_t* store, char* name, char* value) +{ + struct replay_var* x = macro_getvar(store, name); + if(x) { + free(x->value); + } else { + x = (struct replay_var*)malloc(sizeof(*x)); + if(!x) return 0; + x->node.key = x; + x->name = strdup(name); + if(!x->name) { + free(x); + return 0; + } + (void)rbtree_insert(store, &x->node); + } + x->value = strdup(value); + return x->value != NULL; +} + +void testbound_selftest(void) +{ + /* test the macro store */ + rbtree_t* store = macro_store_create(); + char* v; + int r; + log_assert(store); + + v = macro_lookup(store, "bla"); + log_assert(strcmp(v, "") == 0); + free(v); + + v = macro_lookup(store, "vlerk"); + log_assert(strcmp(v, "") == 0); + free(v); + + r = macro_assign(store, "bla", "waarde1"); + log_assert(r); + + v = macro_lookup(store, "vlerk"); + log_assert(strcmp(v, "") == 0); + free(v); + + v = macro_lookup(store, "bla"); + log_assert(strcmp(v, "waarde1") == 0); + free(v); + + r = macro_assign(store, "vlerk", "kanteel"); + log_assert(r); + + v = macro_lookup(store, "bla"); + log_assert(strcmp(v, "waarde1") == 0); + free(v); + + v = macro_lookup(store, "vlerk"); + log_assert(strcmp(v, "kanteel") == 0); + free(v); + + r = macro_assign(store, "bla", "ww"); + log_assert(r); + + v = macro_lookup(store, "bla"); + log_assert(strcmp(v, "ww") == 0); + free(v); + + log_assert( macro_length("}") == 1); + log_assert( macro_length("blabla}") == 7); + log_assert( macro_length("bla${zoink}bla}") == 7+8); + log_assert( macro_length("bla${zoink}${bla}bla}") == 7+8+6); + + v = macro_process(store, NULL, ""); + log_assert( v && strcmp(v, "") == 0); + free(v); + + v = macro_process(store, NULL, "${}"); + log_assert( v && strcmp(v, "") == 0); + free(v); + + v = macro_process(store, NULL, "blabla ${} dinges"); + log_assert( v && strcmp(v, "blabla dinges") == 0); + free(v); + + v = macro_process(store, NULL, "1${$bla}2${$bla}3"); + log_assert( v && strcmp(v, "1ww2ww3") == 0); + free(v); + + macro_store_delete(store); +} diff --git a/testcode/replay.h b/testcode/replay.h index 1428428d3..b1321321e 100644 --- a/testcode/replay.h +++ b/testcode/replay.h @@ -75,6 +75,17 @@ * ; more STEP items * SCENARIO_END * + * Calculations, a macro-like system: ${$myvar + 3600} + * STEP 10 ASSIGN $myvar = 3600 + * ; ASSIGN event. '=' is syntactic sugar here. 3600 is some expression. + * ${..} is macro expanded from its expression. Text substitution. + * o $var replaced with its value. var is identifier [azAZ09_]* + * o number is that number. + * o +, -, / and *. Note, evaluated left-to-right. Use ${} for brackets. + * o ${time} is the current time. + * o ${ctime value} is the text ctime(value), i.e. Fri 3 Aug 2009, ... + * must have one space after 'ctime'. + * o ${timeout} is the time until next timeout in the comm_timer list. * * ; Example file * SCENARIO_BEGIN Example scenario @@ -108,11 +119,13 @@ #define TESTCODE_REPLAY_H #include "util/netevent.h" #include "testcode/ldns-testpkts.h" +#include "util/rbtree.h" struct replay_answer; struct replay_moment; struct replay_range; struct fake_pending; struct fake_timer; +struct replay_var; /** * A replay scenario. @@ -267,6 +280,11 @@ struct replay_runtime { /** size of buffers */ size_t bufsize; + + /** + * Tree of macro values. Of type replay_var + */ + rbtree_t* vars; }; /** @@ -328,6 +346,18 @@ struct fake_timer { struct timeval tv; }; +/** + * Replay macro variable. And its value. + */ +struct replay_var { + /** rbtree node. Key is this structure. Sorted by name. */ + rbnode_t node; + /** the variable name */ + char* name; + /** the variable value */ + char* value; +}; + /** * Read a replay scenario from the file. * @param in: file to read from. @@ -344,4 +374,50 @@ struct replay_scenario* replay_scenario_read(FILE* in, const char* name, */ void replay_scenario_delete(struct replay_scenario* scen); +/** compare two replay_vars */ +int replay_var_compare(const void* a, const void* b); + +/** + * Create variable storage + * @return new or NULL on failure. + */ +rbtree_t* macro_store_create(void); + +/** + * Delete variable storage + * @param store: the macro storage to free up. + */ +void macro_store_delete(rbtree_t* store); + +/** + * Apply macro substitution to string. + * @param store: variable store. + * @param runtime: the runtime to look up values as needed. + * @param text: string to work on. + * @return newly malloced string with result. + */ +char* macro_process(rbtree_t* store, struct replay_runtime* runtime, + char* text); + +/** + * Look up a macro value. Like calling ${$name}. + * @param store: variable store + * @param name: macro name + * @return newly malloced string with result or strdup("") if not found. + * or NULL on malloc failure. + */ +char* macro_lookup(rbtree_t* store, char* name); + +/** + * Set macro value. + * @param store: variable store + * @param name: macro name + * @param value: text to set it to. Not expanded. + * @return false on failure. + */ +int macro_assign(rbtree_t* store, char* name, char* value); + +/** testbounds self test */ +void testbound_selftest(void); + #endif /* TESTCODE_REPLAY_H */ diff --git a/testcode/testbound.c b/testcode/testbound.c index 106cd9870..1552f3ee4 100644 --- a/testcode/testbound.c +++ b/testcode/testbound.c @@ -45,6 +45,8 @@ #include "daemon/remote.h" #include "util/config_file.h" +/** signal that this is a testbound compile */ +#define unbound_testbound 1 /** * include the main program from the unbound daemon. * rename main to daemon_main to call it @@ -68,6 +70,7 @@ testbound_usage() printf("-p file playback text file\n"); printf("-2 detect SHA256 support (exit code 0 or 1)\n"); printf("-g detect GOST support (exit code 0 or 1)\n"); + printf("-s testbound self-test - unit test of testbound parts.\n"); printf("-o str unbound commandline options separated by spaces.\n"); printf("Version %s\n", PACKAGE_VERSION); printf("BSD licensed, see LICENSE file in source package.\n"); @@ -265,8 +268,13 @@ main(int argc, char* argv[]) pass_argc = 1; pass_argv[0] = "unbound"; add_opts("-d", &pass_argc, pass_argv); - while( (c=getopt(argc, argv, "2gho:p:")) != -1) { + while( (c=getopt(argc, argv, "2gho:p:s")) != -1) { switch(c) { + case 's': + free(pass_argv[1]); + testbound_selftest(); + printf("selftest successful\n"); + exit(0); case '2': #if defined(HAVE_EVP_SHA256) && defined(USE_SHA2) printf("SHA256 supported\n"); diff --git a/testdata/03-testbound.tpkg b/testdata/03-testbound.tpkg index 79803d5ad..ef2852b46 100644 Binary files a/testdata/03-testbound.tpkg and b/testdata/03-testbound.tpkg differ diff --git a/util/fptr_wlist.c b/util/fptr_wlist.c index 5b395a671..f973c6bec 100644 --- a/util/fptr_wlist.c +++ b/util/fptr_wlist.c @@ -184,6 +184,7 @@ fptr_whitelist_rbtree_cmp(int (*fptr) (const void *, const void *)) else if(fptr == &val_neg_data_compare) return 1; else if(fptr == &val_neg_zone_compare) return 1; else if(fptr == &probetree_cmp) return 1; + else if(fptr == &replay_var_compare) return 1; return 0; } diff --git a/util/fptr_wlist.h b/util/fptr_wlist.h index 11a0905d0..4338b2fb7 100644 --- a/util/fptr_wlist.h +++ b/util/fptr_wlist.h @@ -334,4 +334,7 @@ int order_lock_cmp(const void* e1, const void* e2); */ int codeline_cmp(const void* a, const void* b); +/** compare two replay_vars */ +int replay_var_compare(const void* a, const void* b); + #endif /* UTIL_FPTR_WLIST_H */