Skip to content
Snippets Groups Projects
Commit db583289 authored by Michael Tüxen's avatar Michael Tüxen
Browse files

Add support for finalizer.

This is ported from packetdrill 2.0.
parent b7d591f7
No related branches found
No related tags found
No related merge requests found
......@@ -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);
}
......@@ -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__ */
......@@ -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;
}
;
......@@ -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);
......
......@@ -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__ */
......@@ -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.
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment