implement execution timeouts for executors
Previously, it was possible for an executor to hang forever. To mitigate this, we now implement process execution timeouts for executors, by looping with waitpid(..., WNOHANG) and sleeping. This could be implemented more efficiently with process descriptors, see pidfd_open(2), but that interface is Linux-specific and is only available on Linux 5.3 or newer.
This commit is contained in:
parent
7a46b61996
commit
941d7c51d7
4 changed files with 43 additions and 10 deletions
|
@ -21,10 +21,13 @@
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
#include "cmd/multicall.h"
|
#include "cmd/multicall.h"
|
||||||
|
|
||||||
|
#define DEFAULT_TIMEOUT 300
|
||||||
|
|
||||||
struct lif_execute_opts exec_opts = {
|
struct lif_execute_opts exec_opts = {
|
||||||
.interfaces_file = INTERFACES_FILE,
|
.interfaces_file = INTERFACES_FILE,
|
||||||
.executor_path = EXECUTOR_PATH,
|
.executor_path = EXECUTOR_PATH,
|
||||||
.state_file = STATE_FILE
|
.state_file = STATE_FILE,
|
||||||
|
.timeout = DEFAULT_TIMEOUT,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -74,6 +77,14 @@ set_force(const char *opt_arg)
|
||||||
exec_opts.force = true;
|
exec_opts.force = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
set_timeout(const char *opt_arg)
|
||||||
|
{
|
||||||
|
exec_opts.timeout = atoi(opt_arg);
|
||||||
|
if (exec_opts.timeout < 0)
|
||||||
|
exec_opts.timeout = DEFAULT_TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
static struct if_option exec_options[] = {
|
static struct if_option exec_options[] = {
|
||||||
{'f', "force", NULL, "force (de)configuration", false, set_force},
|
{'f', "force", NULL, "force (de)configuration", false, set_force},
|
||||||
{'i', "interfaces", "interfaces FILE", "use FILE for interface definitions", true, set_interfaces_file},
|
{'i', "interfaces", "interfaces FILE", "use FILE for interface definitions", true, set_interfaces_file},
|
||||||
|
@ -82,6 +93,7 @@ static struct if_option exec_options[] = {
|
||||||
{'v', "verbose", NULL, "show what commands are being run", false, set_verbose},
|
{'v', "verbose", NULL, "show what commands are being run", false, set_verbose},
|
||||||
{'E', "executor-path", "executor-path PATH", "use PATH for executor directory", true, set_executor_path},
|
{'E', "executor-path", "executor-path PATH", "use PATH for executor directory", true, set_executor_path},
|
||||||
{'S', "state-file", "state-file FILE", "use FILE for state", true, set_state_file},
|
{'S', "state-file", "state-file FILE", "use FILE for state", true, set_state_file},
|
||||||
|
{'T', "timeout", "timeout TIMEOUT", "wait TIMEOUT seconds for executors to complete", true, set_timeout},
|
||||||
};
|
};
|
||||||
|
|
||||||
struct if_option_group exec_option_group = {
|
struct if_option_group exec_option_group = {
|
||||||
|
|
|
@ -30,6 +30,31 @@
|
||||||
|
|
||||||
#define SHELL "/bin/sh"
|
#define SHELL "/bin/sh"
|
||||||
|
|
||||||
|
/* TODO: Add support for Linux process descriptors once it is okay to require
|
||||||
|
* Linux 5.3 or newer.
|
||||||
|
*/
|
||||||
|
static inline bool
|
||||||
|
lif_process_monitor(const char *cmdbuf, pid_t child, int timeout_sec)
|
||||||
|
{
|
||||||
|
int status;
|
||||||
|
int ticks = 0;
|
||||||
|
|
||||||
|
while (ticks < timeout_sec)
|
||||||
|
{
|
||||||
|
if (waitpid(child, &status, WNOHANG) == child)
|
||||||
|
return WIFEXITED(status) && WEXITSTATUS(status) == 0;
|
||||||
|
|
||||||
|
sleep(1);
|
||||||
|
ticks++;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "execution of '%s': timeout after %d seconds\n", cmdbuf, timeout_sec);
|
||||||
|
kill(child, SIGKILL);
|
||||||
|
waitpid(child, &status, 0);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
lif_execute_fmt(const struct lif_execute_opts *opts, char *const envp[], const char *fmt, ...)
|
lif_execute_fmt(const struct lif_execute_opts *opts, char *const envp[], const char *fmt, ...)
|
||||||
{
|
{
|
||||||
|
@ -55,10 +80,7 @@ lif_execute_fmt(const struct lif_execute_opts *opts, char *const envp[], const c
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int status;
|
return lif_process_monitor(cmdbuf, child, opts->timeout);
|
||||||
waitpid(child, &status, 0);
|
|
||||||
|
|
||||||
return WIFEXITED(status) && WEXITSTATUS(status) == 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
@ -118,11 +140,8 @@ lif_execute_fmt_with_result(const struct lif_execute_opts *opts, char *buf, size
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int status;
|
|
||||||
no_result:
|
no_result:
|
||||||
waitpid(child, &status, 0);
|
return lif_process_monitor(cmdbuf, child, opts->timeout);
|
||||||
|
|
||||||
return WIFEXITED(status) && WEXITSTATUS(status) == 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
|
|
@ -27,6 +27,7 @@ struct lif_execute_opts {
|
||||||
const char *executor_path;
|
const char *executor_path;
|
||||||
const char *interfaces_file;
|
const char *interfaces_file;
|
||||||
const char *state_file;
|
const char *state_file;
|
||||||
|
int timeout;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern bool lif_execute_fmt(const struct lif_execute_opts *opts, char *const envp[], const char *fmt, ...);
|
extern bool lif_execute_fmt(const struct lif_execute_opts *opts, char *const envp[], const char *fmt, ...);
|
||||||
|
|
|
@ -94,7 +94,8 @@ query_dependents_from_executors(const struct lif_execute_opts *opts, char *const
|
||||||
struct lif_execute_opts exec_opts = {
|
struct lif_execute_opts exec_opts = {
|
||||||
.verbose = opts->verbose,
|
.verbose = opts->verbose,
|
||||||
.executor_path = opts->executor_path,
|
.executor_path = opts->executor_path,
|
||||||
.interfaces_file = opts->interfaces_file
|
.interfaces_file = opts->interfaces_file,
|
||||||
|
.timeout = opts->timeout,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (strcmp(entry->key, "use"))
|
if (strcmp(entry->key, "use"))
|
||||||
|
|
Loading…
Reference in a new issue