]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
variable processing.
authorWouter Wijngaards <wouter@nlnetlabs.nl>
Mon, 31 Aug 2009 15:58:38 +0000 (15:58 +0000)
committerWouter Wijngaards <wouter@nlnetlabs.nl>
Mon, 31 Aug 2009 15:58:38 +0000 (15:58 +0000)
git-svn-id: file:///svn/unbound/trunk@1795 be551aaa-1e26-0410-a405-d3ace91eadb9

12 files changed:
daemon/unbound.c
daemon/worker.c
doc/Changelog
libunbound/libworker.c
smallapp/worker_cb.c
testcode/fake_event.c
testcode/replay.c
testcode/replay.h
testcode/testbound.c
testdata/03-testbound.tpkg
util/fptr_wlist.c
util/fptr_wlist.h

index d516bef4b1b3f7d9e0c2524bdf7646ca85b45e38..424766d9011669f432698439c329bf35aec9605f 100644 (file)
@@ -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)
index 99ec8223319913ae48cde78435a7218defdbcc04..761e9af2bf941ac7b4ae41596ec47fa73ccf5693 100644 (file)
@@ -1305,3 +1305,4 @@ int codeline_cmp(const void* ATTR_UNUSED(a), const void* ATTR_UNUSED(b))
         log_assert(0);
         return 0;
 }
+
index b03f08a6e7a9eb8f426b287e1afc722c8355c39a..7c0aeea8e1333c147589fd536ef82fa83e444a6f 100644 (file)
@@ -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.
 
index 44d21419d4890f8cac58c5465172e10ab13f4082..be0c5b47e760fca7f88d67c81c539d79ab13cf55 100644 (file)
@@ -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* 
index 8e7dc5b5755025a858bc7cb5d799592c95a58a48..f5f6f5335a41dfc35d37f90e6f33406b460e33e6 100644 (file)
@@ -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;
+}
index c3407bfde14cb2d9d8dee2a857a17890c0210fbe..28fe2fad2edea7c716fed8d8f48e26723d0d517b 100644 (file)
@@ -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);
 }
 
index e97017b50792da9096c53e92092b0e306bbac060..1e1ce208b933ff3900598d6e0ca4a12d72bd7ca5 100644 (file)
 /** 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);
+}
index 1428428d3cd911e0ef6fe514c5ac8b6444660bad..b1321321e1d9e7afe2f26548627bf127786ed3d8 100644 (file)
  * ; 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
 #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 */
index 106cd9870ba8aa2d89acacbc134592c3279f850f..1552f3ee478c80fb8f0c514b15335c7a92a0eafb 100644 (file)
@@ -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");
index 79803d5ad692069717aaea9279530054ac759bfe..ef2852b4633c16e85d6a30f6837169d9e8fdae9f 100644 (file)
Binary files a/testdata/03-testbound.tpkg and b/testdata/03-testbound.tpkg differ
index 5b395a6718011bf50193a540606baa9008a7e245..f973c6becaefcf78f501f8f0c38017995b573060 100644 (file)
@@ -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;
 }
 
index 11a0905d0f565a78148b0d5c8f3bc8e2fa4ec9e3..4338b2fb7d06e9944a67735e8807b31df766208c 100644 (file)
@@ -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 */