From: Michael Tremer Date: Fri, 6 Jan 2023 10:27:56 +0000 (+0000) Subject: jail: Implement configuring timeouts for commands X-Git-Tag: 0.9.29~405 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=335b8a44549c2f0441dc16b72444fb641e9aa727;p=pakfire.git jail: Implement configuring timeouts for commands Signed-off-by: Michael Tremer --- diff --git a/src/libpakfire/include/pakfire/jail.h b/src/libpakfire/include/pakfire/jail.h index 46d1d4024..03500bb82 100644 --- a/src/libpakfire/include/pakfire/jail.h +++ b/src/libpakfire/include/pakfire/jail.h @@ -41,6 +41,9 @@ int pakfire_jail_bind(struct pakfire_jail* jail, // Resource Limits int pakfire_jail_nice(struct pakfire_jail* jail, int nice); +// Timeout +int pakfire_jail_set_timeout(struct pakfire_jail* jail, unsigned int timeout); + // Environment const char* pakfire_jail_get_env(struct pakfire_jail* jail, const char* key); int pakfire_jail_set_env(struct pakfire_jail* jail, const char* key, const char* value); diff --git a/src/libpakfire/jail.c b/src/libpakfire/jail.c index 5c7a844b9..9cb347335 100644 --- a/src/libpakfire/jail.c +++ b/src/libpakfire/jail.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -95,6 +96,9 @@ struct pakfire_jail { // Resource Limits int nice; + // Timeout + struct itimerspec timeout; + // CGroup struct pakfire_cgroup* cgroup; @@ -166,6 +170,10 @@ static int clone3(struct clone_args* args, size_t size) { return syscall(__NR_clone3, args, size); } +static int pidfd_send_signal(int pidfd, int sig, siginfo_t* info, unsigned int flags) { + return syscall(SYS_pidfd_send_signal, pidfd, sig, info, flags); +} + static int pakfire_jail_exec_has_flag( const struct pakfire_jail_exec* ctx, const enum pakfire_jail_exec_flags flag) { return ctx->flags & flag; @@ -439,6 +447,51 @@ PAKFIRE_EXPORT int pakfire_jail_import_env(struct pakfire_jail* jail, const char return 0; } +// Timeout + +PAKFIRE_EXPORT int pakfire_jail_set_timeout( + struct pakfire_jail* jail, unsigned int timeout) { + // Store value + jail->timeout.it_value.tv_sec = timeout; + + if (timeout > 0) + DEBUG(jail->pakfire, "Timeout set to %d second(s)\n", timeout); + else + DEBUG(jail->pakfire, "Timeout disabled\n"); + + return 0; +} + +static int pakfire_jail_create_timer(struct pakfire_jail* jail) { + int r; + + // Nothing to do if no timeout has been set + if (!jail->timeout.it_value.tv_sec) + return -1; + + // Create a new timer + const int fd = timerfd_create(CLOCK_MONOTONIC, 0); + if (fd < 0) { + ERROR(jail->pakfire, "Could not create timer: %m\n"); + goto ERROR; + } + + // Arm timer + r = timerfd_settime(fd, 0, &jail->timeout, NULL); + if (r) { + ERROR(jail->pakfire, "Could not arm timer: %m\n"); + goto ERROR; + } + + return fd; + +ERROR: + if (fd > 0) + close(fd); + + return -1; +} + /* This function replaces any logging in the child process. @@ -640,6 +693,7 @@ static int pakfire_jail_wait(struct pakfire_jail* jail, struct pakfire_jail_exec int epollfd = -1; struct epoll_event ev; struct epoll_event events[EPOLL_MAX_EVENTS]; + char garbage[8]; int r = 0; // Fetch file descriptors from context @@ -648,6 +702,9 @@ static int pakfire_jail_wait(struct pakfire_jail* jail, struct pakfire_jail_exec const int stderr = pakfire_jail_get_pipe_to_read(jail, &ctx->pipes.stderr); const int pidfd = ctx->pidfd; + // Timer + const int timerfd = pakfire_jail_create_timer(jail); + // Logging const int log_INFO = pakfire_jail_get_pipe_to_read(jail, &ctx->pipes.log_INFO); const int log_ERROR = pakfire_jail_get_pipe_to_read(jail, &ctx->pipes.log_ERROR); @@ -655,7 +712,7 @@ static int pakfire_jail_wait(struct pakfire_jail* jail, struct pakfire_jail_exec // Make a list of all file descriptors we are interested in int fds[] = { - stdin, stdout, stderr, pidfd, log_INFO, log_ERROR, log_DEBUG, + stdin, stdout, stderr, pidfd, timerfd, log_INFO, log_ERROR, log_DEBUG, }; // Setup epoll @@ -742,6 +799,33 @@ static int pakfire_jail_wait(struct pakfire_jail* jail, struct pakfire_jail_exec ended = 1; continue; + // Handle timer events + } else if (fd == timerfd) { + DEBUG(jail->pakfire, "Timer event received\n"); + + // Disarm the timer + r = read(timerfd, garbage, sizeof(garbage)); + if (r < 1) { + ERROR(jail->pakfire, "Could not disarm timer: %m\n"); + r = 1; + goto ERROR; + } + + // Terminate the process if it hasn't already ended + if (!ended) { + DEBUG(jail->pakfire, "Terminating process...\n"); + + // Send SIGTERM to the process + r = pidfd_send_signal(pidfd, SIGKILL, NULL, 0); + if (r) { + ERROR(jail->pakfire, "Could not kill process: %m\n"); + goto ERROR; + } + } + + // There is nothing else to do + continue; + // Handle logging messages } else if (fd == log_INFO) { buffer = &ctx->buffers.log_INFO; @@ -820,6 +904,8 @@ static int pakfire_jail_wait(struct pakfire_jail* jail, struct pakfire_jail_exec ERROR: if (epollfd > 0) close(epollfd); + if (timerfd > 0) + close(timerfd); return r; } diff --git a/src/libpakfire/libpakfire.sym b/src/libpakfire/libpakfire.sym index 71f1608a3..b6d13d407 100644 --- a/src/libpakfire/libpakfire.sym +++ b/src/libpakfire/libpakfire.sym @@ -157,6 +157,7 @@ global: pakfire_jail_nice; pakfire_jail_ref; pakfire_jail_set_env; + pakfire_jail_set_timeout; pakfire_jail_unref; # log diff --git a/tests/libpakfire/jail.c b/tests/libpakfire/jail.c index 4b115dd58..94f362d90 100644 --- a/tests/libpakfire/jail.c +++ b/tests/libpakfire/jail.c @@ -425,6 +425,33 @@ FAIL: return r; } +static int test_timeout(const struct test* t) { + struct pakfire_jail* jail = NULL; + int r = EXIT_FAILURE; + + const char* argv[] = { + "/command", "sleep", "5", NULL, + }; + + // Create a new jail + ASSERT_SUCCESS(pakfire_jail_create(&jail, t->pakfire, 0)); + + // Set a timeout of one second + ASSERT_SUCCESS(pakfire_jail_set_timeout(jail, 1)); + + // Check if we receive the correct exit code + ASSERT(pakfire_jail_exec(jail, argv, NULL, NULL, NULL) == 139); + + // Success + r = EXIT_SUCCESS; + +FAIL: + if (jail) + pakfire_jail_unref(jail); + + return r; +} + int main(int argc, const char* argv[]) { testsuite_add_test(test_create); testsuite_add_test(test_exit_code); @@ -439,6 +466,7 @@ int main(int argc, const char* argv[]) { testsuite_add_test(test_bind); testsuite_add_test(test_communicate); testsuite_add_test(test_send_signal); + testsuite_add_test(test_timeout); return testsuite_run(argc, argv); }