From db3bf25bdf898a60ec5906036847a0c4c06aea08 Mon Sep 17 00:00:00 2001 From: Michael Tuexen <tuexen@fh-muenster.de> Date: Sun, 3 Jun 2018 14:48:13 +0200 Subject: [PATCH] Add support for defines as in Google version 2.0. You can use -Dproto=IPPROTO_SCTP or --define proto=IPPROTO_SCTP and run the script ``` 0.0 socket(..., SOCK_STREAM, proto) = 3 +0.0 close (3) ``` to control the protocol being used from the command line. --- gtests/net/packetdrill/config.c | 42 ++++++++++++++++++++-------- gtests/net/packetdrill/config.h | 49 +++++++++++++++++++++++++++++++++ gtests/net/packetdrill/lexer.l | 31 ++++++++++++++++++++- gtests/net/packetdrill/parse.h | 5 +++- gtests/net/packetdrill/parser.y | 8 +++--- 5 files changed, 117 insertions(+), 18 deletions(-) diff --git a/gtests/net/packetdrill/config.c b/gtests/net/packetdrill/config.c index bff0d71e..1d344781 100644 --- a/gtests/net/packetdrill/config.c +++ b/gtests/net/packetdrill/config.c @@ -58,13 +58,14 @@ enum option_codes { OPT_TCP_TS_TICK_USECS, OPT_NON_FATAL, OPT_DRY_RUN, - OPT_VERBOSE = 'v', /* our only single-letter option */ OPT_DEBUG, OPT_UDP_ENCAPS, #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) OPT_TUN_DEV, OPT_PERSISTENT_TUN_DEV, #endif + OPT_DEFINE = 'D', /* a '-D' single-letter option */ + OPT_VERBOSE = 'v', /* a '-v' single-letter option */ }; /* Specification of command line options for getopt_long(). */ @@ -92,6 +93,7 @@ struct option options[] = { { "tcp_ts_tick_usecs", .has_arg = true, NULL, OPT_TCP_TS_TICK_USECS }, { "non_fatal", .has_arg = true, NULL, OPT_NON_FATAL }, { "dry_run", .has_arg = false, NULL, OPT_DRY_RUN }, + { "define", .has_arg = true, NULL, OPT_DEFINE }, { "verbose", .has_arg = false, NULL, OPT_VERBOSE }, { "debug", .has_arg = false, NULL, OPT_DEBUG }, { "udp_encapsulation", .has_arg = true, NULL, OPT_UDP_ENCAPS }, @@ -128,6 +130,7 @@ void show_usage(void) "\t[--wire_client_dev=<eth_dev_name>]\n" "\t[--wire_server_dev=<eth_dev_name>]\n" "\t[--dry_run]\n" + "\t[--define symbol1=val1 --define symbol2=val2 ...]\n" "\t[--verbose|-v]\n" "\t[--debug] * requires compilation with DEBUG *\n" "\t[--udp_encapsulation=[sctp,tcp]]\n" @@ -391,7 +394,7 @@ static void process_option(int opt, char *optarg, struct config *config, char *where) { int port = 0; - char *end = NULL; + char *end = NULL, *equals = NULL, *symbol = NULL, *value = NULL; unsigned long speed = 0; DEBUGP("process_option %d = %s\n", opt, optarg); @@ -494,6 +497,14 @@ static void process_option(int opt, char *optarg, struct config *config, case OPT_DRY_RUN: config->dry_run = true; break; + case OPT_DEFINE: + equals = strstr(optarg, "="); + if (equals == optarg || equals == NULL) + die("%s: bad definition: %s\n", where, optarg); + symbol = strndup(optarg, equals - optarg); + value = strdup(equals + 1); + definition_set(&config->defines, symbol, value); + break; case OPT_VERBOSE: config->verbose = true; break; @@ -550,7 +561,7 @@ char **parse_command_line_options(int argc, char *argv[], /* Parse the arguments. */ optind = 0; - while ((c = getopt_long(argc, argv, "v", options, NULL)) > 0) + while ((c = getopt_long(argc, argv, "vD:", options, NULL)) > 0) process_option(c, optarg, config, "Command Line"); return argv + optind; } @@ -560,6 +571,7 @@ static void parse_script_options(struct config *config, { struct option_list *opt = option_list; while (opt != NULL) { +printf("opt->name = %s\n", opt->name); int i; int c = 0; for (i = 0; options[i].name != NULL; i++) { @@ -568,15 +580,21 @@ static void parse_script_options(struct config *config, break; } } - if (c != 0) { - process_option(options[i].val, - opt->value, config, - config->script_path); - } else { - die("%s: option '%s' unknown in file: %s\n", - config->script_path, opt->name, - config->script_path); - } + + if (!c) + die("%s: option '%s' unknown\n", + config->script_path, opt->name); + if (opt->value && !options[i].has_arg) + die("%s: option '%s' forbids an argument\n", + config->script_path, opt->name); + if (!opt->value && options[i].has_arg) + die("%s: option '%s' requires an argument\n", + config->script_path, opt->name); + + process_option(options[i].val, + opt->value, config, + config->script_path); + opt = opt->next; } } diff --git a/gtests/net/packetdrill/config.h b/gtests/net/packetdrill/config.h index 3ee061d4..561785d3 100644 --- a/gtests/net/packetdrill/config.h +++ b/gtests/net/packetdrill/config.h @@ -40,6 +40,52 @@ extern struct option options[]; +/* A linked list of symbol->value (FOO=bar) definitions from command line. */ +struct definition { + char *symbol; /* name of the symbol; owns the string */ + char *value; /* value of the symbol; owns the string */ + struct definition *next; /* link for linked list */ +}; + +/* Return the definition in the linked list with a matching symbol, or NULL */ +static inline struct definition *definition_find(struct definition *defs, + char *symbol) +{ + struct definition *def = NULL; + + for (def = defs; def != NULL; def = def->next) { + if (strcmp(def->symbol, symbol) == 0) + return def; + } + return NULL; +} + +/* Set the value of the given symbol to the given value. */ +static inline void definition_set(struct definition **defs, + char *symbol, char *value) +{ + struct definition *def = definition_find(*defs, symbol); + + if (def) { + free(def->value); + def->value = value; + } else { + def = calloc(1, sizeof(struct definition)); + def->symbol = symbol; + def->value = value; + def->next = *defs; /* link to existing entries */ + *defs = def; /* insert at head of linked list */ + } +} + +/* Return the value of the given symbol, or NULL if not found. */ +static inline char *definition_get(struct definition *defs, char *symbol) +{ + struct definition *def = definition_find(defs, symbol); + + return def ? def->value : NULL; +} + struct config { const char **argv; /* a copy of process argv */ @@ -112,6 +158,9 @@ struct config { char *tun_device; bool persistent_tun_device; #endif + + /* List of FOO=bar definitions from command line. */ + struct definition *defines; }; /* Top-level info about the invocation of a test script */ diff --git a/gtests/net/packetdrill/lexer.l b/gtests/net/packetdrill/lexer.l index a91ab140..b46d1c32 100644 --- a/gtests/net/packetdrill/lexer.l +++ b/gtests/net/packetdrill/lexer.l @@ -49,6 +49,8 @@ #include <stdio.h> #include "script.h" #include "tcp_options.h" +#include "parse.h" +#include "config.h" /* This include of the bison-generated .h file must go last so that we * can first include all of the declarations on which it depends. @@ -75,6 +77,33 @@ static char *quoted(const char *s) return strndup(s + delim_len, strlen(s) - 2*delim_len); } +/* Check to see if the word in yytext is a user-defined symbol, and if so then + * return its value. Otherwise return the word itself. + */ +int word(void) +{ + char *word = yytext; + char *value = NULL; + + /* Look in symbol table for matching user-defined symbol->value map. */ + value = definition_get(in_config->defines, word); + if (value) { + if (value[0] == '"') { + yylval.string = quoted(value); /* SYM="val" */ + return STRING; + } else if (value[0] == '`') { + yylval.string = quoted(value); /* SYM=`val` */ + return BACK_QUOTED; + } else { + yylval.string = strdup(value); /* SYM=val */ + return WORD; + } + } + /* A literal word (e.g. system call name or socket option name). */ + yylval.string = strdup(word); + return WORD; +} + /* Copy the code inside a code snippet that is enclosed in %{ }% after * first stripping the space and tab characters from either end of the * snippet. We strip leading and trailing whitespace for Python users @@ -655,7 +684,7 @@ NULL return NULL_; [-]?[0-9]*[.][0-9]+ yylval.floating = atof(yytext); return FLOAT; [-]?[0-9]+ yylval.integer = atoll(yytext); return INTEGER; 0x[0-9a-fA-F]+ yylval.integer = hextol(yytext); return HEX_INTEGER; -[a-zA-Z0-9_]+ yylval.string = strdup(yytext); return WORD; +[a-zA-Z0-9_]+ return word(); \"(\\.|[^"])*\" yylval.string = quoted(yytext); return STRING; \`(\\.|[^`])*\` yylval.string = quoted(yytext); return BACK_QUOTED; [^ \t\n] return (int) yytext[0]; diff --git a/gtests/net/packetdrill/parse.h b/gtests/net/packetdrill/parse.h index f7ba2b78..989ebced 100644 --- a/gtests/net/packetdrill/parse.h +++ b/gtests/net/packetdrill/parse.h @@ -52,8 +52,11 @@ extern void read_script(const char *script_path, * implementation for this function is in the bison parser file * parser.y. */ -extern int parse_script(const struct config *config, +extern int parse_script(struct config *config, struct script *script, struct invocation *callback_invocation); +/* Config for lexing and parsing. */ +extern struct config *in_config; + #endif /* __PARSER_H__ */ diff --git a/gtests/net/packetdrill/parser.y b/gtests/net/packetdrill/parser.y index 739184eb..d00f6775 100644 --- a/gtests/net/packetdrill/parser.y +++ b/gtests/net/packetdrill/parser.y @@ -141,7 +141,7 @@ static int current_script_line = -1; * We uses this object to look up configuration info needed during * parsing (such as whether packets are IPv4 or IPv6). */ -static const struct config *in_config = NULL; +struct config *in_config = NULL; /* The output of the parser: an output script containing * 1) a linked list of options @@ -228,9 +228,9 @@ void read_script(const char *script_path, struct script *script) * text script file with the given path name and fills in the script * object with the parsed representation. */ -int parse_script(const struct config *config, - struct script *script, - struct invocation *callback_invocation) +int parse_script(struct config *config, + struct script *script, + struct invocation *callback_invocation) { /* This bison-generated parser is not multi-thread safe, so we * have a lock to prevent more than one thread using the -- GitLab