diff --git a/gtests/net/packetdrill/logging.c b/gtests/net/packetdrill/logging.c index 546b5d1188a87f1145378e5c5bc30b6ca45a2aba..730add4b8c8683cf8631a0b18cefa231ef397013 100644 --- a/gtests/net/packetdrill/logging.c +++ b/gtests/net/packetdrill/logging.c @@ -22,12 +22,13 @@ * Logging and output functions. */ -#include "logging.h" +#include "run.h" +#include "system.h" #include <stdarg.h> #include <stdlib.h> -extern void die(char *format, ...) +extern void __attribute__((noreturn)) die(char *format, ...) { va_list ap; @@ -35,12 +36,16 @@ extern void die(char *format, ...) vfprintf(stderr, format, ap); va_end(ap); + run_cleanup_command(); + exit(EXIT_FAILURE); } -void die_perror(char *message) +void __attribute__((noreturn)) die_perror(char *message) { perror(message); + run_cleanup_command(); + exit(EXIT_FAILURE); } diff --git a/gtests/net/packetdrill/logging.h b/gtests/net/packetdrill/logging.h index a537ffa4e6dc42b9e830693a422e708b1017a253..2e17bd097c522bb58cbebd1b7ffe686fcfc3368c 100644 --- a/gtests/net/packetdrill/logging.h +++ b/gtests/net/packetdrill/logging.h @@ -42,9 +42,9 @@ extern int debug_logging; #endif /* DEBUG */ /* Log the message to stderr and then exit with a failure status code. */ -extern void die(char *format, ...); +extern void __attribute__((noreturn)) die(char *format, ...); /* Call perror() with message and then exit with a failure status code. */ -extern void die_perror(char *message); +extern void __attribute__((noreturn)) die_perror(char *message); #endif /* __LOGGING_H__ */ diff --git a/gtests/net/packetdrill/parser.y b/gtests/net/packetdrill/parser.y index 54c4ee119d09cf46af5179fbe09c21ee2aa996e1..2bff622a50bed1691ea5e52ef7c596c797a6ec87 100644 --- a/gtests/net/packetdrill/parser.y +++ b/gtests/net/packetdrill/parser.y @@ -123,6 +123,7 @@ extern char *yytext; extern int yylex(void); extern int yyparse(void); extern int yywrap(void); +extern const char *cleanup_cmd; /* This mutex guards all parser global variables declared in this file. */ pthread_mutex_t parser_mutex = PTHREAD_MUTEX_INITIALIZER; @@ -811,7 +812,7 @@ static struct tcp_option *new_tcp_exp_fast_open_option(const char *cookie_string %% /* The grammar follows. */ script -: opt_options opt_init_command events { +: opt_options opt_init_command events opt_cleanup_command { $$ = NULL; /* The parser output is in out_script */ } ; @@ -5851,3 +5852,15 @@ null $$ = new_expression(EXPR_NULL); } ; + +opt_cleanup_command +: { } +| cleanup_command { } +; + +cleanup_command +: command_spec { + out_script->cleanup_command = $1; + cleanup_cmd = out_script->cleanup_command->command_line; +} +; diff --git a/gtests/net/packetdrill/run.c b/gtests/net/packetdrill/run.c index f079e185a624c57d07dc0e80d8bf9356449e105c..151301ed756777760c303040706c76217b30986f 100644 --- a/gtests/net/packetdrill/run.c +++ b/gtests/net/packetdrill/run.c @@ -68,6 +68,15 @@ const int MAX_SPIN_USECS = 100; const int MAX_SPIN_USECS = 20; #endif +/* Global bool init_cmd_exed */ +bool init_cmd_exed = false; + +/* Final command to always execute at end of script, in order to clean up: */ +const char *cleanup_cmd; + +/* Path of currently-executing script, for use in cleanup command errors: */ +const char *script_path; + static struct state *state = NULL; struct state *state_new(struct config *config, @@ -514,7 +523,8 @@ static s64 schedule_start_time_usecs(void) #endif } -void signal_handler(int signal_number) { +void signal_handler(int signal_number) +{ if (state != NULL) { close_all_sockets(state); if (state->netdev != NULL) { @@ -524,6 +534,28 @@ void signal_handler(int signal_number) { die("Handled signal %d\n", signal_number); } +/* Run final command we always execute at end of script, to clean up. If there + * is a cleanup command at the end of a packetdrill script, we execute that no + * matter whether the test passes or fails. This makes the cleanup command a + * good place to undo any sysctl settings the script changed, for example. + */ +int run_cleanup_command(void) +{ + if (cleanup_cmd != NULL && init_cmd_exed) { + char *error = NULL; + + if (safe_system(cleanup_cmd, &error)) { + fprintf(stderr, + "%s: error executing cleanup command: %s\n", + script_path, error); + free(error); + return STATUS_ERR; + } + } + return STATUS_OK; +} + + void run_script(struct config *config, struct script *script) { char *error = NULL; @@ -548,6 +580,8 @@ void run_script(struct config *config, struct script *script) /* This interpreter loop runs for local mode or wire client mode. */ assert(!config->is_wire_server); + script_path = config->script_path; + /* How we use the network is of course a little different in * each of the two cases.... */ @@ -563,12 +597,16 @@ void run_script(struct config *config, struct script *script) wire_client_init(state->wire_client, config, script, state); } + init_cmd_exed = false; if (script->init_command != NULL) { if (safe_system(script->init_command->command_line, &error)) { - die("%s: error executing init command: %s\n", - config->script_path, error); + asprintf(&error, "%s: error executing init command: %s\n", + config->script_path, error); + free(error); + exit(EXIT_FAILURE); } + init_cmd_exed = true; } signal(SIGPIPE, SIG_IGN); /* ignore EPIPE */ @@ -630,6 +668,9 @@ void run_script(struct config *config, struct script *script) if (state->wire_client != NULL) wire_client_next_event(state->wire_client, NULL); + if (run_cleanup_command() == STATUS_ERR) + exit(EXIT_FAILURE); + if (code_execute(state->code, &error)) { char *script_path = strdup(state->config->script_path); state_free(state, 1); diff --git a/gtests/net/packetdrill/run.h b/gtests/net/packetdrill/run.h index 981692102cf1cb7bc326bcf7b0793316bba3b123..6acb518b397a222ac605fd83b751b940194156f3 100644 --- a/gtests/net/packetdrill/run.h +++ b/gtests/net/packetdrill/run.h @@ -184,4 +184,7 @@ extern void set_scheduling_priority(void); /* Try to pin our pages into RAM. */ extern void lock_memory(void); +/* Run final command we always execute at end of script, to clean up. */ +extern int run_cleanup_command(void); + #endif /* __RUN_H__ */ diff --git a/gtests/net/packetdrill/script.h b/gtests/net/packetdrill/script.h index 31c69416d9aa01cf2120d0c60529190ee96dd6d8..228442b08189386e24e20d9675de3d50d49773f1 100644 --- a/gtests/net/packetdrill/script.h +++ b/gtests/net/packetdrill/script.h @@ -763,10 +763,14 @@ struct script { struct option_list *option_list; /* linked list of options */ struct command_spec *init_command; /* untimed initialization command */ struct event *event_list; /* linked list of all events */ + struct command_spec *cleanup_command; /* untimed cleanup command */ char *buffer; /* raw input text of the script */ int length; /* number of bytes in the script */ }; +/* Global pointer for final command we always execute at end of script: */ +extern const char *cleanup_cmd; + /* A table entry mapping a bit mask to its human-readable name. * A table of such mappings must be terminated with a struct with a * NULL name.