From: Arran Cudbard-Bell Date: Sat, 10 Dec 2022 00:00:54 +0000 (-0600) Subject: Add support for setting timeouts in jlibtool X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7de57097c5707d6a6126c276eb180c08099afd73;p=thirdparty%2Ffreeradius-server.git Add support for setting timeouts in jlibtool For tests, this means the child receives kill(SIGKILL) after 30 seconds if it's not completed, so we don't block CI indefinitely. --- diff --git a/Make.inc.in b/Make.inc.in index 76eb7857f9b..477ae3fcdba 100644 --- a/Make.inc.in +++ b/Make.inc.in @@ -204,8 +204,12 @@ ANALYZE.c := @clang_path@ # # Have wrappers for the test tools, so that they run. # +# We have a hardcoded maximum execution time of 30 seconds +# If your test takes > 30 seconds to run, you're doing +# something wrong. +# TEST_BIN_DIR = ./$(BUILD_DIR)/bin/local -TEST_BIN = $(JLIBTOOL) $(if ${VERBOSE},--debug,--silent) --mode=execute $(TEST_BIN_DIR) +TEST_BIN = $(JLIBTOOL) $(if ${VERBOSE},--debug,--silent) --timeout=30 --mode=execute $(TEST_BIN_DIR) # # For creating documentation via doc/all.mk diff --git a/scripts/jlibtool.c b/scripts/jlibtool.c index 92c65bdfa77..4870466cbd4 100644 --- a/scripts/jlibtool.c +++ b/scripts/jlibtool.c @@ -102,6 +102,11 @@ typedef struct { # define TARGET_RANLIB "ranlib" #endif +/* + * Set to true when an exec times out + */ +static bool timeout = false; + static const toolset_t toolset_host = { .cc = BUILD_CC, .cxx = HOST_CXX, @@ -587,6 +592,8 @@ typedef struct { char const *version_info; char const *undefined_flag; + + unsigned int timeout; } command_t; static void add_rpath(count_chars *cc, char const *path); @@ -854,6 +861,11 @@ static void external_spawn_sig_handler(int signo) kill(spawn_pid, signo); /* Forward the signal to the process we're executing */ } +void external_spawn_timeout(int pid) +{ + timeout = true; +} + static int external_spawn(command_t *cmd, __attribute__((unused)) char const *file, char const **argv) { if (!cmd->options.silent) { @@ -905,8 +917,30 @@ static int external_spawn(command_t *cmd, __attribute__((unused)) char const *fi SIGNAL_FORWARD(SIGUSR1); SIGNAL_FORWARD(SIGUSR2); + /* + * Deliver's SIGALRM after N seconds + */ + if (cmd->timeout > 0) { + /* + * Seems like SA_RESTART is set + * implicitly when signal() is + * used, which is NOT what we want. + */ + sigaction(SIGALRM, + &(struct sigaction){ + .sa_handler = external_spawn_timeout, + .sa_flags = SA_RESETHAND + }, NULL); + alarm(cmd->timeout); + } + waitpid(spawn_pid, &status, 0); + if (cmd->timeout > 0) { + signal(SIGALRM, NULL); + alarm(0); + } + SIGNAL_RESET(SIGHUP); SIGNAL_RESET(SIGINT); SIGNAL_RESET(SIGQUIT); @@ -916,6 +950,20 @@ static int external_spawn(command_t *cmd, __attribute__((unused)) char const *fi SIGNAL_RESET(SIGUSR1); SIGNAL_RESET(SIGUSR2); + /* + * We're assuming waitpid was delivered + * because of the alarm we set. + * + * Kill the child and clean it up. + */ + if (timeout) { + NOTICE("exec timeout\n"); + kill(spawn_pid, SIGKILL); + + waitpid(spawn_pid, &status, 0); /* Cleanup child state */ + timeout = false; /* reset */ + } + /* * Exited via exit(status) */ @@ -1175,6 +1223,10 @@ static int parse_long_opt(char const *arg, command_t *cmd) exit(0); } else if (strcmp(var, "tag") == 0) { DEBUG("discard --tag=%s\n", value); + + } else if (strcmp(var, "timeout") == 0) { + cmd->timeout = strtoul(value, NULL, 10); + NOTICE("Timeout %u\n", cmd->timeout); } else { return 0; } @@ -2993,6 +3045,7 @@ static void parse_args(int argc, char *argv[], command_t *cmd) } else if (!strcmp(arg + 1, "undefined")) { cmd->undefined_flag = argv[++a]; arg_used = 1; + /* * Add dir to runtime library search path. */